Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
36e461795a | |||
d6e7641364 | |||
15cebd6e06 | |||
e9a074d4d1 | |||
4b7fd39e57 | |||
fa005f1327 | |||
c7a9f40baa | |||
d5b9726158 | |||
f659e66cf7 | |||
801bdbf298 | |||
09da93cfb3 | |||
70ace02e80 | |||
1f758e953d | |||
ffad2cab81 | |||
dbb10644de | |||
4848392185 | |||
956f4ac30f | |||
c09ff28fd5 | |||
20cf290d37 | |||
4ca0fcc6d1 | |||
ce4ce72820 | |||
e363d55899 | |||
172479e4fb | |||
156fa5dace | |||
4d40e0aa38 | |||
045e66b631 | |||
62e60d78de | |||
23bdaa1517 | |||
50f222cced | |||
640e1adf96 | |||
d4bb84180c | |||
bda47fc36b | |||
fd6ba56143 | |||
b63a0fc246 | |||
ed92cccf0e | |||
95892802fd | |||
8a5004e828 | |||
c6c523e005 | |||
a692ec818d | |||
c65f780613 | |||
507c2ab468 | |||
1180da8d11 | |||
83f574e3ab | |||
60837f307d | |||
50d5dedabe | |||
f15c774c70 | |||
069f4805f6 | |||
eb98624a6a | |||
6a0c7cf499 | |||
73ab9ca778 | |||
9f9e0750e1 | |||
5664965491 | |||
db4016e79f | |||
f84c4370cf | |||
b39cb6391b | |||
4f7f60188f | |||
dce58343db | |||
415838ad39 | |||
ce0b1a7585 | |||
352995e852 | |||
a3d55a3274 | |||
70adadf129 | |||
d42ac8a146 | |||
f304ff8862 | |||
7d91e02dc9 | |||
dae510ae0a | |||
cd382a78a5 | |||
987de4a7be | |||
52d3b2f8c2 | |||
5038429a70 | |||
2acbf0f3f5 | |||
aed703e260 | |||
5ece7c0da4 |
45
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
name: Image Publisher
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [ published ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup-build-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.release.tag_name }}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Docker & GHCR
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
|
||||||
|
#echo "${{ secrets.GHCR_PASSWORD }}" | docker login ghcr.io -u "${{ secrets.GHCR_USERNAME }}" --password-stdin
|
||||||
|
|
||||||
|
- name: Setup building file structure
|
||||||
|
run: |
|
||||||
|
cp -r $GITHUB_WORKSPACE/src/ $GITHUB_WORKSPACE/docker/
|
||||||
|
|
||||||
|
- name: Build the image
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE/docker/
|
||||||
|
docker buildx create --name mainbuilder --driver docker-container --platform linux/amd64,linux/arm64 --use
|
||||||
|
|
||||||
|
docker buildx build --push \
|
||||||
|
--build-arg VERSION=${{ github.event.release.tag_name }} \
|
||||||
|
--provenance=false \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--tag zoraxydocker/zoraxy:${{ github.event.release.tag_name }} \
|
||||||
|
--tag zoraxydocker/zoraxy:latest \
|
||||||
|
# Since this is still undetermined, I will leave it commented
|
||||||
|
#--tag ghcr.io/zoraxydocker/zoraxy:${{ steps.get_latest_release_tag.outputs.latest_tag }} \
|
||||||
|
#--tag ghcr.io/zoraxydocker/zoraxy:latest \
|
||||||
|
.
|
46
CHANGELOG.md
@ -1,7 +1,43 @@
|
|||||||
v2.6.5 Jul 19 2023
|
# v2.6.8 Nov 25 2023
|
||||||
|
|
||||||
|
+ Added opt-out for subdomains for global TLS settings: See [release notes](https://github.com/tobychui/zoraxy/releases/tag/2.6.8)
|
||||||
|
+ Optimized subdomain / vdir editing interface
|
||||||
|
+ Added system-wide logger (Work in progress)
|
||||||
|
+ Fixed issue for uptime monitor bug [#77](https://github.com/tobychui/zoraxy/issues/77)
|
||||||
|
+ Changed default static web port to 5487 (prevent already in use)
|
||||||
|
+ Added automatic HTTP/2 to TLS mode
|
||||||
|
+ Bug fix for webserver autostart [67](https://github.com/tobychui/zoraxy/issues/67)
|
||||||
|
|
||||||
|
# v2.6.7 Sep 26 2023
|
||||||
|
|
||||||
|
+ Added Static Web Server function [#56](https://github.com/tobychui/zoraxy/issues/56)
|
||||||
|
+ Web Directory Manager (see static webserver tab)
|
||||||
|
+ Added static web server and black / whitelist template [#38](https://github.com/tobychui/zoraxy/issues/38)
|
||||||
|
+ Added default / preferred CA features for ACME [#47](https://github.com/tobychui/zoraxy/issues/47)
|
||||||
|
+ Optimized TLS/SSL page and added dedicated section for ACME related features
|
||||||
|
+ Bugfixes [#61](https://github.com/tobychui/zoraxy/issues/61) [#58](https://github.com/tobychui/zoraxy/issues/58)
|
||||||
|
|
||||||
|
# v2.6.6 Aug 30 2023
|
||||||
|
|
||||||
|
+ Added basic auth editor custom exception rules
|
||||||
|
+ Fixed redirection bug under another reverse proxy and Apache location headers [#39](https://github.com/tobychui/zoraxy/issues/39)
|
||||||
|
+ Optimized memory usage (from 1.2GB to 61MB for low speed geoip lookup) [#52](https://github.com/tobychui/zoraxy/issues/52)
|
||||||
|
+ Added unset subdomain custom redirection feature [#46](https://github.com/tobychui/zoraxy/issues/46)
|
||||||
|
+ Fixed potential security issue in satori/go.uuid [#55](https://github.com/tobychui/zoraxy/issues/55)
|
||||||
|
+ Added custom ACME feature in backend, thx [@daluntw](https://github.com/daluntw)
|
||||||
|
+ Added bypass TLS check for custom acme server, thx [@daluntw](https://github.com/daluntw)
|
||||||
|
+ Introduce new start parameter `-fastgeoip=true`: see [release notes](https://github.com/tobychui/zoraxy/releases/tag/2.6.6)
|
||||||
|
|
||||||
|
# v2.6.5.1 Jul 26 2023
|
||||||
|
|
||||||
|
+ Patch on memory leaking for Windows netstat module (do not effect any of the previous non Windows builds)
|
||||||
|
+ Fixed potential memory leak in ACME handler logic
|
||||||
|
+ Added "Do you want to get a TLS certificate for this subdomain?" dialogue when a new subdomain proxy rule is created
|
||||||
|
|
||||||
|
# v2.6.5 Jul 19 2023
|
||||||
|
|
||||||
+ Added Import / Export-Feature
|
+ Added Import / Export-Feature
|
||||||
+ Moved configurationfiles to a separate folder [#26](https://github.com/tobychui/zoraxy/issues/26)
|
+ Moved configuration files to a separate folder [#26](https://github.com/tobychui/zoraxy/issues/26)
|
||||||
+ Added auto-renew with ACME [#6](https://github.com/tobychui/zoraxy/issues/6)
|
+ Added auto-renew with ACME [#6](https://github.com/tobychui/zoraxy/issues/6)
|
||||||
+ Fixed Whitelistbug [#18](https://github.com/tobychui/zoraxy/issues/18)
|
+ Fixed Whitelistbug [#18](https://github.com/tobychui/zoraxy/issues/18)
|
||||||
+ Added Whois
|
+ Added Whois
|
||||||
@ -11,7 +47,7 @@ v2.6.5 Jul 19 2023
|
|||||||
+ Added force TLS v1.2 above toggle
|
+ Added force TLS v1.2 above toggle
|
||||||
+ Added trace route
|
+ Added trace route
|
||||||
+ Added ICMP ping
|
+ Added ICMP ping
|
||||||
+ Added special routing rules module for up-coming acme integration
|
+ Added special routing rules module for up-coming ACME integration
|
||||||
+ Fixed IPv6 check bug in black/whitelist
|
+ Fixed IPv6 check bug in black/whitelist
|
||||||
+ Optimized UI for TCP Proxy
|
+ Optimized UI for TCP Proxy
|
||||||
|
|
||||||
@ -21,7 +57,7 @@ v2.6.5 Jul 19 2023
|
|||||||
+ Split blacklist and whitelist from geodb script file
|
+ Split blacklist and whitelist from geodb script file
|
||||||
+ Optimized compile binary size
|
+ Optimized compile binary size
|
||||||
+ Added access control to TCP proxy
|
+ Added access control to TCP proxy
|
||||||
+ Added "invalid config detect" in up time monitor for isse [#7](https://github.com/tobychui/zoraxy/issues/7)
|
+ Added "invalid config detect" in up time monitor for issue [#7](https://github.com/tobychui/zoraxy/issues/7)
|
||||||
+ Fixed minor bugs in advance stats panel
|
+ Fixed minor bugs in advance stats panel
|
||||||
+ Reduced file size of embedded materials
|
+ Reduced file size of embedded materials
|
||||||
|
|
||||||
@ -48,6 +84,6 @@ v2.6.5 Jul 19 2023
|
|||||||
+ Basic auth
|
+ Basic auth
|
||||||
+ Support TLS verification skip (for self signed certs)
|
+ Support TLS verification skip (for self signed certs)
|
||||||
+ Added trend analysis
|
+ Added trend analysis
|
||||||
+ Added referer and file type analysis
|
+ Added referrer and file type analysis
|
||||||
+ Added cert expire day display
|
+ Added cert expire day display
|
||||||
+ Moved subdomain proxy logic to dpcore
|
+ Moved subdomain proxy logic to dpcore
|
||||||
|
91
README.md
@ -8,9 +8,7 @@ General purpose request (reverse) proxy and forwarding tool for low power device
|
|||||||
|
|
||||||
- Simple to use interface with detail in-system instructions
|
- Simple to use interface with detail in-system instructions
|
||||||
- Reverse Proxy
|
- Reverse Proxy
|
||||||
|
|
||||||
- Subdomain Reverse Proxy
|
- Subdomain Reverse Proxy
|
||||||
|
|
||||||
- Virtual Directory Reverse Proxy
|
- Virtual Directory Reverse Proxy
|
||||||
- Redirection Rules
|
- Redirection Rules
|
||||||
- TLS / SSL setup and deploy
|
- TLS / SSL setup and deploy
|
||||||
@ -19,7 +17,6 @@ General purpose request (reverse) proxy and forwarding tool for low power device
|
|||||||
- Integrated Up-time Monitor
|
- Integrated Up-time Monitor
|
||||||
- Web-SSH Terminal
|
- Web-SSH Terminal
|
||||||
- Utilities
|
- Utilities
|
||||||
|
|
||||||
- CIDR IP converters
|
- CIDR IP converters
|
||||||
- mDNS Scanner
|
- mDNS Scanner
|
||||||
- IP Scanner
|
- IP Scanner
|
||||||
@ -29,9 +26,9 @@ General purpose request (reverse) proxy and forwarding tool for low power device
|
|||||||
- SMTP config for password reset
|
- SMTP config for password reset
|
||||||
|
|
||||||
## Build from Source
|
## Build from Source
|
||||||
Require Go 1.20 or above
|
Requires Go 1.20 or higher
|
||||||
|
|
||||||
```
|
```bash
|
||||||
git clone https://github.com/tobychui/zoraxy
|
git clone https://github.com/tobychui/zoraxy
|
||||||
cd ./zoraxy/src/
|
cd ./zoraxy/src/
|
||||||
go mod tidy
|
go mod tidy
|
||||||
@ -42,11 +39,11 @@ sudo ./zoraxy -port=:8000
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Zoraxy provide basic authentication system for standalone mode. To use it in standalone mode, follow the instruction below for your desired deployment platform.
|
Zoraxy provides basic authentication system for standalone mode. To use it in standalone mode, follow the instructionss below for your desired deployment platform.
|
||||||
|
|
||||||
### Standalone Mode
|
### Standalone Mode
|
||||||
|
|
||||||
Standalone mode is the default mode for Zoraxy. This allow single account to manage your reverse proxy server just like a home router. This mode is suitable for new owners for homelab or makers start growing their web services into multiple servers.
|
Standalone mode is the default mode for Zoraxy. This allows a single account to manage your reverse proxy server, just like a home router. This mode is suitable for new owners to homelabs or makers starting growing their web services into multiple servers.
|
||||||
|
|
||||||
#### Linux
|
#### Linux
|
||||||
|
|
||||||
@ -60,18 +57,51 @@ Download the binary executable and double click the binary file to start it.
|
|||||||
|
|
||||||
#### Raspberry Pi
|
#### Raspberry Pi
|
||||||
|
|
||||||
The installation method is same as Linux. If you are using Raspberry Pi 4 or newer models, pick the arm64 release. For older version of Pis, use the arm (armv6) version instead.
|
The installation method is same as Linux. If you are using a Raspberry Pi 4 or newer models, pick the arm64 release. For older version of Pis, use the arm (armv6) version instead.
|
||||||
|
|
||||||
#### Other ARM SBCs or Android phone with Termux
|
#### Other ARM SBCs or Android phone with Termux
|
||||||
|
|
||||||
The installation method is same as Linux. For other ARM SBCs, please refer to your SBC's CPU architecture and pick the one that is suitable for your device.
|
The installation method is same as Linux. For other ARM SBCs, please refer to your SBC's CPU architecture and pick the one that is suitable for your device.
|
||||||
|
|
||||||
#### Docker
|
#### Docker
|
||||||
See the [/docker](https://github.com/tobychui/zoraxy/tree/main/docker) folder for more details
|
See the [/docker](https://github.com/tobychui/zoraxy/tree/main/docker) folder for more details.
|
||||||
|
|
||||||
|
### Start Paramters
|
||||||
|
```
|
||||||
|
Usage of zoraxy:
|
||||||
|
-autorenew int
|
||||||
|
ACME auto TLS/SSL certificate renew check interval (seconds) (default 86400)
|
||||||
|
-fastgeoip
|
||||||
|
Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)
|
||||||
|
-info
|
||||||
|
Show information about this program in JSON
|
||||||
|
-log
|
||||||
|
Log terminal output to file (default true)
|
||||||
|
-mdns
|
||||||
|
Enable mDNS scanner and transponder (default true)
|
||||||
|
-noauth
|
||||||
|
Disable authentication for management interface
|
||||||
|
-port string
|
||||||
|
Management web interface listening port (default ":8000")
|
||||||
|
-rpt string
|
||||||
|
Reserved
|
||||||
|
-sshlb
|
||||||
|
Allow loopback web ssh connection (DANGER)
|
||||||
|
-version
|
||||||
|
Show version of this server
|
||||||
|
-webfm
|
||||||
|
Enable web file manager for static web server root folder (default true)
|
||||||
|
-webroot string
|
||||||
|
Static web server root folder. Only allow chnage in start paramters (default "./www")
|
||||||
|
-ztauth string
|
||||||
|
ZeroTier authtoken for the local node
|
||||||
|
-ztport int
|
||||||
|
ZeroTier controller API port (default 9993)
|
||||||
|
```
|
||||||
|
|
||||||
### External Permission Management Mode
|
### External Permission Management Mode
|
||||||
|
|
||||||
If you already have a up-stream reverse proxy server in place with permission management, you can use Zoraxy in noauth mode. To enable noauth mode, start Zoraxy with the following flag
|
If you already have an upstream reverse proxy server in place with permission management, you can use Zoraxy in noauth mode. To enable noauth mode, start Zoraxy with the following flag:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./zoraxy -noauth=true
|
./zoraxy -noauth=true
|
||||||
@ -81,32 +111,32 @@ If you already have a up-stream reverse proxy server in place with permission ma
|
|||||||
|
|
||||||
#### Use with ArozOS
|
#### Use with ArozOS
|
||||||
|
|
||||||
[ArozOS ](https://arozos.com)subservice is a build in permission managed reverse proxy server. To use zoraxy with arozos, connect to your arozos host via ssh and use the following command to install zoraxy
|
The [ArozOS](https://arozos.com) subservice is a built-in, permission-managed, reverse proxy server. To use Zoraxy with ArozOS, connect to your ArozOS host via SSH and use the following command to install Zoraxy:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# cd into your arozos subservice folder. Sometime it is under ~/arozos/src/subservice
|
# cd into your ArozOS subservice folder. Sometimes it is under ~/arozos/src/subservice.
|
||||||
cd ~/arozos/subservices
|
cd ~/arozos/subservices
|
||||||
mkdir zoraxy
|
mkdir zoraxy
|
||||||
cd ./zoraxy
|
cd ./zoraxy
|
||||||
|
|
||||||
# Download the release binary from Github release
|
# Download the release binary from Github release.
|
||||||
wget {binary executable link from release page}
|
wget {binary executable link from release page}
|
||||||
|
|
||||||
# Set permission. Change this if required
|
# Set permission. Change this if required.
|
||||||
sudo chmod 775 -R ./
|
sudo chmod 775 -R ./
|
||||||
|
|
||||||
# Start zoraxy to see if the downloaded arch is correct.
|
# Start zoraxy to see if the downloaded arch is correct.
|
||||||
./zoraxy
|
./zoraxy
|
||||||
|
|
||||||
# After the unzip done, press Ctrl + C to kill it
|
# After unzipping, press Ctrl + C to kill it.
|
||||||
# Rename it to valid arozos subservice binary format
|
# Rename it to validate the ArozOS subservice binary format.
|
||||||
mv ./zoraxy zoraxy_linux_amd64
|
mv ./zoraxy zoraxy_linux_amd64
|
||||||
|
|
||||||
# If you are using SBCs with different CPU arch, use the following names
|
# If you are using SBCs with a different CPU arch, use the following names:
|
||||||
# mv ./zoraxy zoraxy_linux_arm
|
# mv ./zoraxy zoraxy_linux_arm
|
||||||
# mv ./zoraxy zoraxy_linux_arm64
|
# mv ./zoraxy zoraxy_linux_arm64
|
||||||
|
|
||||||
# Restart arozos
|
# Restart ArozOS
|
||||||
sudo systemctl restart arozos
|
sudo systemctl restart arozos
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -128,22 +158,21 @@ There is a wikipage with [Frequently-Asked-Questions](https://github.com/tobychu
|
|||||||
|
|
||||||
This project also compatible with [ZeroTier](https://www.zerotier.com/). However, due to licensing issues, ZeroTier is not included in the binary.
|
This project also compatible with [ZeroTier](https://www.zerotier.com/). However, due to licensing issues, ZeroTier is not included in the binary.
|
||||||
|
|
||||||
Assuming you already have a valid license, to use Zoraxy with ZeroTier, install ZeroTier on your host and then run Zoraxy in sudo mode (or Run As Administrator if you are on Windows). The program will automatically grab the authtoken at correct location in your host.
|
To use Zoraxy with ZeroTier, assuming you already have a valid license, install ZeroTier on your host and then run Zoraxy in sudo mode (or Run As Administrator if you are on Windows). The program will automatically grab the authtoken in the correct location on your host.
|
||||||
|
|
||||||
If you prefer not to run Zoraxy in sudo mode or you have some weird installation profile, you can also pass in the ZeroTier auth token using the following flags
|
If you prefer not to run Zoraxy in sudo mode or you have some weird installation profile, you can also pass in the ZeroTier auth token using the following flags::
|
||||||
|
|
||||||
```
|
```bash
|
||||||
./zoraxy -ztauth="your_zerotier_authtoken" -ztport=9993
|
./zoraxy -ztauth="your_zerotier_authtoken" -ztport=9993
|
||||||
```
|
```
|
||||||
|
|
||||||
The ZeroTier auth token can usually be found at ```/var/lib/zerotier-one/authtoken.secret``` or ```C:\ProgramData\ZeroTier\One\authtoken.secret```.
|
The ZeroTier auth token can usually be found at ```/var/lib/zerotier-one/authtoken.secret``` or ```C:\ProgramData\ZeroTier\One\authtoken.secret```.
|
||||||
|
|
||||||
This allows you to have infinite number of network members in your Global Area Network controller. For more technical details, see [here](https://docs.zerotier.com/self-hosting/network-controllers/).
|
This allows you to have an infinite number of network members in your Global Area Network controller. For more technical details, see [here](https://docs.zerotier.com/self-hosting/network-controllers/).
|
||||||
|
|
||||||
## Web.SSH
|
## Web SSH
|
||||||
|
|
||||||
Web SSH currently only support Linux based OS. The following platforms are supported
|
|
||||||
|
|
||||||
|
Web SSH currently only supports Linux based OSes. The following platforms are supported:
|
||||||
- linux/amd64
|
- linux/amd64
|
||||||
- linux/arm64
|
- linux/arm64
|
||||||
- linux/armv6 (experimental)
|
- linux/armv6 (experimental)
|
||||||
@ -151,13 +180,19 @@ Web SSH currently only support Linux based OS. The following platforms are suppo
|
|||||||
|
|
||||||
### Loopback Connection
|
### Loopback Connection
|
||||||
|
|
||||||
Loopback web ssh connection, by default, is disabled. This means that if you are trying to connect to address like 127.0.0.1 or localhost, the system will reject your connection due to security issues. To enable loopback for testing or development purpose, use the following flags to override the loopback checking.
|
Loopback web SSH connection, by default, is disabled. This means that if you are trying to connect to an address like 127.0.0.1 or localhost, the system will reject your connection for security reasons. To enable loopback for testing or development purpose, use the following flags to override the loopback checking:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
./zoraxy -sshlb=true
|
./zoraxy -sshlb=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Sponsor This Project
|
||||||
|
If you like the project and want to support us, please consider a donation. You can use the links below
|
||||||
|
- [tobychui (Primary author)](https://paypal.me/tobychui)
|
||||||
|
- PassiveLemon (Docker compatibility maintainer)
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is open source under AGPL. I open source this project so everyone can check for security issues and benefit all users. **If your plans to use this project in commercial environment which violate the AGPL terms, please contact toby@imuslab.com for an alternative commercial license.**
|
This project is open-sourced under AGPL. I open-sourced this project so everyone can check for security issues and benefit all users. **If you plan to use this project in a commercial environment (which violate the AGPL terms), please contact toby@imuslab.com for an alternative commercial license.**
|
||||||
|
|
||||||
|
@ -1,19 +1,40 @@
|
|||||||
FROM alpine:latest
|
FROM docker.io/golang:alpine
|
||||||
|
# VERSION comes from the main.yml workflow --build-arg
|
||||||
|
ARG VERSION
|
||||||
|
|
||||||
RUN apk update && apk upgrade &&\
|
RUN apk add --no-cache bash netcat-openbsd sudo
|
||||||
apk add bash curl jq sudo &&\
|
# Alternatives for security
|
||||||
mkdir -p /zoraxy/data/
|
RUN apk add --no-cache openssl=3.1.4-r1
|
||||||
|
|
||||||
VOLUME [ "/zoraxy/data/" ]
|
RUN mkdir -p /opt/zoraxy/source/ &&\
|
||||||
|
mkdir -p /opt/zoraxy/config/ &&\
|
||||||
|
mkdir -p /usr/local/bin/
|
||||||
|
|
||||||
COPY entrypoint.sh /zoraxy/
|
COPY entrypoint.sh /opt/zoraxy/
|
||||||
|
|
||||||
RUN chmod +x /zoraxy/entrypoint.sh
|
RUN chmod -R 755 /opt/zoraxy/ &&\
|
||||||
|
chmod +x /opt/zoraxy/entrypoint.sh
|
||||||
|
|
||||||
ENV ARGS="-port=:8000 -noauth=false"
|
VOLUME [ "/opt/zoraxy/config/" ]
|
||||||
|
|
||||||
EXPOSE 80
|
# If you build it yourself, you will need to add the src directory into the docker directory.
|
||||||
EXPOSE 443
|
COPY ./src/ /opt/zoraxy/source/
|
||||||
EXPOSE 8000
|
|
||||||
|
WORKDIR /opt/zoraxy/source/
|
||||||
|
|
||||||
|
RUN go mod tidy &&\
|
||||||
|
go build -o /usr/local/bin/zoraxy &&\
|
||||||
|
rm -r /opt/zoraxy/source/
|
||||||
|
|
||||||
|
RUN chmod +x /usr/local/bin/zoraxy
|
||||||
|
|
||||||
|
WORKDIR /opt/zoraxy/config/
|
||||||
|
|
||||||
|
ENV VERSION=$VERSION
|
||||||
|
|
||||||
|
ENV ARGS="-noauth=false"
|
||||||
|
|
||||||
|
ENTRYPOINT ["/opt/zoraxy/entrypoint.sh"]
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=5s --timeout=5s --retries=2 CMD nc -vz 127.0.0.1 8000 || exit 1
|
||||||
|
|
||||||
ENTRYPOINT ["/zoraxy/entrypoint.sh"]
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 PassiveLemon
|
|
||||||
|
|
||||||
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,7 +1,9 @@
|
|||||||
[](https://hub.docker.com/r/passivelemon/zoraxy-docker)
|
# [zoraxy](https://github.com/tobychui/zoraxy/) </br>
|
||||||
[](https://hub.docker.com/r/passivelemon/zoraxy-docker)
|
|
||||||
[](https://hub.docker.com/r/passivelemon/zoraxy-docker)
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
[](https://hub.docker.com/r/passivelemon/zoraxy-docker)
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
|
[](https://hub.docker.com/r/zoraxydocker/zoraxy)
|
||||||
|
|
||||||
## Setup: </br>
|
## Setup: </br>
|
||||||
Although not required, it is recommended to give Zoraxy a dedicated location on the host to mount the container. That way, the host/user can access them whenever needed. A volume will be created automatically within Docker if a location is not specified. </br>
|
Although not required, it is recommended to give Zoraxy a dedicated location on the host to mount the container. That way, the host/user can access them whenever needed. A volume will be created automatically within Docker if a location is not specified. </br>
|
||||||
@ -10,24 +12,24 @@ You may also need to portforward your 80/443 to allow http and https traffic. If
|
|||||||
|
|
||||||
### Using Docker run </br>
|
### Using Docker run </br>
|
||||||
```
|
```
|
||||||
docker run -d --name (container name) -p (ports) -v (path to storage directory):/zoraxy/data/ -e ARGS=(your arguments) -e VERSION=(version) passivelemon/zoraxy-docker:latest
|
docker run -d --name (container name) -p (ports) -v (path to storage directory):/opt/zoraxy/data/ -e ARGS='(your arguments)' zoraxydocker/zoraxy:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using Docker Compose </br>
|
### Using Docker Compose </br>
|
||||||
```
|
```yml
|
||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
zoraxy-docker:
|
zoraxy-docker:
|
||||||
image: passivelemon/zoraxy-docker:latest
|
image: zoraxydocker/zoraxy:latest
|
||||||
container_name: (container name)
|
container_name: (container name)
|
||||||
ports:
|
ports:
|
||||||
- 80:80 # Http port
|
- 80:80
|
||||||
- 443:443 # Https port
|
- 443:443
|
||||||
- (external):8000 # Management portal port
|
- (external):8000
|
||||||
volumes:
|
volumes:
|
||||||
- (path to storage directory):/zoraxy/data/ # Host directory for Zoraxy file storage
|
- (path to storage directory):/opt/zoraxy/config/
|
||||||
environment:
|
environment:
|
||||||
ARGS: '(your arguments)' # The arguments to run with Zoraxy. Enter them as they would be entered normally.
|
ARGS: '(your arguments)'
|
||||||
```
|
```
|
||||||
|
|
||||||
| Operator | Need | Details |
|
| Operator | Need | Details |
|
||||||
@ -35,33 +37,29 @@ services:
|
|||||||
| `-d` | Yes | will run the container in the background. |
|
| `-d` | Yes | will run the container in the background. |
|
||||||
| `--name (container name)` | No | Sets the name of the container to the following word. You can change this to whatever you want. |
|
| `--name (container name)` | No | Sets the name of the container to the following word. You can change this to whatever you want. |
|
||||||
| `-p (ports)` | Yes | Depending on how your network is setup, you may need to portforward 80, 443, and the management port. |
|
| `-p (ports)` | Yes | Depending on how your network is setup, you may need to portforward 80, 443, and the management port. |
|
||||||
| `-v (path to storage directory):/zoraxy/data/` | Recommend | Sets the folder that holds your files. This should be the place you just chose. By default, it will create a Docker volume for the files for persistency but they will not be accessible. |
|
| `-v (path to storage directory):/opt/zoraxy/config/` | Recommend | Sets the folder that holds your files. This should be the place you just chose. By default, it will create a Docker volume for the files for persistency but they will not be accessible. |
|
||||||
| `-e ARGS=(your arguments)` | No | Sets the arguments to run Zoraxy with. Enter them as you would normally. By default, it is ran with `-port=:8000 -noauth=false` |
|
| `-e ARGS='(your arguments)'` | No | Sets the arguments to run Zoraxy with. Enter them as you would normally. By default, it is ran with `-noauth=false` but <b>you cannot change the management port.</b> This is required for the healthcheck to work. |
|
||||||
| `-e VERSION=(version)` | No | Sets the version of Zoraxy that the container will download. Must be a supported version found on the Zoraxy Github. Defaults to the latest if not set. |
|
| `zoraxydocker/zoraxy:latest` | Yes | The repository on Docker hub. By default, it is the latest version that is published. |
|
||||||
| `passivelemon/zoraxy-docker:latest` | Yes | The repository on Docker hub. By default, it is the latest version that I have published. |
|
|
||||||
|
|
||||||
## Examples: </br>
|
## Examples: </br>
|
||||||
### Docker Run </br>
|
### Docker Run </br>
|
||||||
```
|
```
|
||||||
docker run -d --name zoraxy -p 80:80 -p 443:443 -p 8005:8000/tcp -v /home/docker/Containers/Zoraxy:/zoraxy/data/ -e ARGS="-port=:8000 -noauth=false" passivelemon/zoraxy-docker:latest
|
docker run -d --name zoraxy -p 80:80 -p 443:443 -p 8005:8000/tcp -v /home/docker/Containers/Zoraxy:/opt/zoraxy/config/ -e ARGS='-noauth=false' zoraxydocker/zoraxy:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose </br>
|
### Docker Compose </br>
|
||||||
```
|
```yml
|
||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
zoraxy-docker:
|
zoraxy-docker:
|
||||||
image: passivelemon/zoraxy-docker:latest
|
image: zoraxydocker/zoraxy:latest
|
||||||
container_name: zoraxy
|
container_name: zoraxy
|
||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
- 443:443
|
- 443:443
|
||||||
- 8005:8000/tcp
|
- 8005:8000/tcp
|
||||||
volumes:
|
volumes:
|
||||||
- /home/docker/Containers/Zoraxy:/zoraxy/data/
|
- /home/docker/Containers/Zoraxy:/opt/zoraxy/config/
|
||||||
environment:
|
environment:
|
||||||
ARGS: '-port=:8000 -noauth=false'
|
ARGS: '-noauth=false'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Other </br>
|
|
||||||
If the container doesn't start properly, you might be rate limited from GitHub for some amount of time. You can check this by running `curl -s https://api.github.com/repos/tobychui/zoraxy/releases` on the host. If you are, you will just have to wait for a little while or use a VPN. </br>
|
|
||||||
|
@ -1,20 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
echo "Zoraxy version $VERSION"
|
||||||
|
|
||||||
cd /zoraxy/data/
|
zoraxy -port=:8000 ${ARGS}
|
||||||
|
|
||||||
if [ "$VERSION" != "" ]; then
|
|
||||||
echo "|| Using release ${VERSION} ||"
|
|
||||||
release=${VERSION}
|
|
||||||
else
|
|
||||||
echo "|| Using latest release ||"
|
|
||||||
# Gets the latest pre-release version tag. Will be updated when official release comes out.
|
|
||||||
release=$(curl -s https://api.github.com/repos/tobychui/zoraxy/releases | jq -r 'map(select(.prerelease)) | .[0].tag_name')
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -e "/zoraxy/data/zoraxy_linux_amd64-${release}" ]; then
|
|
||||||
echo "|| Downloading version ${release} ||"
|
|
||||||
curl -Lso /zoraxy/data/zoraxy_linux_amd64-${release} https://github.com/tobychui/zoraxy/releases/download/${release}/zoraxy_linux_amd64
|
|
||||||
chmod u+x /zoraxy/data/zoraxy_linux_amd64-${release}
|
|
||||||
fi
|
|
||||||
|
|
||||||
./zoraxy_linux_amd64-${release} ${ARGS}
|
|
||||||
|
@ -148,7 +148,7 @@
|
|||||||
Reverse Proxy
|
Reverse Proxy
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Simple to use, noobs friendly reverse proxy server that can be easily set-up using a web form and a few toggle switches.</p>
|
<p>Simple to use noob-friendly reverse proxy server that can be easily set up using a web form and a few toggle switches.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="four wide column featureItem">
|
<div class="four wide column featureItem">
|
||||||
@ -158,7 +158,7 @@
|
|||||||
Redirection
|
Redirection
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Direct and intuitive redirection rules with basic rewrite options. Suitable for most of the simple use cases.</p>
|
<p>Direct and intuitive redirection rules with basic rewrite options. Suitable for most simple use cases.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="four wide column featureItem">
|
<div class="four wide column featureItem">
|
||||||
@ -168,7 +168,7 @@
|
|||||||
Geo-IP & Blacklist
|
Geo-IP & Blacklist
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Blacklist with GeoIP support. Allow easy setup for regional services.</p>
|
<p>Blacklist with GeoIP support. Allows easy setup for regional services.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="four wide column featureItem">
|
<div class="four wide column featureItem">
|
||||||
@ -189,7 +189,7 @@
|
|||||||
Web SSH
|
Web SSH
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Integrated with Gotty Web SSH terminal, allow one-stop management of your nodes inside private LAN via gateway nodes.</p>
|
<p>Integration with Gotty Web SSH terminal allows one-stop management of your nodes inside private LAN via gateway nodes.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="four wide column featureItem">
|
<div class="four wide column featureItem">
|
||||||
@ -199,7 +199,7 @@
|
|||||||
Real Time Statistics
|
Real Time Statistics
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Traffic data collection and real time analytic tools, provide you the best insights of visitors data without cookies.</p>
|
<p>Traffic data collection and real-time analytic tools provide you the best insight of visitors data without cookies.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="four wide column featureItem">
|
<div class="four wide column featureItem">
|
||||||
@ -209,7 +209,7 @@
|
|||||||
Scanner & Utilities
|
Scanner & Utilities
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Build in IP scanner and mDNS discovering service, enable automatic service discovery within LAN.</p>
|
<p>Build in IP scanner and mDNS discovery service to enable automatic service discovery within LAN.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="four wide column featureItem">
|
<div class="four wide column featureItem">
|
||||||
@ -219,7 +219,7 @@
|
|||||||
Open Source
|
Open Source
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
<p>Project is open source under AGPL on Github. Feel free to contribute on missing functions you need! </p>
|
<p>Project is open-source under AGPL on Github. Feel free to contribute on missing functions you need! </p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,7 +19,8 @@ clean:
|
|||||||
|
|
||||||
$(PLATFORMS):
|
$(PLATFORMS):
|
||||||
@echo "Building $(os)/$(arch)"
|
@echo "Building $(os)/$(arch)"
|
||||||
GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) GOARM=6 go build -o './dist/zoraxy_$(os)_$(arch)' -ldflags "-s -w" -trimpath
|
GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) $(if $(filter linux/arm,$(os)/$(arch)),GOARM=6,) go build -o './dist/zoraxy_$(os)_$(arch)' -ldflags "-s -w" -trimpath
|
||||||
|
# GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) GOARM=6 go build -o './dist/zoraxy_$(os)_$(arch)' -ldflags "-s -w" -trimpath
|
||||||
|
|
||||||
|
|
||||||
fixwindows:
|
fixwindows:
|
||||||
|
39
src/acme.go
@ -1,9 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"log"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -28,7 +28,7 @@ func getRandomPort(minPort int) int {
|
|||||||
|
|
||||||
// init the new ACME instance
|
// init the new ACME instance
|
||||||
func initACME() *acme.ACMEHandler {
|
func initACME() *acme.ACMEHandler {
|
||||||
log.Println("Starting ACME handler")
|
SystemWideLogger.Println("Starting ACME handler")
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
// Generate a random port above 30000
|
// Generate a random port above 30000
|
||||||
port := getRandomPort(30000)
|
port := getRandomPort(30000)
|
||||||
@ -38,12 +38,12 @@ func initACME() *acme.ACMEHandler {
|
|||||||
port = getRandomPort(30000)
|
port = getRandomPort(30000)
|
||||||
}
|
}
|
||||||
|
|
||||||
return acme.NewACME("https://acme-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port))
|
return acme.NewACME("https://acme-v02.api.letsencrypt.org/directory", strconv.Itoa(port))
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the special routing rule for ACME
|
// create the special routing rule for ACME
|
||||||
func acmeRegisterSpecialRoutingRule() {
|
func acmeRegisterSpecialRoutingRule() {
|
||||||
log.Println("Assigned temporary port:" + acmeHandler.Getport())
|
SystemWideLogger.Println("Assigned temporary port:" + acmeHandler.Getport())
|
||||||
|
|
||||||
err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{
|
err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{
|
||||||
ID: "acme-autorenew",
|
ID: "acme-autorenew",
|
||||||
@ -65,7 +65,8 @@ func acmeRegisterSpecialRoutingRule() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resBody, err := ioutil.ReadAll(res.Body)
|
resBody, err := io.ReadAll(res.Body)
|
||||||
|
defer res.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error reading: %s\n", err)
|
fmt.Printf("error reading: %s\n", err)
|
||||||
return
|
return
|
||||||
@ -77,7 +78,7 @@ func acmeRegisterSpecialRoutingRule() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[Err] " + err.Error())
|
SystemWideLogger.PrintAndLog("ACME", "Unable register temp port for DNS resolver", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request)
|
|||||||
if dynamicProxyRouter.Option.Port == 443 {
|
if dynamicProxyRouter.Option.Port == 443 {
|
||||||
//Enable port 80 to 443 redirect
|
//Enable port 80 to 443 redirect
|
||||||
if !dynamicProxyRouter.Option.ForceHttpsRedirect {
|
if !dynamicProxyRouter.Option.ForceHttpsRedirect {
|
||||||
log.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests")
|
SystemWideLogger.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests")
|
||||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
|
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
|
||||||
} else {
|
} else {
|
||||||
//Set this to true, so after renew, do not turn it off
|
//Set this to true, so after renew, do not turn it off
|
||||||
@ -108,8 +109,28 @@ func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request)
|
|||||||
if dynamicProxyRouter.Option.Port == 443 {
|
if dynamicProxyRouter.Option.Port == 443 {
|
||||||
if !isForceHttpsRedirectEnabledOriginally {
|
if !isForceHttpsRedirectEnabledOriginally {
|
||||||
//Default is off. Turn the redirection off
|
//Default is off. Turn the redirection off
|
||||||
log.Println("Restoring HTTP to HTTPS redirect settings")
|
SystemWideLogger.PrintAndLog("ACME", "Restoring HTTP to HTTPS redirect settings", nil)
|
||||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
|
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleACMEPreferredCA return the user preferred / default CA for new subdomain auto creation
|
||||||
|
func HandleACMEPreferredCA(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ca, err := utils.PostPara(r, "set")
|
||||||
|
if err != nil {
|
||||||
|
//Return the current ca to user
|
||||||
|
prefCA := "Let's Encrypt"
|
||||||
|
sysdb.Read("acmepref", "prefca", &prefCA)
|
||||||
|
js, _ := json.Marshal(prefCA)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
} else {
|
||||||
|
//Check if the CA is supported
|
||||||
|
acme.IsSupportedCA(ca)
|
||||||
|
//Set the new config
|
||||||
|
sysdb.Write("acmepref", "prefca", ca)
|
||||||
|
SystemWideLogger.Println("Updating prefered ACME CA to " + ca)
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
36
src/api.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/acme/acmewizard"
|
"imuslab.com/zoraxy/mod/acme/acmewizard"
|
||||||
"imuslab.com/zoraxy/mod/auth"
|
"imuslab.com/zoraxy/mod/auth"
|
||||||
@ -53,7 +54,15 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
|
authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
|
||||||
authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
|
authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
|
||||||
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
|
authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
|
||||||
|
authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)
|
||||||
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
authRouter.HandleFunc("/api/proxy/requestIsProxied", HandleManagementProxyCheck)
|
||||||
|
//Reverse proxy root related APIs
|
||||||
|
authRouter.HandleFunc("/api/proxy/root/listOptions", HandleRootRouteOptionList)
|
||||||
|
authRouter.HandleFunc("/api/proxy/root/updateOptions", HandleRootRouteOptionsUpdate)
|
||||||
|
//Reverse proxy auth related APIs
|
||||||
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/list", ListProxyBasicAuthExceptionPaths)
|
||||||
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/add", AddProxyBasicAuthExceptionPaths)
|
||||||
|
authRouter.HandleFunc("/api/proxy/auth/exceptions/delete", RemoveProxyBasicAuthExceptionPaths)
|
||||||
|
|
||||||
//TLS / SSL config
|
//TLS / SSL config
|
||||||
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
|
authRouter.HandleFunc("/api/cert/tls", handleToggleTLSProxy)
|
||||||
@ -154,6 +163,7 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains)
|
authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains)
|
||||||
authRouter.HandleFunc("/api/acme/obtainCert", AcmeCheckAndHandleRenewCertificate)
|
authRouter.HandleFunc("/api/acme/obtainCert", AcmeCheckAndHandleRenewCertificate)
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/enable", acmeAutoRenewer.HandleAutoRenewEnable)
|
authRouter.HandleFunc("/api/acme/autoRenew/enable", acmeAutoRenewer.HandleAutoRenewEnable)
|
||||||
|
authRouter.HandleFunc("/api/acme/autoRenew/ca", HandleACMEPreferredCA)
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/email", acmeAutoRenewer.HandleACMEEmail)
|
authRouter.HandleFunc("/api/acme/autoRenew/email", acmeAutoRenewer.HandleACMEEmail)
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/setDomains", acmeAutoRenewer.HandleSetAutoRenewDomains)
|
authRouter.HandleFunc("/api/acme/autoRenew/setDomains", acmeAutoRenewer.HandleSetAutoRenewDomains)
|
||||||
authRouter.HandleFunc("/api/acme/autoRenew/listDomains", acmeAutoRenewer.HandleLoadAutoRenewDomains)
|
authRouter.HandleFunc("/api/acme/autoRenew/listDomains", acmeAutoRenewer.HandleLoadAutoRenewDomains)
|
||||||
@ -161,10 +171,32 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/acme/autoRenew/renewNow", acmeAutoRenewer.HandleRenewNow)
|
authRouter.HandleFunc("/api/acme/autoRenew/renewNow", acmeAutoRenewer.HandleRenewNow)
|
||||||
authRouter.HandleFunc("/api/acme/wizard", acmewizard.HandleGuidedStepCheck) //ACME Wizard
|
authRouter.HandleFunc("/api/acme/wizard", acmewizard.HandleGuidedStepCheck) //ACME Wizard
|
||||||
|
|
||||||
|
//Static Web Server
|
||||||
|
authRouter.HandleFunc("/api/webserv/status", staticWebServer.HandleGetStatus)
|
||||||
|
authRouter.HandleFunc("/api/webserv/start", staticWebServer.HandleStartServer)
|
||||||
|
authRouter.HandleFunc("/api/webserv/stop", staticWebServer.HandleStopServer)
|
||||||
|
authRouter.HandleFunc("/api/webserv/setPort", staticWebServer.HandlePortChange)
|
||||||
|
authRouter.HandleFunc("/api/webserv/setDirList", staticWebServer.SetEnableDirectoryListing)
|
||||||
|
if *allowWebFileManager {
|
||||||
|
//Web Directory Manager file operation functions
|
||||||
|
authRouter.HandleFunc("/api/fs/list", staticWebServer.FileManager.HandleList)
|
||||||
|
authRouter.HandleFunc("/api/fs/upload", staticWebServer.FileManager.HandleUpload)
|
||||||
|
authRouter.HandleFunc("/api/fs/download", staticWebServer.FileManager.HandleDownload)
|
||||||
|
authRouter.HandleFunc("/api/fs/newFolder", staticWebServer.FileManager.HandleNewFolder)
|
||||||
|
authRouter.HandleFunc("/api/fs/copy", staticWebServer.FileManager.HandleFileCopy)
|
||||||
|
authRouter.HandleFunc("/api/fs/move", staticWebServer.FileManager.HandleFileMove)
|
||||||
|
authRouter.HandleFunc("/api/fs/properties", staticWebServer.FileManager.HandleFileProperties)
|
||||||
|
authRouter.HandleFunc("/api/fs/del", staticWebServer.FileManager.HandleFileDelete)
|
||||||
|
}
|
||||||
|
|
||||||
//Others
|
//Others
|
||||||
http.HandleFunc("/api/info/x", HandleZoraxyInfo)
|
http.HandleFunc("/api/info/x", HandleZoraxyInfo)
|
||||||
http.HandleFunc("/api/conf/export", ExportConfigAsZip)
|
authRouter.HandleFunc("/api/info/geoip", HandleGeoIpLookup)
|
||||||
http.HandleFunc("/api/conf/import", ImportConfigFromZip)
|
authRouter.HandleFunc("/api/conf/export", ExportConfigAsZip)
|
||||||
|
authRouter.HandleFunc("/api/conf/import", ImportConfigFromZip)
|
||||||
|
|
||||||
|
//Debug
|
||||||
|
authRouter.HandleFunc("/api/info/pprof", pprof.Index)
|
||||||
|
|
||||||
//If you got APIs to add, append them here
|
//If you got APIs to add, append them here
|
||||||
}
|
}
|
||||||
|
11
src/cert.go
@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -128,7 +127,7 @@ func handleListDomains(w http.ResponseWriter, r *http.Request) {
|
|||||||
certBtyes, err := os.ReadFile(certFilepath)
|
certBtyes, err := os.ReadFile(certFilepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Unable to load this file
|
// Unable to load this file
|
||||||
log.Println("Unable to load certificate: " + certFilepath)
|
SystemWideLogger.PrintAndLog("TLS", "Unable to load certificate: "+certFilepath, err)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
// Cert loaded. Check its expiry time
|
// Cert loaded. Check its expiry time
|
||||||
@ -182,11 +181,11 @@ func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
if newState == "true" {
|
if newState == "true" {
|
||||||
sysdb.Write("settings", "usetls", true)
|
sysdb.Write("settings", "usetls", true)
|
||||||
log.Println("Enabling TLS mode on reverse proxy")
|
SystemWideLogger.Println("Enabling TLS mode on reverse proxy")
|
||||||
dynamicProxyRouter.UpdateTLSSetting(true)
|
dynamicProxyRouter.UpdateTLSSetting(true)
|
||||||
} else if newState == "false" {
|
} else if newState == "false" {
|
||||||
sysdb.Write("settings", "usetls", false)
|
sysdb.Write("settings", "usetls", false)
|
||||||
log.Println("Disabling TLS mode on reverse proxy")
|
SystemWideLogger.Println("Disabling TLS mode on reverse proxy")
|
||||||
dynamicProxyRouter.UpdateTLSSetting(false)
|
dynamicProxyRouter.UpdateTLSSetting(false)
|
||||||
} else {
|
} else {
|
||||||
utils.SendErrorResponse(w, "invalid state given. Only support true or false")
|
utils.SendErrorResponse(w, "invalid state given. Only support true or false")
|
||||||
@ -213,11 +212,11 @@ func handleSetTlsRequireLatest(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
if newState == "true" {
|
if newState == "true" {
|
||||||
sysdb.Write("settings", "forceLatestTLS", true)
|
sysdb.Write("settings", "forceLatestTLS", true)
|
||||||
log.Println("Updating minimum TLS version to v1.2 or above")
|
SystemWideLogger.Println("Updating minimum TLS version to v1.2 or above")
|
||||||
dynamicProxyRouter.UpdateTLSVersion(true)
|
dynamicProxyRouter.UpdateTLSVersion(true)
|
||||||
} else if newState == "false" {
|
} else if newState == "false" {
|
||||||
sysdb.Write("settings", "forceLatestTLS", false)
|
sysdb.Write("settings", "forceLatestTLS", false)
|
||||||
log.Println("Updating minimum TLS version to v1.0 or above")
|
SystemWideLogger.Println("Updating minimum TLS version to v1.0 or above")
|
||||||
dynamicProxyRouter.UpdateTLSVersion(false)
|
dynamicProxyRouter.UpdateTLSVersion(false)
|
||||||
} else {
|
} else {
|
||||||
utils.SendErrorResponse(w, "invalid state given")
|
utils.SendErrorResponse(w, "invalid state given")
|
||||||
|
@ -5,8 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -26,16 +24,19 @@ import (
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
type Record struct {
|
type Record struct {
|
||||||
ProxyType string
|
ProxyType string
|
||||||
Rootname string
|
Rootname string
|
||||||
ProxyTarget string
|
ProxyTarget string
|
||||||
UseTLS bool
|
UseTLS bool
|
||||||
SkipTlsValidation bool
|
BypassGlobalTLS bool
|
||||||
RequireBasicAuth bool
|
SkipTlsValidation bool
|
||||||
BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials
|
RequireBasicAuth bool
|
||||||
|
BasicAuthCredentials []*dynamicproxy.BasicAuthCredentials
|
||||||
|
BasicAuthExceptionRules []*dynamicproxy.BasicAuthExceptionRule
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaveReverseProxyConfig(proxyConfigRecord *Record) error {
|
// Save a reverse proxy config record to file
|
||||||
|
func SaveReverseProxyConfigToFile(proxyConfigRecord *Record) error {
|
||||||
//TODO: Make this accept new def types
|
//TODO: Make this accept new def types
|
||||||
os.MkdirAll("./conf/proxy/", 0775)
|
os.MkdirAll("./conf/proxy/", 0775)
|
||||||
filename := getFilenameFromRootName(proxyConfigRecord.Rootname)
|
filename := getFilenameFromRootName(proxyConfigRecord.Rootname)
|
||||||
@ -45,17 +46,26 @@ func SaveReverseProxyConfig(proxyConfigRecord *Record) error {
|
|||||||
|
|
||||||
//Write to file
|
//Write to file
|
||||||
js, _ := json.MarshalIndent(thisRecord, "", " ")
|
js, _ := json.MarshalIndent(thisRecord, "", " ")
|
||||||
return ioutil.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775)
|
return os.WriteFile(filepath.Join("./conf/proxy/", filename), js, 0775)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveReverseProxyConfig(rootname string) error {
|
// Save a running reverse proxy endpoint to file (with automatic endpoint to record conversion)
|
||||||
|
func SaveReverseProxyEndpointToFile(proxyEndpoint *dynamicproxy.ProxyEndpoint) error {
|
||||||
|
recordToSave, err := ConvertProxyEndpointToRecord(proxyEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return SaveReverseProxyConfigToFile(recordToSave)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveReverseProxyConfigFile(rootname string) error {
|
||||||
filename := getFilenameFromRootName(rootname)
|
filename := getFilenameFromRootName(rootname)
|
||||||
removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/")
|
removePendingFile := strings.ReplaceAll(filepath.Join("./conf/proxy/", filename), "\\", "/")
|
||||||
log.Println("Config Removed: ", removePendingFile)
|
SystemWideLogger.Println("Config Removed: ", removePendingFile)
|
||||||
if utils.FileExists(removePendingFile) {
|
if utils.FileExists(removePendingFile) {
|
||||||
err := os.Remove(removePendingFile)
|
err := os.Remove(removePendingFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
SystemWideLogger.PrintAndLog("Proxy", "Unabel to remove config file", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,8 +76,19 @@ func RemoveReverseProxyConfig(rootname string) error {
|
|||||||
|
|
||||||
// Return ptype, rootname and proxyTarget, error if any
|
// Return ptype, rootname and proxyTarget, error if any
|
||||||
func LoadReverseProxyConfig(filename string) (*Record, error) {
|
func LoadReverseProxyConfig(filename string) (*Record, error) {
|
||||||
thisRecord := Record{}
|
thisRecord := Record{
|
||||||
configContent, err := ioutil.ReadFile(filename)
|
ProxyType: "",
|
||||||
|
Rootname: "",
|
||||||
|
ProxyTarget: "",
|
||||||
|
UseTLS: false,
|
||||||
|
BypassGlobalTLS: false,
|
||||||
|
SkipTlsValidation: false,
|
||||||
|
RequireBasicAuth: false,
|
||||||
|
BasicAuthCredentials: []*dynamicproxy.BasicAuthCredentials{},
|
||||||
|
BasicAuthExceptionRules: []*dynamicproxy.BasicAuthExceptionRule{},
|
||||||
|
}
|
||||||
|
|
||||||
|
configContent, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &thisRecord, err
|
return &thisRecord, err
|
||||||
}
|
}
|
||||||
@ -82,6 +103,23 @@ func LoadReverseProxyConfig(filename string) (*Record, error) {
|
|||||||
return &thisRecord, nil
|
return &thisRecord, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a running proxy endpoint object into a save-able record struct
|
||||||
|
func ConvertProxyEndpointToRecord(targetProxyEndpoint *dynamicproxy.ProxyEndpoint) (*Record, error) {
|
||||||
|
thisProxyConfigRecord := Record{
|
||||||
|
ProxyType: targetProxyEndpoint.GetProxyTypeString(),
|
||||||
|
Rootname: targetProxyEndpoint.RootOrMatchingDomain,
|
||||||
|
ProxyTarget: targetProxyEndpoint.Domain,
|
||||||
|
UseTLS: targetProxyEndpoint.RequireTLS,
|
||||||
|
BypassGlobalTLS: targetProxyEndpoint.BypassGlobalTLS,
|
||||||
|
SkipTlsValidation: targetProxyEndpoint.SkipCertValidations,
|
||||||
|
RequireBasicAuth: targetProxyEndpoint.RequireBasicAuth,
|
||||||
|
BasicAuthCredentials: targetProxyEndpoint.BasicAuthCredentials,
|
||||||
|
BasicAuthExceptionRules: targetProxyEndpoint.BasicAuthExceptionRules,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &thisProxyConfigRecord, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getFilenameFromRootName(rootname string) string {
|
func getFilenameFromRootName(rootname string) string {
|
||||||
//Generate a filename for this rootname
|
//Generate a filename for this rootname
|
||||||
filename := strings.ReplaceAll(rootname, ".", "_")
|
filename := strings.ReplaceAll(rootname, ".", "_")
|
||||||
@ -155,14 +193,14 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {
|
|||||||
//Also zip in the sysdb
|
//Also zip in the sysdb
|
||||||
zipFile, err := zipWriter.Create("sys.db")
|
zipFile, err := zipWriter.Create("sys.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[Backup] Unable to zip sysdb: " + err.Error())
|
SystemWideLogger.PrintAndLog("Backup", "Unable to zip sysdb", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the file on disk
|
// Open the file on disk
|
||||||
file, err := os.Open("sys.db")
|
file, err := os.Open("sys.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[Backup] Unable to open sysdb: " + err.Error())
|
SystemWideLogger.PrintAndLog("Backup", "Unable to open sysdb", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
@ -170,7 +208,7 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Copy the file contents to the zip file
|
// Copy the file contents to the zip file
|
||||||
_, err = io.Copy(zipFile, file)
|
_, err = io.Copy(zipFile, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
SystemWideLogger.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,12 +313,12 @@ func ImportConfigFromZip(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Send a success response
|
// Send a success response
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
log.Println("Configuration restored")
|
SystemWideLogger.Println("Configuration restored")
|
||||||
fmt.Fprintln(w, "Configuration restored")
|
fmt.Fprintln(w, "Configuration restored")
|
||||||
|
|
||||||
if restoreDatabase {
|
if restoreDatabase {
|
||||||
go func() {
|
go func() {
|
||||||
log.Println("Database altered. Restarting in 3 seconds...")
|
SystemWideLogger.Println("Database altered. Restarting in 3 seconds...")
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
uuid "github.com/satori/go.uuid"
|
"github.com/google/uuid"
|
||||||
"imuslab.com/zoraxy/mod/email"
|
"imuslab.com/zoraxy/mod/email"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
@ -180,7 +180,7 @@ func setSMTPAdminAddress(adminAddr string) error {
|
|||||||
return sysdb.Write("smtp", "admin", adminAddr)
|
return sysdb.Write("smtp", "admin", adminAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load SMTP admin address. Return empty string if not set
|
// Load SMTP admin address. Return empty string if not set
|
||||||
func loadSMTPAdminAddr() string {
|
func loadSMTPAdminAddr() string {
|
||||||
adminAddr := ""
|
adminAddr := ""
|
||||||
if sysdb.KeyExists("smtp", "admin") {
|
if sysdb.KeyExists("smtp", "admin") {
|
||||||
@ -223,7 +223,7 @@ func HandleAdminAccountResetEmail(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordResetAccessToken = uuid.NewV4().String()
|
passwordResetAccessToken = uuid.New().String()
|
||||||
|
|
||||||
//SMTP info exists. Send reset account email
|
//SMTP info exists. Send reset account email
|
||||||
lastAccountResetEmail = time.Now().Unix()
|
lastAccountResetEmail = time.Now().Unix()
|
||||||
|
39
src/geoip.go
@ -1,39 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/oschwald/geoip2-golang"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getCountryCodeFromRequest(r *http.Request) string {
|
|
||||||
countryCode := ""
|
|
||||||
|
|
||||||
// Get the IP address of the user from the request headers
|
|
||||||
ipAddress := r.Header.Get("X-Forwarded-For")
|
|
||||||
if ipAddress == "" {
|
|
||||||
ipAddress = strings.Split(r.RemoteAddr, ":")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the GeoIP database
|
|
||||||
db, err := geoip2.Open("./tmp/GeoIP2-Country.mmdb")
|
|
||||||
if err != nil {
|
|
||||||
// Handle the error
|
|
||||||
return countryCode
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
// Look up the country code for the IP address
|
|
||||||
record, err := db.Country(net.ParseIP(ipAddress))
|
|
||||||
if err != nil {
|
|
||||||
// Handle the error
|
|
||||||
return countryCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the ISO country code from the record
|
|
||||||
countryCode = record.Country.IsoCode
|
|
||||||
|
|
||||||
return countryCode
|
|
||||||
}
|
|
17
src/go.mod
@ -4,16 +4,15 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/boltdb/bolt v1.3.1
|
github.com/boltdb/bolt v1.3.1
|
||||||
github.com/go-acme/lego/v4 v4.12.1 // indirect
|
github.com/go-acme/lego/v4 v4.14.0
|
||||||
github.com/go-ping/ping v1.1.0
|
github.com/go-ping/ping v1.1.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.1
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/grandcat/zeroconf v1.0.0
|
github.com/grandcat/zeroconf v1.0.0
|
||||||
github.com/likexian/whois v1.15.0 // indirect
|
github.com/likexian/whois v1.15.1
|
||||||
github.com/microcosm-cc/bluemonday v1.0.24
|
github.com/microcosm-cc/bluemonday v1.0.25
|
||||||
github.com/oschwald/geoip2-golang v1.8.0
|
golang.org/x/net v0.14.0
|
||||||
github.com/satori/go.uuid v1.2.0
|
golang.org/x/sys v0.11.0
|
||||||
golang.org/x/net v0.11.0
|
golang.org/x/tools v0.12.0 // indirect
|
||||||
golang.org/x/sys v0.9.0
|
|
||||||
)
|
)
|
||||||
|
287
src/go.sum
@ -123,7 +123,6 @@ cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOV
|
|||||||
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
|
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
|
||||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
|
||||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||||
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
|
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
|
||||||
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
||||||
@ -392,34 +391,59 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS
|
|||||||
cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=
|
cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M=
|
||||||
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
|
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo=
|
||||||
|
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0/go.mod h1:copqlcjMWc/wgQ1N2fzsJFQxDdqKGg1EQt8T5wJMOGE=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0/go.mod h1:y2zXtLSMM/X5Mfawq0lOftpWn3f4V6OCsRdINsvWBPI=
|
||||||
|
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s=
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
|
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
|
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
|
||||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
|
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
|
||||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
|
||||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||||
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||||
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
|
||||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||||
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
|
||||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
|
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||||
@ -427,7 +451,26 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
|
|||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go-v2 v1.19.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.18.28/go.mod h1:nIL+4/8JdAuNHEjn/gPEXqtnS02Q3NXB/9Z7o5xE4+A=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.27/go.mod h1:syOqAek45ZXZp29HlnRS/BNgMIW6uiRmeuQsz4Qh2UE=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5/go.mod h1:Gj7tm95r+QsDoN2Fhuz/3npQvcZbkEf5mL70n3Xfluc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35/go.mod h1:ipR5PvpSPqIqL5Mi82BxLnfMkHVbmco8kUwO2xrCi0M=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29/go.mod h1:M/eUABlDbw2uVrdAn+UsI6M727qp2fxkp8K0ejcBDUY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36/go.mod h1:Rmw2M1hMVTwiUhjwMoIBFWFJMhvJbct06sSidxInkhY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.27/go.mod h1:ZdjYvJpDlefgh8/hWelJhqgqJeodxu4SmbVsSdBlL7E=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.30/go.mod h1:qQtIBl5OVMfmeQkz8HaVyh5DzFmmFXyvK27UgIgOr4c=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.29/go.mod h1:fDbkK4o7fpPXWn8YAPmTieAMuB9mk/VgvW64uaUqxd4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.4/go.mod h1:JniVpqvw90sVjNqanGLufrVapWySL28fhBlYgl96Q/w=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/lightsail v1.27.2/go.mod h1:gQhLZrTEath4zik5ixIe6axvgY5jJrgSBDJ360Fxnco=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/route53 v1.28.4/go.mod h1:VBLWpaHvhQNeu7N9rMEf00SWeOONb/HvaDUxe/7b44k=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.37.0/go.mod h1:PwyKKVL0cNkC37QwLcrhyeCrAk+5bY8O2ou7USyAS2A=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.12.13/go.mod h1:DfX0sWuT46KpcqbMhJ9QWtxAIP1VozkDWf8VAkByjYY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.13/go.mod h1:BzqsVVFduubEmzrVtUFQQIQdFqvUItF8XUq2EnS8Wog=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3/go.mod h1:yVGZA1CPkmUhBdA039jXNJJG7/6t+G+EBWmFq23xqnY=
|
||||||
|
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
@ -438,12 +481,14 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
|
|||||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
|
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
|
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
|
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
|
||||||
@ -455,7 +500,9 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
|||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/civo/civogo v0.3.11/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g=
|
github.com/civo/civogo v0.3.11/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/cloudflare-go v0.49.0/go.mod h1:h0QgcIZ3qEXwFiwfBO8sQxjVdYsLX+PfD7NFEnANaKg=
|
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||||
|
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||||
|
github.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
@ -487,9 +534,11 @@ github.com/deepmap/oapi-codegen v1.9.1/go.mod h1:PLqNAhdedP8ttRpBBkzLKU3bp+Fpy+t
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||||
|
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||||
github.com/dnsimple/dnsimple-go v0.71.1/go.mod h1:F9WHww9cC76hrnwGFfAfrqdW99j3MOYasQcIwTS/aUk=
|
github.com/dnsimple/dnsimple-go v1.2.0/go.mod h1:z/cs26v/eiRvUyXsHQBLd8lWF8+cD6GbmkPH84plM4U=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
@ -503,7 +552,7 @@ github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJ
|
|||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||||
github.com/exoscale/egoscale v0.90.0/go.mod h1:wyXE5zrnFynMXA0jMhwQqSe24CfUhmBk2WI5wFZcq6Y=
|
github.com/exoscale/egoscale v0.100.1/go.mod h1:BAb9p4rmyU+Wl400CJZO5270H2sXtdsZjLcm5xMKkz4=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
@ -511,22 +560,27 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
|
|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/go-acme/lego/v4 v4.12.1 h1:Cy3FS7wADLNBqCLpz2wdfdNrThW9rZy8RCAfnUrL2uE=
|
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||||
github.com/go-acme/lego/v4 v4.12.1/go.mod h1:UZoOlhVmUYP/N0z4tEbfUjoCNHRZNObzqWZtT76DIsc=
|
github.com/go-acme/lego/v4 v4.14.0 h1:/skZoRHgVh0d2RK7l1g3Ch8HqeqP9LB8ZEjLdGEpcDE=
|
||||||
|
github.com/go-acme/lego/v4 v4.14.0/go.mod h1:zjmvNCDLGz7GrC1OqdVpVmZFKSRabEDtWbdzmcpBsGo=
|
||||||
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
|
||||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||||
|
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
|
||||||
|
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||||
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
|
||||||
|
github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
@ -540,14 +594,18 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
|||||||
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
|
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
|
||||||
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
|
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
|
||||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
||||||
@ -556,10 +614,14 @@ github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
|
|||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||||
|
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@ -611,6 +673,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
@ -640,8 +703,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||||
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
||||||
@ -671,8 +735,9 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
|
|||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
@ -695,38 +760,44 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
|
|||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hc-install v0.4.0/go.mod h1:5d155H8EC5ewegao9A4PUTMNPZaq+TbOzkJJZ4vrXeI=
|
github.com/hashicorp/hc-install v0.5.0/go.mod h1:JyzMfbzfSBSjoDCRPna1vi/24BEDxFaCPfdHtM5SCdo=
|
||||||
|
github.com/hashicorp/hc-install v0.5.2/go.mod h1:9QISwe6newMWIfEiXpzuu1k9HAGtQYgnSH8H9T8wmoI=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hashicorp/terraform-exec v0.17.2/go.mod h1:tuIbsL2l4MlwwIZx9HPM+LOV9vVyEfBYu2GsO1uH3/8=
|
github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980=
|
||||||
github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM=
|
github.com/hashicorp/terraform-json v0.15.0/go.mod h1:+L1RNzjDU5leLFZkHTFTbJXaoqUC6TqXlFgDoOXrtvk=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
|
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
|
||||||
|
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
|
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
|
github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
|
||||||
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
|
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
@ -744,9 +815,10 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
|||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
|
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
@ -757,12 +829,15 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
|
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
|
||||||
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
|
||||||
github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A=
|
github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A=
|
||||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
|
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
|
||||||
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
|
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
|
||||||
github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
|
github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
|
||||||
@ -770,11 +845,12 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++
|
|||||||
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
||||||
github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
|
github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
|
||||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
|
github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
|
||||||
github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
|
github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
|
||||||
github.com/likexian/whois v1.15.0 h1:AYYJ5bNUo8Qy2T1Z5GgMp1oIcIlCcTDfg1buYz6TdAE=
|
github.com/likexian/whois v1.15.1 h1:6vTMI8n9s1eJdmcO4R9h1x99aQWIZZX1CD3am68gApU=
|
||||||
github.com/likexian/whois v1.15.0/go.mod h1:456fUTkh+O8F8v09bGdVl7XxBjRaQ4LvYHyVWX5Bxyg=
|
github.com/likexian/whois v1.15.1/go.mod h1:/nxmQ6YXvLz+qTxC/QFtEJNAt0zLuRxJrKiWpBJX8X0=
|
||||||
github.com/likexian/whois-parser v1.24.8/go.mod h1:b6STMHHDaSKbd4PzGrP50wWE5NzeBUETa/hT9gI0G9I=
|
github.com/likexian/whois-parser v1.24.9/go.mod h1:b6STMHHDaSKbd4PzGrP50wWE5NzeBUETa/hT9gI0G9I=
|
||||||
github.com/linode/linodego v1.9.1/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w=
|
github.com/linode/linodego v1.17.2/go.mod h1:C2iyT3Vg2O2sPxkWka4XAQ5WSUtm5LmTZ3Adw43Ra7Q=
|
||||||
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||||
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
|
||||||
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
|
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
|
||||||
@ -799,22 +875,24 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw=
|
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8=
|
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
|
|
||||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||||
github.com/mimuret/golang-iij-dpf v0.7.1/go.mod h1:IXWYcQVIHYzuM+W7kDWX0mseHDfUoqMuarxMXHVTir0=
|
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
|
||||||
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
@ -825,27 +903,32 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
|
|||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||||
|
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
|
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk=
|
github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk=
|
||||||
github.com/nrdcg/desec v0.6.0/go.mod h1:wybWg5cRrNmtXLYpUCPCLvz4jfFNEGZQEnoUiX9WqcY=
|
github.com/nrdcg/desec v0.7.0/go.mod h1:e1uRqqKv1mJdd5+SQROAhmy75lKMphLzWIuASLkpeFY=
|
||||||
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
|
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
|
||||||
github.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4=
|
github.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4=
|
||||||
github.com/nrdcg/goinwx v0.8.1/go.mod h1:tILVc10gieBp/5PMvbcYeXM6pVQ+c9jxDZnpaR1UW7c=
|
github.com/nrdcg/goinwx v0.8.2/go.mod h1:mnMSTi7CXBu2io4DzdOBoGFA1XclD0sEPWJaDhNgkA4=
|
||||||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||||
github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms=
|
github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms=
|
||||||
github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk+CPoA=
|
github.com/nrdcg/porkbun v0.2.0/go.mod h1:i0uLMn9ItFsLsSQIAeEu1wQ9/+6EvX1eQw15hulMMRw=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/nzdjb/go-metaname v1.0.0/go.mod h1:0GR0LshZax1Lz4VrOrfNSE4dGvTp7HGjiemdczXT2H4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@ -859,15 +942,14 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
|
|||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||||
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
|
github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M=
|
||||||
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
|
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
|
||||||
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
|
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||||
|
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||||
|
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||||
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@ -879,7 +961,7 @@ github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
@ -907,30 +989,33 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
|
|||||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/sacloud/api-client-go v0.2.1/go.mod h1:8fmYy5OpT3W8ltV5ZxF8evultNwKpduGN4YKmU9Af7w=
|
github.com/sacloud/api-client-go v0.2.8/go.mod h1:0CV/kWNYlS1hCNdnk6Wx7Wdg8DPFCnv0zOIzdXjeAeY=
|
||||||
github.com/sacloud/go-http v0.1.2/go.mod h1:gvWaT8LFBFnSBFVrznOQXC62uad46bHZQM8w+xoH3eE=
|
github.com/sacloud/go-http v0.1.6/go.mod h1:oLAHoDJRkptf8sq4fE8oERLkdCh0kJWfWu+paoJY7I0=
|
||||||
github.com/sacloud/iaas-api-go v1.3.2/go.mod h1:CoqpRYBG2NRB5xfqTfZNyh2lVLKyLkE/HV9ISqmbhGc=
|
github.com/sacloud/iaas-api-go v1.11.1/go.mod h1:uBDSa06F/V0OnoR66jGdbH0PVnCJw+NeE9RVbVgMfss=
|
||||||
github.com/sacloud/packages-go v0.0.5/go.mod h1:XWMBSNHT9YKY3lCh6yJsx1o1RRQQGpuhNqJA6bSHdD4=
|
github.com/sacloud/packages-go v0.0.8/go.mod h1:btPji+wtZ+Pk7MeCy+zo61o5IziBoLdHIrdGiYq9Kb8=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/sacloud/packages-go v0.0.9/go.mod h1:k+EEUMF2LlncjbNIJNOqLyZ9wjTESPIWIk1OA7x9j2Q=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
|
||||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
|
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg=
|
github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04/go.mod h1:5KS21fpch8TIMyAUv/qQqTa3GZfBDYgjaZbd2KXKYfg=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
|
github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
|
||||||
github.com/softlayer/softlayer-go v1.0.6/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qyx2MNvYyHof7ik4=
|
github.com/softlayer/softlayer-go v1.1.2/go.mod h1:hvAbzGH4LRXA6yXY8BNx99yoqZ7urfDdtl9mvBf0G+g=
|
||||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums=
|
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums=
|
||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
@ -950,7 +1035,6 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q
|
|||||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
@ -960,31 +1044,35 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
|||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.3/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490/go.mod h1:l9q4vc1QiawUB1m3RU+87yLvrrxe54jc0w/kEl4DbSQ=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490/go.mod h1:l9q4vc1QiawUB1m3RU+87yLvrrxe54jc0w/kEl4DbSQ=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/transip/gotransip/v6 v6.17.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
github.com/transip/gotransip/v6 v6.20.0/go.mod h1:nzv9eN2tdsUrm5nG5ZX6AugYIU4qgsMwIn2c0EZLk8c=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||||
github.com/ultradns/ultradns-go-sdk v1.4.0-20221107152238-f3f1d1d/go.mod h1:IgdoVzrGYzq4H4IGI0DAVnM3CbcuQDSxEP4s/j6cztI=
|
github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c/go.mod h1:F4UyVEmq4/m5lAmx+GccrxyRCXmnBjzUL09JLTQFp94=
|
||||||
github.com/urfave/cli/v2 v2.11.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
|
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
github.com/urfave/cli/v2 v2.14.0/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
|
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
|
||||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
|
||||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||||
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
@ -1000,6 +1088,7 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||||
github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||||
|
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
|
||||||
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
@ -1017,6 +1106,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@ -1026,26 +1116,36 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
|
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
|
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||||
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||||
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -1083,9 +1183,13 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -1121,6 +1225,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
|||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
@ -1136,6 +1241,7 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
@ -1146,6 +1252,7 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
@ -1156,10 +1263,11 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
|
||||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||||
|
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -1185,10 +1293,9 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri
|
|||||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||||
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||||
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
|
|
||||||
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
||||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -1203,8 +1310,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -1257,6 +1365,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -1271,6 +1380,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -1278,6 +1388,7 @@ golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -1293,23 +1404,27 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
|
||||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||||
@ -1318,6 +1433,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
|||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||||
|
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||||
|
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -1333,16 +1450,16 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
|
||||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -1408,9 +1525,12 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
|
||||||
|
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||||
|
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -1653,7 +1773,6 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@ -1664,11 +1783,11 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
|||||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ns1/ns1-go.v2 v2.6.5/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/ns1/ns1-go.v2 v2.7.6/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
@ -1682,6 +1801,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
@ -1692,6 +1812,7 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
|
|||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
|
software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ=
|
||||||
|
32
src/main.go
@ -20,6 +20,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/email"
|
"imuslab.com/zoraxy/mod/email"
|
||||||
"imuslab.com/zoraxy/mod/ganserv"
|
"imuslab.com/zoraxy/mod/ganserv"
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/mdns"
|
"imuslab.com/zoraxy/mod/mdns"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
"imuslab.com/zoraxy/mod/pathrule"
|
"imuslab.com/zoraxy/mod/pathrule"
|
||||||
@ -30,18 +31,25 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/tlscert"
|
"imuslab.com/zoraxy/mod/tlscert"
|
||||||
"imuslab.com/zoraxy/mod/uptime"
|
"imuslab.com/zoraxy/mod/uptime"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
"imuslab.com/zoraxy/mod/webserv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// General flags
|
// General flags
|
||||||
var noauth = flag.Bool("noauth", false, "Disable authentication for management interface")
|
var noauth = flag.Bool("noauth", false, "Disable authentication for management interface")
|
||||||
var showver = flag.Bool("version", false, "Show version of this server")
|
var showver = flag.Bool("version", false, "Show version of this server")
|
||||||
var allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)")
|
var allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)")
|
||||||
|
var allowMdnsScanning = flag.Bool("mdns", true, "Enable mDNS scanner and transponder")
|
||||||
var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node")
|
var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local node")
|
||||||
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
|
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
|
||||||
var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
|
var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
|
||||||
|
var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
|
||||||
|
var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
|
||||||
|
var allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
||||||
|
var logOutputToFile = flag.Bool("log", true, "Log terminal output to file")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
name = "Zoraxy"
|
name = "Zoraxy"
|
||||||
version = "2.6.5"
|
version = "2.6.8"
|
||||||
nodeUUID = "generic"
|
nodeUUID = "generic"
|
||||||
development = false //Set this to false to use embedded web fs
|
development = false //Set this to false to use embedded web fs
|
||||||
bootTime = time.Now().Unix()
|
bootTime = time.Now().Unix()
|
||||||
@ -71,10 +79,12 @@ var (
|
|||||||
tcpProxyManager *tcpprox.Manager //TCP Proxy Manager
|
tcpProxyManager *tcpprox.Manager //TCP Proxy Manager
|
||||||
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
|
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
|
||||||
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
|
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
|
||||||
|
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
|
||||||
|
|
||||||
//Helper modules
|
//Helper modules
|
||||||
EmailSender *email.Sender //Email sender that handle email sending
|
EmailSender *email.Sender //Email sender that handle email sending
|
||||||
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
|
AnalyticLoader *analytic.DataLoader //Data loader for Zoraxy Analytic
|
||||||
|
SystemWideLogger *logger.Logger //Logger for Zoraxy
|
||||||
)
|
)
|
||||||
|
|
||||||
// Kill signal handler. Do something before the system the core terminate.
|
// Kill signal handler. Do something before the system the core terminate.
|
||||||
@ -96,9 +106,12 @@ func ShutdownSeq() {
|
|||||||
netstatBuffers.Close()
|
netstatBuffers.Close()
|
||||||
fmt.Println("- Closing Statistic Collector")
|
fmt.Println("- Closing Statistic Collector")
|
||||||
statisticCollector.Close()
|
statisticCollector.Close()
|
||||||
fmt.Println("- Stopping mDNS Discoverer")
|
if mdnsTickerStop != nil {
|
||||||
//Stop the mdns service
|
fmt.Println("- Stopping mDNS Discoverer (might take a few minutes)")
|
||||||
mdnsTickerStop <- true
|
// Stop the mdns service
|
||||||
|
mdnsTickerStop <- true
|
||||||
|
}
|
||||||
|
|
||||||
mdnsScanner.Close()
|
mdnsScanner.Close()
|
||||||
fmt.Println("- Closing Certificates Auto Renewer")
|
fmt.Println("- Closing Certificates Auto Renewer")
|
||||||
acmeAutoRenewer.Close()
|
acmeAutoRenewer.Close()
|
||||||
@ -106,6 +119,9 @@ func ShutdownSeq() {
|
|||||||
fmt.Println("- Cleaning up tmp files")
|
fmt.Println("- Cleaning up tmp files")
|
||||||
os.RemoveAll("./tmp")
|
os.RemoveAll("./tmp")
|
||||||
|
|
||||||
|
fmt.Println("- Closing system wide logger")
|
||||||
|
SystemWideLogger.Close()
|
||||||
|
|
||||||
//Close database, final
|
//Close database, final
|
||||||
fmt.Println("- Stopping system database")
|
fmt.Println("- Stopping system database")
|
||||||
sysdb.Close()
|
sysdb.Close()
|
||||||
@ -141,7 +157,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
uuidBytes, err := os.ReadFile(uuidRecord)
|
uuidBytes, err := os.ReadFile(uuidRecord)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Unable to read system uuid from file system")
|
SystemWideLogger.PrintAndLog("ZeroTier", "Unable to read system uuid from file system", nil)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
nodeUUID = string(uuidBytes)
|
nodeUUID = string(uuidBytes)
|
||||||
@ -163,7 +179,7 @@ func main() {
|
|||||||
//Start the finalize sequences
|
//Start the finalize sequences
|
||||||
finalSequence()
|
finalSequence()
|
||||||
|
|
||||||
log.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port)
|
SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port)
|
||||||
err = http.ListenAndServe(handler.Port, nil)
|
err = http.ListenAndServe(handler.Port, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -5,12 +5,11 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -28,6 +27,12 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CertificateInfoJSON struct {
|
||||||
|
AcmeName string `json:"acme_name"`
|
||||||
|
AcmeUrl string `json:"acme_url"`
|
||||||
|
SkipTLS bool `json:"skip_tls"`
|
||||||
|
}
|
||||||
|
|
||||||
// ACMEUser represents a user in the ACME system.
|
// ACMEUser represents a user in the ACME system.
|
||||||
type ACMEUser struct {
|
type ACMEUser struct {
|
||||||
Email string
|
Email string
|
||||||
@ -65,7 +70,7 @@ func NewACME(acmeServer string, port string) *ACMEHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ObtainCert obtains a certificate for the specified domains.
|
// ObtainCert obtains a certificate for the specified domains.
|
||||||
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, ca string) (bool, error) {
|
func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email string, caName string, caUrl string, skipTLS bool) (bool, error) {
|
||||||
log.Println("[ACME] Obtaining certificate...")
|
log.Println("[ACME] Obtaining certificate...")
|
||||||
|
|
||||||
// generate private key
|
// generate private key
|
||||||
@ -84,17 +89,41 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
// create config
|
// create config
|
||||||
config := lego.NewConfig(&adminUser)
|
config := lego.NewConfig(&adminUser)
|
||||||
|
|
||||||
// setup who is the issuer and the key type
|
// skip TLS verify if need
|
||||||
config.CADirURL = a.DefaultAcmeServer
|
// Ref: https://github.com/go-acme/lego/blob/6af2c756ac73a9cb401621afca722d0f4112b1b8/lego/client_config.go#L74
|
||||||
|
if skipTLS {
|
||||||
|
log.Println("[INFO] Ignore TLS/SSL Verification Error for ACME Server")
|
||||||
|
config.HTTPClient.Transport = &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).DialContext,
|
||||||
|
TLSHandshakeTimeout: 30 * time.Second,
|
||||||
|
ResponseHeaderTimeout: 30 * time.Second,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Overwrite the CADir URL if set
|
// setup the custom ACME url endpoint.
|
||||||
if ca != "" {
|
if caUrl != "" {
|
||||||
caLinkOverwrite, err := loadCAApiServerFromName(ca)
|
config.CADirURL = caUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not custom ACME url, load it from ca.json
|
||||||
|
if caName == "custom" {
|
||||||
|
log.Println("[INFO] Using Custom ACME " + caUrl + " for CA Directory URL")
|
||||||
|
} else {
|
||||||
|
caLinkOverwrite, err := loadCAApiServerFromName(caName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
config.CADirURL = caLinkOverwrite
|
config.CADirURL = caLinkOverwrite
|
||||||
log.Println("[INFO] Using " + caLinkOverwrite + " for CA Directory URL")
|
log.Println("[INFO] Using " + caLinkOverwrite + " for CA Directory URL")
|
||||||
} else {
|
} else {
|
||||||
return false, errors.New("CA " + ca + " is not supported. Please contribute to the source code and add this CA's directory link.")
|
// (caName == "" || caUrl == "") will use default acme
|
||||||
|
config.CADirURL = a.DefaultAcmeServer
|
||||||
|
log.Println("[INFO] Using Default ACME " + a.DefaultAcmeServer + " for CA Directory URL")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,12 +163,31 @@ func (a *ACMEHandler) ObtainCert(domains []string, certificateName string, email
|
|||||||
|
|
||||||
// Each certificate comes back with the cert bytes, the bytes of the client's
|
// Each certificate comes back with the cert bytes, the bytes of the client's
|
||||||
// private key, and a certificate URL.
|
// private key, and a certificate URL.
|
||||||
err = ioutil.WriteFile("./conf/certs/"+certificateName+".crt", certificates.Certificate, 0777)
|
err = os.WriteFile("./conf/certs/"+certificateName+".crt", certificates.Certificate, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile("./conf/certs/"+certificateName+".key", certificates.PrivateKey, 0777)
|
err = os.WriteFile("./conf/certs/"+certificateName+".key", certificates.PrivateKey, 0777)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save certificate's ACME info for renew usage
|
||||||
|
certInfo := &CertificateInfoJSON{
|
||||||
|
AcmeName: caName,
|
||||||
|
AcmeUrl: caUrl,
|
||||||
|
SkipTLS: skipTLS,
|
||||||
|
}
|
||||||
|
|
||||||
|
certInfoBytes, err := json.Marshal(certInfo)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.WriteFile("./conf/certs/"+certificateName+".json", certInfoBytes, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return false, err
|
return false, err
|
||||||
@ -250,14 +298,39 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var caUrl string
|
||||||
|
|
||||||
ca, err := utils.PostPara(r, "ca")
|
ca, err := utils.PostPara(r, "ca")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("CA not set. Using default (Let's Encrypt)")
|
log.Println("[INFO] CA not set. Using default")
|
||||||
|
ca, caUrl = "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if ca == "custom" {
|
||||||
|
caUrl, err = utils.PostPara(r, "caURL")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[INFO] Custom CA set but no URL provide, Using default")
|
||||||
|
ca, caUrl = "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ca == "" {
|
||||||
|
//default. Use Let's Encrypt
|
||||||
ca = "Let's Encrypt"
|
ca = "Let's Encrypt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var skipTLS bool
|
||||||
|
|
||||||
|
if skipTLSString, err := utils.PostPara(r, "skipTLS"); err != nil {
|
||||||
|
skipTLS = false
|
||||||
|
} else if skipTLSString != "true" {
|
||||||
|
skipTLS = false
|
||||||
|
} else {
|
||||||
|
skipTLS = true
|
||||||
|
}
|
||||||
|
|
||||||
domains := strings.Split(domainPara, ",")
|
domains := strings.Split(domainPara, ",")
|
||||||
result, err := a.ObtainCert(domains, filename, email, ca)
|
result, err := a.ObtainCert(domains, filename, email, ca, caUrl, skipTLS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, jsonEscape(err.Error()))
|
utils.SendErrorResponse(w, jsonEscape(err.Error()))
|
||||||
return
|
return
|
||||||
@ -285,4 +358,20 @@ func IsPortInUse(port int) bool {
|
|||||||
}
|
}
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
return false // Port is not in use
|
return false // Port is not in use
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load cert information from json file
|
||||||
|
func loadCertInfoJSON(filename string) (*CertificateInfoJSON, error) {
|
||||||
|
certInfoBytes, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certInfo := &CertificateInfoJSON{}
|
||||||
|
if err = json.Unmarshal(certInfoBytes, certInfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return certInfo, nil
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package acme
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
@ -39,7 +40,6 @@ type AutoRenewer struct {
|
|||||||
type ExpiredCerts struct {
|
type ExpiredCerts struct {
|
||||||
Domains []string
|
Domains []string
|
||||||
Filepath string
|
Filepath string
|
||||||
CA string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an auto renew agent, require config filepath and auto scan & renew interval (seconds)
|
// Create an auto renew agent, require config filepath and auto scan & renew interval (seconds)
|
||||||
@ -279,12 +279,6 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
|
|||||||
}
|
}
|
||||||
if CertExpireSoon(certBytes) || CertIsExpired(certBytes) {
|
if CertExpireSoon(certBytes) || CertIsExpired(certBytes) {
|
||||||
//This cert is expired
|
//This cert is expired
|
||||||
CAName, err := ExtractIssuerName(certBytes)
|
|
||||||
if err != nil {
|
|
||||||
//Maybe self signed. Ignore this
|
|
||||||
log.Println("Unable to extract issuer name for cert " + file.Name())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
DNSName, err := ExtractDomains(certBytes)
|
DNSName, err := ExtractDomains(certBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -295,7 +289,6 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
|
|||||||
|
|
||||||
expiredCertList = append(expiredCertList, &ExpiredCerts{
|
expiredCertList = append(expiredCertList, &ExpiredCerts{
|
||||||
Filepath: filepath.Join(certFolder, file.Name()),
|
Filepath: filepath.Join(certFolder, file.Name()),
|
||||||
CA: CAName,
|
|
||||||
Domains: DNSName,
|
Domains: DNSName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -314,12 +307,6 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
|
|||||||
}
|
}
|
||||||
if CertExpireSoon(certBytes) || CertIsExpired(certBytes) {
|
if CertExpireSoon(certBytes) || CertIsExpired(certBytes) {
|
||||||
//This cert is expired
|
//This cert is expired
|
||||||
CAName, err := ExtractIssuerName(certBytes)
|
|
||||||
if err != nil {
|
|
||||||
//Maybe self signed. Ignore this
|
|
||||||
log.Println("Unable to extract issuer name for cert " + file.Name())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
DNSName, err := ExtractDomains(certBytes)
|
DNSName, err := ExtractDomains(certBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -330,7 +317,6 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
|
|||||||
|
|
||||||
expiredCertList = append(expiredCertList, &ExpiredCerts{
|
expiredCertList = append(expiredCertList, &ExpiredCerts{
|
||||||
Filepath: filepath.Join(certFolder, file.Name()),
|
Filepath: filepath.Join(certFolder, file.Name()),
|
||||||
CA: CAName,
|
|
||||||
Domains: DNSName,
|
Domains: DNSName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -355,7 +341,22 @@ func (a *AutoRenewer) renewExpiredDomains(certs []*ExpiredCerts) ([]string, erro
|
|||||||
log.Println("Renewing " + expiredCert.Filepath + " (Might take a few minutes)")
|
log.Println("Renewing " + expiredCert.Filepath + " (Might take a few minutes)")
|
||||||
fileName := filepath.Base(expiredCert.Filepath)
|
fileName := filepath.Base(expiredCert.Filepath)
|
||||||
certName := fileName[:len(fileName)-len(filepath.Ext(fileName))]
|
certName := fileName[:len(fileName)-len(filepath.Ext(fileName))]
|
||||||
_, err := a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, expiredCert.CA)
|
|
||||||
|
// Load certificate info for ACME detail
|
||||||
|
certInfoFilename := fmt.Sprintf("%s/%s.json", filepath.Dir(expiredCert.Filepath), certName)
|
||||||
|
certInfo, err := loadCertInfoJSON(certInfoFilename)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Renew %s certificate error, can't get the ACME detail for cert: %v, trying org section as ca", certName, err)
|
||||||
|
|
||||||
|
if CAName, extractErr := ExtractIssuerNameFromPEM(expiredCert.Filepath); extractErr != nil {
|
||||||
|
log.Printf("extract issuer name for cert error: %v, using default ca", extractErr)
|
||||||
|
certInfo = &CertificateInfoJSON{}
|
||||||
|
} else {
|
||||||
|
certInfo = &CertificateInfoJSON{AcmeName: CAName}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = a.AcmeHandler.ObtainCert(expiredCert.Domains, certName, a.RenewerConfig.Email, certInfo.AcmeName, certInfo.AcmeUrl, certInfo.SkipTLS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Renew " + fileName + "(" + strings.Join(expiredCert.Domains, ",") + ") failed: " + err.Error())
|
log.Println("Renew " + fileName + "(" + strings.Join(expiredCert.Domains, ",") + ") failed: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CA Defination, load from embeded json when startup
|
// CA Defination, load from embeded json when startup
|
||||||
@ -32,14 +33,24 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
caDef = runtimeCaDef
|
caDef = runtimeCaDef
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the CA ACME server endpoint and error if not found
|
// Get the CA ACME server endpoint and error if not found
|
||||||
func loadCAApiServerFromName(caName string) (string, error) {
|
func loadCAApiServerFromName(caName string) (string, error) {
|
||||||
|
// handle BuyPass cert org section (Buypass AS-983163327)
|
||||||
|
if strings.HasPrefix(caName, "Buypass AS") {
|
||||||
|
caName = "Buypass"
|
||||||
|
}
|
||||||
|
|
||||||
val, ok := caDef.Production[caName]
|
val, ok := caDef.Production[caName]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("This CA is not supported")
|
return "", errors.New("This CA is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsSupportedCA(caName string) bool {
|
||||||
|
_, err := loadCAApiServerFromName(caName)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
@ -53,6 +53,11 @@ func ExtractIssuerName(certBytes []byte) (string, error) {
|
|||||||
return "", fmt.Errorf("failed to parse certificate: %v", err)
|
return "", fmt.Errorf("failed to parse certificate: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if exist incase some acme server didn't have org section
|
||||||
|
if len(cert.Issuer.Organization) == 0 {
|
||||||
|
return "", fmt.Errorf("cert didn't have org section exist")
|
||||||
|
}
|
||||||
|
|
||||||
// Extract the issuer name
|
// Extract the issuer name
|
||||||
issuer := cert.Issuer.Organization[0]
|
issuer := cert.Issuer.Organization[0]
|
||||||
|
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
package dynamicproxy
|
package dynamicproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
@ -21,6 +26,11 @@ import (
|
|||||||
- Vitrual Directory Routing
|
- Vitrual Directory Routing
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed tld.json
|
||||||
|
rawTldMap []byte
|
||||||
|
)
|
||||||
|
|
||||||
func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
/*
|
/*
|
||||||
Special Routing Rules, bypass most of the limitations
|
Special Routing Rules, bypass most of the limitations
|
||||||
@ -108,10 +118,69 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
//Passthrough the request to root
|
//Passthrough the request to root
|
||||||
h.proxyRequest(w, r, h.Parent.Root)
|
h.handleRootRouting(w, r)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//No routing rules found. Route to root.
|
//No routing rules found.
|
||||||
|
h.handleRootRouting(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
handleRootRouting
|
||||||
|
|
||||||
|
This function handle root routing situations where there are no subdomain
|
||||||
|
, vdir or special routing rule matches the requested URI.
|
||||||
|
|
||||||
|
Once entered this routing segment, the root routing options will take over
|
||||||
|
for the routing logic.
|
||||||
|
*/
|
||||||
|
func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) {
|
||||||
|
domainOnly := r.Host
|
||||||
|
if strings.Contains(r.Host, ":") {
|
||||||
|
hostPath := strings.Split(r.Host, ":")
|
||||||
|
domainOnly = hostPath[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.Parent.RootRoutingOptions.EnableRedirectForUnsetRules {
|
||||||
|
//Route to custom domain
|
||||||
|
if h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget == "" {
|
||||||
|
//Not set. Redirect to first level of domain redirectable
|
||||||
|
fld, err := h.getTopLevelRedirectableDomain(domainOnly)
|
||||||
|
if err != nil {
|
||||||
|
//Redirect to proxy root
|
||||||
|
h.proxyRequest(w, r, h.Parent.Root)
|
||||||
|
} else {
|
||||||
|
log.Println("[Router] Redirecting request from " + domainOnly + " to " + fld)
|
||||||
|
h.logRequest(r, false, 307, "root-redirect", domainOnly)
|
||||||
|
http.Redirect(w, r, fld, http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if h.isTopLevelRedirectableDomain(domainOnly) {
|
||||||
|
//This is requesting a top level private domain that should be serving root
|
||||||
|
h.proxyRequest(w, r, h.Parent.Root)
|
||||||
|
} else {
|
||||||
|
//Validate the redirection target URL
|
||||||
|
parsedURL, err := url.Parse(h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget)
|
||||||
|
if err != nil {
|
||||||
|
//Error when parsing target. Send to root
|
||||||
|
h.proxyRequest(w, r, h.Parent.Root)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hostname := parsedURL.Hostname()
|
||||||
|
if domainOnly != hostname {
|
||||||
|
//Redirect to target
|
||||||
|
h.logRequest(r, false, 307, "root-redirect", domainOnly)
|
||||||
|
http.Redirect(w, r, h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget, http.StatusTemporaryRedirect)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
//Loopback request due to bad settings (Shd leave it empty)
|
||||||
|
//Forward it to root proxy
|
||||||
|
h.proxyRequest(w, r, h.Parent.Root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Route to root
|
||||||
h.proxyRequest(w, r, h.Parent.Root)
|
h.proxyRequest(w, r, h.Parent.Root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,9 +193,9 @@ func (h *ProxyHandler) handleAccessRouting(w http.ResponseWriter, r *http.Reques
|
|||||||
if h.Parent.Option.GeodbStore.IsBlacklisted(clientIpAddr) {
|
if h.Parent.Option.GeodbStore.IsBlacklisted(clientIpAddr) {
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
template, err := os.ReadFile("./web/forbidden.html")
|
template, err := os.ReadFile(filepath.Join(h.Parent.Option.WebDirectory, "templates/blacklist.html"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.Write([]byte("403 - Forbidden"))
|
w.Write(page_forbidden)
|
||||||
} else {
|
} else {
|
||||||
w.Write(template)
|
w.Write(template)
|
||||||
}
|
}
|
||||||
@ -138,9 +207,9 @@ func (h *ProxyHandler) handleAccessRouting(w http.ResponseWriter, r *http.Reques
|
|||||||
if !h.Parent.Option.GeodbStore.IsWhitelisted(clientIpAddr) {
|
if !h.Parent.Option.GeodbStore.IsWhitelisted(clientIpAddr) {
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
template, err := os.ReadFile("./web/forbidden.html")
|
template, err := os.ReadFile(filepath.Join(h.Parent.Option.WebDirectory, "templates/whitelist.html"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.Write([]byte("403 - Forbidden"))
|
w.Write(page_forbidden)
|
||||||
} else {
|
} else {
|
||||||
w.Write(template)
|
w.Write(template)
|
||||||
}
|
}
|
||||||
@ -150,3 +219,44 @@ func (h *ProxyHandler) handleAccessRouting(w http.ResponseWriter, r *http.Reques
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return if the given host is already topped (e.g. example.com or example.co.uk) instead of
|
||||||
|
// a host with subdomain (e.g. test.example.com)
|
||||||
|
func (h *ProxyHandler) isTopLevelRedirectableDomain(requestHost string) bool {
|
||||||
|
parts := strings.Split(requestHost, ".")
|
||||||
|
if len(parts) > 2 {
|
||||||
|
//Cases where strange tld is used like .co.uk or .com.hk
|
||||||
|
_, ok := h.Parent.tldMap[strings.Join(parts[1:], ".")]
|
||||||
|
if ok {
|
||||||
|
//Already topped
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//Already topped
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTopLevelRedirectableDomain returns the toppest level of domain
|
||||||
|
// that is redirectable. E.g. a.b.c.example.co.uk will return example.co.uk
|
||||||
|
func (h *ProxyHandler) getTopLevelRedirectableDomain(unsetSubdomainHost string) (string, error) {
|
||||||
|
parts := strings.Split(unsetSubdomainHost, ".")
|
||||||
|
if h.isTopLevelRedirectableDomain(unsetSubdomainHost) {
|
||||||
|
//Already topped
|
||||||
|
return "", errors.New("already at top level domain")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(parts); i++ {
|
||||||
|
possibleTld := parts[i:]
|
||||||
|
_, ok := h.Parent.tldMap[strings.Join(possibleTld, ".")]
|
||||||
|
if ok {
|
||||||
|
//This is tld length
|
||||||
|
tld := strings.Join(parts[i-1:], ".")
|
||||||
|
return "//" + tld, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("unsupported top level domain given")
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package dynamicproxy
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/auth"
|
"imuslab.com/zoraxy/mod/auth"
|
||||||
)
|
)
|
||||||
@ -15,6 +16,16 @@ import (
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error {
|
func (h *ProxyHandler) handleBasicAuthRouting(w http.ResponseWriter, r *http.Request, pe *ProxyEndpoint) error {
|
||||||
|
if len(pe.BasicAuthExceptionRules) > 0 {
|
||||||
|
//Check if the current path matches the exception rules
|
||||||
|
for _, exceptionRule := range pe.BasicAuthExceptionRules {
|
||||||
|
if strings.HasPrefix(r.RequestURI, exceptionRule.PathPrefix) {
|
||||||
|
//This path is excluded from basic auth
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proxyType := "vdir-auth"
|
proxyType := "vdir-auth"
|
||||||
if pe.ProxyType == ProxyType_Subdomain {
|
if pe.ProxyType == ProxyType_Subdomain {
|
||||||
proxyType = "subd-auth"
|
proxyType = "subd-auth"
|
||||||
|
@ -14,10 +14,6 @@ import (
|
|||||||
|
|
||||||
var onExitFlushLoop func()
|
var onExitFlushLoop func()
|
||||||
|
|
||||||
const (
|
|
||||||
defaultTimeout = time.Minute * 5
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReverseProxy is an HTTP Handler that takes an incoming request and
|
// ReverseProxy is an HTTP Handler that takes an incoming request and
|
||||||
// sends it to another server, proxying the response back to the
|
// sends it to another server, proxying the response back to the
|
||||||
// client, support http, also support https tunnel using http.hijacker
|
// client, support http, also support https tunnel using http.hijacker
|
||||||
|
@ -21,6 +21,17 @@ func replaceLocationHost(urlString string, rrr *ResponseRewriteRuleSet, useTLS b
|
|||||||
u.Scheme = "http"
|
u.Scheme = "http"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Issue #39: Check if it is location target match the proxying domain
|
||||||
|
//E.g. Proxy config: blog.example.com -> example.com/blog
|
||||||
|
//Check if it is actually redirecting to example.com instead of a new domain
|
||||||
|
//like news.example.com.
|
||||||
|
// The later check bypass apache screw up method of redirection header
|
||||||
|
// e.g. https://imuslab.com -> http://imuslab.com:443
|
||||||
|
if rrr.ProxyDomain != u.Host && !strings.Contains(u.Host, rrr.OriginalHost+":") {
|
||||||
|
//New location domain not matching proxy target domain.
|
||||||
|
//Do not modify location header
|
||||||
|
return urlString, nil
|
||||||
|
}
|
||||||
u.Host = rrr.OriginalHost
|
u.Host = rrr.OriginalHost
|
||||||
|
|
||||||
if strings.Contains(rrr.ProxyDomain, "/") {
|
if strings.Contains(rrr.ProxyDomain, "/") {
|
||||||
|
@ -3,6 +3,7 @@ package dynamicproxy
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -29,12 +30,19 @@ func NewDynamicProxy(option RouterOption) (*Router, error) {
|
|||||||
Running: false,
|
Running: false,
|
||||||
server: nil,
|
server: nil,
|
||||||
routingRules: []*RoutingRule{},
|
routingRules: []*RoutingRule{},
|
||||||
|
tldMap: map[string]int{},
|
||||||
}
|
}
|
||||||
|
|
||||||
thisRouter.mux = &ProxyHandler{
|
thisRouter.mux = &ProxyHandler{
|
||||||
Parent: &thisRouter,
|
Parent: &thisRouter,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Prase the tld map for tld redirection in main router
|
||||||
|
//See Server.go declarations
|
||||||
|
if len(rawTldMap) > 0 {
|
||||||
|
json.Unmarshal(rawTldMap, &thisRouter.tldMap)
|
||||||
|
}
|
||||||
|
|
||||||
return &thisRouter, nil
|
return &thisRouter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +60,12 @@ func (router *Router) UpdateTLSVersion(requireLatest bool) {
|
|||||||
router.Restart()
|
router.Restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update port 80 listener state
|
||||||
|
func (router *Router) UpdatePort80ListenerState(useRedirect bool) {
|
||||||
|
router.Option.ListenOnPort80 = useRedirect
|
||||||
|
router.Restart()
|
||||||
|
}
|
||||||
|
|
||||||
// Update https redirect, which will require updates
|
// Update https redirect, which will require updates
|
||||||
func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) {
|
func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) {
|
||||||
router.Option.ForceHttpsRedirect = useRedirect
|
router.Option.ForceHttpsRedirect = useRedirect
|
||||||
@ -65,10 +79,18 @@ func (router *Router) StartProxyService() error {
|
|||||||
return errors.New("Reverse proxy server already running")
|
return errors.New("Reverse proxy server already running")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check if root route is set
|
||||||
if router.Root == nil {
|
if router.Root == nil {
|
||||||
return errors.New("Reverse proxy router root not set")
|
return errors.New("Reverse proxy router root not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Load root options from file
|
||||||
|
loadedRootOption, err := loadRootRoutingOptionsFromFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
router.RootRoutingOptions = loadedRootOption
|
||||||
|
|
||||||
minVersion := tls.VersionTLS10
|
minVersion := tls.VersionTLS10
|
||||||
if router.Option.ForceTLSLatest {
|
if router.Option.ForceTLSLatest {
|
||||||
minVersion = tls.VersionTLS12
|
minVersion = tls.VersionTLS12
|
||||||
@ -79,27 +101,73 @@ func (router *Router) StartProxyService() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if router.Option.UseTls {
|
if router.Option.UseTls {
|
||||||
//Serve with TLS mode
|
/*
|
||||||
ln, err := tls.Listen("tcp", ":"+strconv.Itoa(router.Option.Port), config)
|
//Serve with TLS mode
|
||||||
if err != nil {
|
ln, err := tls.Listen("tcp", ":"+strconv.Itoa(router.Option.Port), config)
|
||||||
log.Println(err)
|
if err != nil {
|
||||||
router.Running = false
|
log.Println(err)
|
||||||
return err
|
router.Running = false
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
router.tlsListener = ln
|
||||||
|
*/
|
||||||
|
router.server = &http.Server{
|
||||||
|
Addr: ":" + strconv.Itoa(router.Option.Port),
|
||||||
|
Handler: router.mux,
|
||||||
|
TLSConfig: config,
|
||||||
}
|
}
|
||||||
router.tlsListener = ln
|
|
||||||
router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux}
|
|
||||||
router.Running = true
|
router.Running = true
|
||||||
|
|
||||||
if router.Option.Port != 80 && router.Option.ForceHttpsRedirect {
|
if router.Option.Port != 80 && router.Option.ListenOnPort80 {
|
||||||
//Add a 80 to 443 redirector
|
//Add a 80 to 443 redirector
|
||||||
httpServer := &http.Server{
|
httpServer := &http.Server{
|
||||||
Addr: ":80",
|
Addr: ":80",
|
||||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
protocol := "https://"
|
//Check if the domain requesting allow non TLS mode
|
||||||
if router.Option.Port == 443 {
|
domainOnly := r.Host
|
||||||
http.Redirect(w, r, protocol+r.Host+r.RequestURI, http.StatusTemporaryRedirect)
|
if strings.Contains(r.Host, ":") {
|
||||||
|
hostPath := strings.Split(r.Host, ":")
|
||||||
|
domainOnly = hostPath[0]
|
||||||
|
}
|
||||||
|
sep := router.getSubdomainProxyEndpointFromHostname(domainOnly)
|
||||||
|
if sep != nil && sep.BypassGlobalTLS {
|
||||||
|
//Allow routing via non-TLS handler
|
||||||
|
originalHostHeader := r.Host
|
||||||
|
if r.URL != nil {
|
||||||
|
r.Host = r.URL.Host
|
||||||
|
} else {
|
||||||
|
//Fallback when the upstream proxy screw something up in the header
|
||||||
|
r.URL, _ = url.Parse(originalHostHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
sep.Proxy.ServeHTTP(w, r, &dpcore.ResponseRewriteRuleSet{
|
||||||
|
ProxyDomain: sep.Domain,
|
||||||
|
OriginalHost: originalHostHeader,
|
||||||
|
UseTLS: sep.RequireTLS,
|
||||||
|
PathPrefix: "",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if router.Option.ForceHttpsRedirect {
|
||||||
|
//Redirect to https is enabled
|
||||||
|
protocol := "https://"
|
||||||
|
if router.Option.Port == 443 {
|
||||||
|
http.Redirect(w, r, protocol+r.Host+r.RequestURI, http.StatusTemporaryRedirect)
|
||||||
|
} else {
|
||||||
|
http.Redirect(w, r, protocol+r.Host+":"+strconv.Itoa(router.Option.Port)+r.RequestURI, http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
http.Redirect(w, r, protocol+r.Host+":"+strconv.Itoa(router.Option.Port)+r.RequestURI, http.StatusTemporaryRedirect)
|
//Do not do redirection
|
||||||
|
if sep != nil {
|
||||||
|
//Sub-domain exists but not allow non-TLS access
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte("400 - Bad Request"))
|
||||||
|
} else {
|
||||||
|
//No defined sub-domain
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}),
|
}),
|
||||||
@ -127,7 +195,7 @@ func (router *Router) StartProxyService() error {
|
|||||||
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
//Unable to startup port 80 listener. Handle shutdown process gracefully
|
//Unable to startup port 80 listener. Handle shutdown process gracefully
|
||||||
stopChan <- true
|
stopChan <- true
|
||||||
log.Fatalf("Could not start server: %v\n", err)
|
log.Fatalf("Could not start redirection server: %v\n", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
router.tlsRedirectStop = stopChan
|
router.tlsRedirectStop = stopChan
|
||||||
@ -136,8 +204,8 @@ func (router *Router) StartProxyService() error {
|
|||||||
//Start the TLS server
|
//Start the TLS server
|
||||||
log.Println("Reverse proxy service started in the background (TLS mode)")
|
log.Println("Reverse proxy service started in the background (TLS mode)")
|
||||||
go func() {
|
go func() {
|
||||||
if err := router.server.Serve(ln); err != nil && err != http.ErrServerClosed {
|
if err := router.server.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
|
||||||
log.Fatalf("Could not start server: %v\n", err)
|
log.Fatalf("Could not start proxy server: %v\n", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
@ -246,14 +314,15 @@ func (router *Router) AddVirtualDirectoryProxyService(options *VdirOptions) erro
|
|||||||
proxy := dpcore.NewDynamicProxyCore(path, options.RootName, options.SkipCertValidations)
|
proxy := dpcore.NewDynamicProxyCore(path, options.RootName, options.SkipCertValidations)
|
||||||
|
|
||||||
endpointObject := ProxyEndpoint{
|
endpointObject := ProxyEndpoint{
|
||||||
ProxyType: ProxyType_Vdir,
|
ProxyType: ProxyType_Vdir,
|
||||||
RootOrMatchingDomain: options.RootName,
|
RootOrMatchingDomain: options.RootName,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
RequireTLS: options.RequireTLS,
|
RequireTLS: options.RequireTLS,
|
||||||
SkipCertValidations: options.SkipCertValidations,
|
SkipCertValidations: options.SkipCertValidations,
|
||||||
RequireBasicAuth: options.RequireBasicAuth,
|
RequireBasicAuth: options.RequireBasicAuth,
|
||||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||||
Proxy: proxy,
|
BasicAuthExceptionRules: options.BasicAuthExceptionRules,
|
||||||
|
Proxy: proxy,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.ProxyEndpoints.Store(options.RootName, &endpointObject)
|
router.ProxyEndpoints.Store(options.RootName, &endpointObject)
|
||||||
@ -271,46 +340,24 @@ func (router *Router) LoadProxy(ptype string, key string) (*ProxyEndpoint, error
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("target proxy not found")
|
return nil, errors.New("target proxy not found")
|
||||||
}
|
}
|
||||||
return proxy.(*ProxyEndpoint), nil
|
|
||||||
|
targetProxy := proxy.(*ProxyEndpoint)
|
||||||
|
targetProxy.parent = router
|
||||||
|
return targetProxy, nil
|
||||||
} else if ptype == "subd" {
|
} else if ptype == "subd" {
|
||||||
proxy, ok := router.SubdomainEndpoint.Load(key)
|
proxy, ok := router.SubdomainEndpoint.Load(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("target proxy not found")
|
return nil, errors.New("target proxy not found")
|
||||||
}
|
}
|
||||||
return proxy.(*ProxyEndpoint), nil
|
|
||||||
|
targetProxy := proxy.(*ProxyEndpoint)
|
||||||
|
targetProxy.parent = router
|
||||||
|
return targetProxy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("unsupported ptype")
|
return nil, errors.New("unsupported ptype")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Save routing from RP
|
|
||||||
*/
|
|
||||||
func (router *Router) SaveProxy(ptype string, key string, newConfig *ProxyEndpoint) {
|
|
||||||
if ptype == "vdir" {
|
|
||||||
router.ProxyEndpoints.Store(key, newConfig)
|
|
||||||
|
|
||||||
} else if ptype == "subd" {
|
|
||||||
router.SubdomainEndpoint.Store(key, newConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Remove routing from RP
|
|
||||||
*/
|
|
||||||
func (router *Router) RemoveProxy(ptype string, key string) error {
|
|
||||||
//fmt.Println(ptype, key)
|
|
||||||
if ptype == "vdir" {
|
|
||||||
router.ProxyEndpoints.Delete(key)
|
|
||||||
return nil
|
|
||||||
} else if ptype == "subd" {
|
|
||||||
router.SubdomainEndpoint.Delete(key)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errors.New("invalid ptype")
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Add an default router for the proxy server
|
Add an default router for the proxy server
|
||||||
*/
|
*/
|
||||||
@ -335,14 +382,15 @@ func (router *Router) SetRootProxy(options *RootOptions) error {
|
|||||||
proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations)
|
proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations)
|
||||||
|
|
||||||
rootEndpoint := ProxyEndpoint{
|
rootEndpoint := ProxyEndpoint{
|
||||||
ProxyType: ProxyType_Vdir,
|
ProxyType: ProxyType_Vdir,
|
||||||
RootOrMatchingDomain: "/",
|
RootOrMatchingDomain: "/",
|
||||||
Domain: proxyLocation,
|
Domain: proxyLocation,
|
||||||
RequireTLS: options.RequireTLS,
|
RequireTLS: options.RequireTLS,
|
||||||
SkipCertValidations: options.SkipCertValidations,
|
SkipCertValidations: options.SkipCertValidations,
|
||||||
RequireBasicAuth: options.RequireBasicAuth,
|
RequireBasicAuth: options.RequireBasicAuth,
|
||||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||||
Proxy: proxy,
|
BasicAuthExceptionRules: options.BasicAuthExceptionRules,
|
||||||
|
Proxy: proxy,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.Root = &rootEndpoint
|
router.Root = &rootEndpoint
|
||||||
|
68
src/mod/dynamicproxy/proxyEndpoint.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package dynamicproxy
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
/*
|
||||||
|
ProxyEndpoint.go
|
||||||
|
author: tobychui
|
||||||
|
|
||||||
|
This script handle the proxy endpoint object actions
|
||||||
|
so proxyEndpoint can be handled like a proper oop object
|
||||||
|
|
||||||
|
Most of the functions are implemented in dynamicproxy.go
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Get the string version of proxy type
|
||||||
|
func (ep *ProxyEndpoint) GetProxyTypeString() string {
|
||||||
|
if ep.ProxyType == ProxyType_Subdomain {
|
||||||
|
return "subd"
|
||||||
|
} else if ep.ProxyType == ProxyType_Vdir {
|
||||||
|
return "vdir"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update change in the current running proxy endpoint config
|
||||||
|
func (ep *ProxyEndpoint) UpdateToRuntime() {
|
||||||
|
if ep.IsVdir() {
|
||||||
|
ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
|
||||||
|
|
||||||
|
} else if ep.IsSubDomain() {
|
||||||
|
ep.parent.SubdomainEndpoint.Store(ep.RootOrMatchingDomain, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Return true if the endpoint type is virtual directory
|
||||||
|
func (ep *ProxyEndpoint) IsVdir() bool {
|
||||||
|
return ep.ProxyType == ProxyType_Vdir
|
||||||
|
}
|
||||||
|
|
||||||
|
//Return true if the endpoint type is subdomain
|
||||||
|
func (ep *ProxyEndpoint) IsSubDomain() bool {
|
||||||
|
return ep.ProxyType == ProxyType_Subdomain
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove this proxy endpoint from running proxy endpoint list
|
||||||
|
func (ep *ProxyEndpoint) Remove() error {
|
||||||
|
//fmt.Println(ptype, key)
|
||||||
|
if ep.IsVdir() {
|
||||||
|
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
|
||||||
|
return nil
|
||||||
|
} else if ep.IsSubDomain() {
|
||||||
|
ep.parent.SubdomainEndpoint.Delete(ep.RootOrMatchingDomain)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("invalid or unsupported type")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//ProxyEndpoint remove provide global access by key
|
||||||
|
func (router *Router) RemoveProxyEndpointByRootname(proxyType string, rootnameOrMatchingDomain string) error {
|
||||||
|
targetEpt, err := router.LoadProxy(proxyType, rootnameOrMatchingDomain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetEpt.Remove()
|
||||||
|
}
|
@ -183,6 +183,5 @@ func (h *ProxyHandler) logRequest(r *http.Request, succ bool, statusCode int, fo
|
|||||||
}
|
}
|
||||||
h.Parent.Option.StatisticCollector.RecordRequest(requestInfo)
|
h.Parent.Option.StatisticCollector.RecordRequest(requestInfo)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
src/mod/dynamicproxy/rootRoute.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package dynamicproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
rootRoute.go
|
||||||
|
|
||||||
|
This script handle special case in routing where the root proxy
|
||||||
|
entity is involved. This also include its setting object
|
||||||
|
RootRoutingOptions
|
||||||
|
*/
|
||||||
|
|
||||||
|
var rootConfigFilepath string = "conf/root_config.json"
|
||||||
|
|
||||||
|
func loadRootRoutingOptionsFromFile() (*RootRoutingOptions, error) {
|
||||||
|
if !utils.FileExists(rootConfigFilepath) {
|
||||||
|
//Not found. Create a root option
|
||||||
|
js, _ := json.MarshalIndent(RootRoutingOptions{}, "", " ")
|
||||||
|
err := os.WriteFile(rootConfigFilepath, js, 0775)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Unable to write root config to file: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRootOption := RootRoutingOptions{}
|
||||||
|
rootOptionsBytes, err := os.ReadFile(rootConfigFilepath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[Error] Unable to read root config file at " + rootConfigFilepath + ": " + err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(rootOptionsBytes, &newRootOption)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[Error] Unable to parse root config file: " + err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &newRootOption, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the new config to file. Note that this will not overwrite the runtime one
|
||||||
|
func (opt *RootRoutingOptions) SaveToFile() error {
|
||||||
|
js, _ := json.MarshalIndent(opt, "", " ")
|
||||||
|
err := os.WriteFile(rootConfigFilepath, js, 0775)
|
||||||
|
return err
|
||||||
|
}
|
@ -34,13 +34,15 @@ func (router *Router) AddSubdomainRoutingService(options *SubdOptions) error {
|
|||||||
proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations)
|
proxy := dpcore.NewDynamicProxyCore(path, "", options.SkipCertValidations)
|
||||||
|
|
||||||
router.SubdomainEndpoint.Store(options.MatchingDomain, &ProxyEndpoint{
|
router.SubdomainEndpoint.Store(options.MatchingDomain, &ProxyEndpoint{
|
||||||
RootOrMatchingDomain: options.MatchingDomain,
|
RootOrMatchingDomain: options.MatchingDomain,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
RequireTLS: options.RequireTLS,
|
RequireTLS: options.RequireTLS,
|
||||||
Proxy: proxy,
|
Proxy: proxy,
|
||||||
SkipCertValidations: options.SkipCertValidations,
|
BypassGlobalTLS: options.BypassGlobalTLS,
|
||||||
RequireBasicAuth: options.RequireBasicAuth,
|
SkipCertValidations: options.SkipCertValidations,
|
||||||
BasicAuthCredentials: options.BasicAuthCredentials,
|
RequireBasicAuth: options.RequireBasicAuth,
|
||||||
|
BasicAuthCredentials: options.BasicAuthCredentials,
|
||||||
|
BasicAuthExceptionRules: options.BasicAuthExceptionRules,
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Println("Adding Subdomain Rule: ", options.MatchingDomain+" to "+domain)
|
log.Println("Adding Subdomain Rule: ", options.MatchingDomain+" to "+domain)
|
||||||
|
55
src/mod/dynamicproxy/templates/forbidden.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Zoraxy Forbidden Template -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.css">
|
||||||
|
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
||||||
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.js"></script>
|
||||||
|
<title>Forbidden</title>
|
||||||
|
<style>
|
||||||
|
#msg{
|
||||||
|
position: absolute;
|
||||||
|
top: calc(50% - 150px);
|
||||||
|
left: calc(50% - 250px);
|
||||||
|
width: 500px;
|
||||||
|
height: 300px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer{
|
||||||
|
position: fixed;
|
||||||
|
padding: 2em;
|
||||||
|
padding-left: 5em;
|
||||||
|
padding-right: 5em;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
small{
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="msg">
|
||||||
|
<h1 style="font-size: 6em; margin-bottom: 0px;"><i class="red ban icon"></i></h1>
|
||||||
|
<div>
|
||||||
|
<h3 style="margin-top: 1em;">403 - Forbidden</h3>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<p>You do not have permission to view this directory or page. <br>
|
||||||
|
This might cause by the region limit setting of this site.</p>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div style="text-align: left;">
|
||||||
|
<small>Request time: <span id="reqtime"></span></small><br>
|
||||||
|
<small id="reqURLDisplay">Request URI: <span id="requrl"></span></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$("#reqtime").text(new Date().toLocaleString(undefined, {year: 'numeric', month: '2-digit', day: '2-digit', weekday:"long", hour: '2-digit', hour12: false, minute:'2-digit', second:'2-digit'}));
|
||||||
|
$("#requrl").text(window.location.href);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
9106
src/mod/dynamicproxy/tld.json
Normal file
@ -1,6 +1,7 @@
|
|||||||
package dynamicproxy
|
package dynamicproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
@ -26,25 +27,29 @@ type RouterOption struct {
|
|||||||
Port int //Incoming port
|
Port int //Incoming port
|
||||||
UseTls bool //Use TLS to serve incoming requsts
|
UseTls bool //Use TLS to serve incoming requsts
|
||||||
ForceTLSLatest bool //Force TLS1.2 or above
|
ForceTLSLatest bool //Force TLS1.2 or above
|
||||||
|
ListenOnPort80 bool //Enable port 80 http listener
|
||||||
ForceHttpsRedirect bool //Force redirection of http to https endpoint
|
ForceHttpsRedirect bool //Force redirection of http to https endpoint
|
||||||
TlsManager *tlscert.Manager
|
TlsManager *tlscert.Manager
|
||||||
RedirectRuleTable *redirection.RuleTable
|
RedirectRuleTable *redirection.RuleTable
|
||||||
GeodbStore *geodb.Store //GeoIP blacklist and whitelist
|
GeodbStore *geodb.Store //GeoIP blacklist and whitelist
|
||||||
StatisticCollector *statistic.Collector
|
StatisticCollector *statistic.Collector
|
||||||
|
WebDirectory string //The static web server directory containing the templates folder
|
||||||
}
|
}
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
Option *RouterOption
|
Option *RouterOption
|
||||||
ProxyEndpoints *sync.Map
|
ProxyEndpoints *sync.Map
|
||||||
SubdomainEndpoint *sync.Map
|
SubdomainEndpoint *sync.Map
|
||||||
Running bool
|
Running bool
|
||||||
Root *ProxyEndpoint
|
Root *ProxyEndpoint
|
||||||
mux http.Handler
|
RootRoutingOptions *RootRoutingOptions
|
||||||
server *http.Server
|
mux http.Handler
|
||||||
tlsListener net.Listener
|
server *http.Server
|
||||||
routingRules []*RoutingRule
|
tlsListener net.Listener
|
||||||
|
routingRules []*RoutingRule
|
||||||
|
|
||||||
tlsRedirectStop chan bool
|
tlsRedirectStop chan bool //Stop channel for tls redirection server
|
||||||
|
tldMap map[string]int //Top level domain map, see tld.json
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth credential for basic auth on certain endpoints
|
// Auth credential for basic auth on certain endpoints
|
||||||
@ -59,56 +64,73 @@ type BasicAuthUnhashedCredentials struct {
|
|||||||
Password string
|
Password string
|
||||||
}
|
}
|
||||||
|
|
||||||
// A proxy endpoint record
|
// Paths to exclude in basic auth enabled proxy handler
|
||||||
type ProxyEndpoint struct {
|
type BasicAuthExceptionRule struct {
|
||||||
ProxyType int //The type of this proxy, see const def
|
PathPrefix string
|
||||||
RootOrMatchingDomain string //Root for vdir or Matching domain for subd
|
|
||||||
Domain string //Domain or IP to proxy to
|
|
||||||
RequireTLS bool //Target domain require TLS
|
|
||||||
SkipCertValidations bool //Set to true to accept self signed certs
|
|
||||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
|
||||||
BasicAuthCredentials []*BasicAuthCredentials `json:"-"`
|
|
||||||
Proxy *dpcore.ReverseProxy `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A proxy endpoint record
|
||||||
|
type ProxyEndpoint struct {
|
||||||
|
ProxyType int //The type of this proxy, see const def
|
||||||
|
RootOrMatchingDomain string //Root for vdir or Matching domain for subd, also act as key
|
||||||
|
Domain string //Domain or IP to proxy to
|
||||||
|
RequireTLS bool //Target domain require TLS
|
||||||
|
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
|
||||||
|
SkipCertValidations bool //Set to true to accept self signed certs
|
||||||
|
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||||
|
BasicAuthCredentials []*BasicAuthCredentials `json:"-"` //Basic auth credentials
|
||||||
|
BasicAuthExceptionRules []*BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
|
||||||
|
Proxy *dpcore.ReverseProxy `json:"-"`
|
||||||
|
|
||||||
|
parent *Router
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root options are those that are required for reverse proxy handler to work
|
||||||
type RootOptions struct {
|
type RootOptions struct {
|
||||||
ProxyLocation string
|
ProxyLocation string //Proxy Root target, all unset traffic will be forward to here
|
||||||
RequireTLS bool
|
RequireTLS bool //Proxy root target require TLS connection (not recommended)
|
||||||
SkipCertValidations bool
|
BypassGlobalTLS bool //Bypass global TLS setting and make root http only (not recommended)
|
||||||
RequireBasicAuth bool
|
SkipCertValidations bool //Skip cert validation, suitable for self-signed certs, CURRENTLY NOT USED
|
||||||
BasicAuthCredentials []*BasicAuthCredentials
|
|
||||||
|
//Basic Auth Related
|
||||||
|
RequireBasicAuth bool //Require basic auth, CURRENTLY NOT USED
|
||||||
|
BasicAuthCredentials []*BasicAuthCredentials
|
||||||
|
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional options are here for letting router knows how to route exception cases for root
|
||||||
|
type RootRoutingOptions struct {
|
||||||
|
//Root only configs
|
||||||
|
EnableRedirectForUnsetRules bool //Force unset rules to redirect to custom domain
|
||||||
|
UnsetRuleRedirectTarget string //Custom domain to redirect to for unset rules
|
||||||
}
|
}
|
||||||
|
|
||||||
type VdirOptions struct {
|
type VdirOptions struct {
|
||||||
RootName string
|
RootName string
|
||||||
Domain string
|
Domain string
|
||||||
RequireTLS bool
|
RequireTLS bool
|
||||||
SkipCertValidations bool
|
BypassGlobalTLS bool
|
||||||
RequireBasicAuth bool
|
SkipCertValidations bool
|
||||||
BasicAuthCredentials []*BasicAuthCredentials
|
RequireBasicAuth bool
|
||||||
|
BasicAuthCredentials []*BasicAuthCredentials
|
||||||
|
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubdOptions struct {
|
type SubdOptions struct {
|
||||||
MatchingDomain string
|
MatchingDomain string
|
||||||
Domain string
|
Domain string
|
||||||
RequireTLS bool
|
RequireTLS bool
|
||||||
SkipCertValidations bool
|
BypassGlobalTLS bool
|
||||||
RequireBasicAuth bool
|
SkipCertValidations bool
|
||||||
BasicAuthCredentials []*BasicAuthCredentials
|
RequireBasicAuth bool
|
||||||
|
BasicAuthCredentials []*BasicAuthCredentials
|
||||||
|
BasicAuthExceptionRules []*BasicAuthExceptionRule
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
type ProxyEndpoint struct {
|
Web Templates
|
||||||
Root string
|
|
||||||
Domain string
|
|
||||||
RequireTLS bool
|
|
||||||
Proxy *reverseproxy.ReverseProxy `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubdomainEndpoint struct {
|
|
||||||
MatchingDomain string
|
|
||||||
Domain string
|
|
||||||
RequireTLS bool
|
|
||||||
Proxy *reverseproxy.ReverseProxy `json:"-"`
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
var (
|
||||||
|
//go:embed templates/forbidden.html
|
||||||
|
page_forbidden []byte
|
||||||
|
)
|
||||||
|
@ -20,13 +20,16 @@ type Store struct {
|
|||||||
WhitelistEnabled bool
|
WhitelistEnabled bool
|
||||||
geodb [][]string //Parsed geodb list
|
geodb [][]string //Parsed geodb list
|
||||||
geodbIpv6 [][]string //Parsed geodb list for ipv6
|
geodbIpv6 [][]string //Parsed geodb list for ipv6
|
||||||
|
geotrie *trie
|
||||||
geotrie *trie
|
geotrieIpv6 *trie
|
||||||
geotrieIpv6 *trie
|
|
||||||
|
|
||||||
//geoipCache sync.Map
|
//geoipCache sync.Map
|
||||||
|
sysdb *database.Database
|
||||||
|
option *StoreOptions
|
||||||
|
}
|
||||||
|
|
||||||
sysdb *database.Database
|
type StoreOptions struct {
|
||||||
|
AllowSlowIpv4LookUp bool
|
||||||
|
AllowSloeIpv6Lookup bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CountryInfo struct {
|
type CountryInfo struct {
|
||||||
@ -34,7 +37,7 @@ type CountryInfo struct {
|
|||||||
ContinetCode string
|
ContinetCode string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGeoDb(sysdb *database.Database) (*Store, error) {
|
func NewGeoDb(sysdb *database.Database, option *StoreOptions) (*Store, error) {
|
||||||
parsedGeoData, err := parseCSV(geoipv4)
|
parsedGeoData, err := parseCSV(geoipv4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -79,14 +82,25 @@ func NewGeoDb(sysdb *database.Database) (*Store, error) {
|
|||||||
log.Println("Database pointer set to nil: Entering debug mode")
|
log.Println("Database pointer set to nil: Entering debug mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ipv4Trie *trie
|
||||||
|
if !option.AllowSlowIpv4LookUp {
|
||||||
|
ipv4Trie = constrctTrieTree(parsedGeoData)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipv6Trie *trie
|
||||||
|
if !option.AllowSloeIpv6Lookup {
|
||||||
|
ipv6Trie = constrctTrieTree(parsedGeoDataIpv6)
|
||||||
|
}
|
||||||
|
|
||||||
return &Store{
|
return &Store{
|
||||||
BlacklistEnabled: blacklistEnabled,
|
BlacklistEnabled: blacklistEnabled,
|
||||||
WhitelistEnabled: whitelistEnabled,
|
WhitelistEnabled: whitelistEnabled,
|
||||||
geodb: parsedGeoData,
|
geodb: parsedGeoData,
|
||||||
geotrie: constrctTrieTree(parsedGeoData),
|
geotrie: ipv4Trie,
|
||||||
geodbIpv6: parsedGeoDataIpv6,
|
geodbIpv6: parsedGeoDataIpv6,
|
||||||
geotrieIpv6: constrctTrieTree(parsedGeoDataIpv6),
|
geotrieIpv6: ipv6Trie,
|
||||||
sysdb: sysdb,
|
sysdb: sysdb,
|
||||||
|
option: option,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +120,7 @@ func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error)
|
|||||||
CountryIsoCode: cc,
|
CountryIsoCode: cc,
|
||||||
ContinetCode: "",
|
ContinetCode: "",
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) Close() {
|
func (s *Store) Close() {
|
||||||
|
@ -41,7 +41,10 @@ func TestTrieConstruct(t *testing.T) {
|
|||||||
|
|
||||||
func TestResolveCountryCodeFromIP(t *testing.T) {
|
func TestResolveCountryCodeFromIP(t *testing.T) {
|
||||||
// Create a new store
|
// Create a new store
|
||||||
store, err := geodb.NewGeoDb(nil)
|
store, err := geodb.NewGeoDb(nil, &geodb.StoreOptions{
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("error creating store: %v", err)
|
t.Errorf("error creating store: %v", err)
|
||||||
return
|
return
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,9 +25,17 @@ func (s *Store) search(ip string) string {
|
|||||||
//Search in geotrie tree
|
//Search in geotrie tree
|
||||||
cc := ""
|
cc := ""
|
||||||
if IsIPv6(ip) {
|
if IsIPv6(ip) {
|
||||||
cc = s.geotrieIpv6.search(ip)
|
if s.geotrieIpv6 == nil {
|
||||||
|
cc = s.slowSearchIpv6(ip)
|
||||||
|
} else {
|
||||||
|
cc = s.geotrieIpv6.search(ip)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cc = s.geotrie.search(ip)
|
if s.geotrie == nil {
|
||||||
|
cc = s.slowSearchIpv4(ip)
|
||||||
|
} else {
|
||||||
|
cc = s.geotrie.search(ip)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -69,27 +76,3 @@ func parseCSV(content []byte) ([][]string, error) {
|
|||||||
}
|
}
|
||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a ip string is within the range of two others
|
|
||||||
func isIPInRange(ip, start, end string) bool {
|
|
||||||
ipAddr := net.ParseIP(ip)
|
|
||||||
if ipAddr == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
startAddr := net.ParseIP(start)
|
|
||||||
if startAddr == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
endAddr := net.ParseIP(end)
|
|
||||||
if endAddr == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if ipAddr.To4() == nil || startAddr.To4() == nil || endAddr.To4() == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes.Compare(ipAddr.To4(), startAddr.To4()) >= 0 && bytes.Compare(ipAddr.To4(), endAddr.To4()) <= 0
|
|
||||||
}
|
|
||||||
|
81
src/mod/geodb/slowSearch.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package geodb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
slowSearch.go
|
||||||
|
|
||||||
|
This script implement the slow search method for ip to country code
|
||||||
|
lookup. If you have the memory allocation for near O(1) lookup,
|
||||||
|
you should not be using slow search mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
func ipv4ToUInt32(ip net.IP) uint32 {
|
||||||
|
ip = ip.To4()
|
||||||
|
return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIPv4InRange(startIP, endIP, testIP string) (bool, error) {
|
||||||
|
start := net.ParseIP(startIP)
|
||||||
|
end := net.ParseIP(endIP)
|
||||||
|
test := net.ParseIP(testIP)
|
||||||
|
|
||||||
|
if start == nil || end == nil || test == nil {
|
||||||
|
return false, errors.New("invalid IP address format")
|
||||||
|
}
|
||||||
|
|
||||||
|
startUint := ipv4ToUInt32(start)
|
||||||
|
endUint := ipv4ToUInt32(end)
|
||||||
|
testUint := ipv4ToUInt32(test)
|
||||||
|
|
||||||
|
return testUint >= startUint && testUint <= endUint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIPv6InRange(startIP, endIP, testIP string) (bool, error) {
|
||||||
|
start := net.ParseIP(startIP)
|
||||||
|
end := net.ParseIP(endIP)
|
||||||
|
test := net.ParseIP(testIP)
|
||||||
|
|
||||||
|
if start == nil || end == nil || test == nil {
|
||||||
|
return false, errors.New("invalid IP address format")
|
||||||
|
}
|
||||||
|
|
||||||
|
startInt := new(big.Int).SetBytes(start.To16())
|
||||||
|
endInt := new(big.Int).SetBytes(end.To16())
|
||||||
|
testInt := new(big.Int).SetBytes(test.To16())
|
||||||
|
|
||||||
|
return testInt.Cmp(startInt) >= 0 && testInt.Cmp(endInt) <= 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow country code lookup for
|
||||||
|
func (s *Store) slowSearchIpv4(ipAddr string) string {
|
||||||
|
for _, ipRange := range s.geodb {
|
||||||
|
startIp := ipRange[0]
|
||||||
|
endIp := ipRange[1]
|
||||||
|
cc := ipRange[2]
|
||||||
|
|
||||||
|
inRange, _ := isIPv4InRange(startIp, endIp, ipAddr)
|
||||||
|
if inRange {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) slowSearchIpv6(ipAddr string) string {
|
||||||
|
for _, ipRange := range s.geodbIpv6 {
|
||||||
|
startIp := ipRange[0]
|
||||||
|
endIp := ipRange[1]
|
||||||
|
cc := ipRange[2]
|
||||||
|
|
||||||
|
inRange, _ := isIPv6InRange(startIp, endIp, ipAddr)
|
||||||
|
if inRange {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
@ -1,15 +1,12 @@
|
|||||||
package geodb
|
package geodb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type trie_Node struct {
|
type trie_Node struct {
|
||||||
childrens [2]*trie_Node
|
childrens [2]*trie_Node
|
||||||
ends bool
|
|
||||||
cc string
|
cc string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +15,7 @@ type trie struct {
|
|||||||
root *trie_Node
|
root *trie_Node
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipToBitString(ip string) string {
|
func ipToBytes(ip string) []byte {
|
||||||
// Parse the IP address string into a net.IP object
|
// Parse the IP address string into a net.IP object
|
||||||
parsedIP := net.ParseIP(ip)
|
parsedIP := net.ParseIP(ip)
|
||||||
|
|
||||||
@ -29,49 +26,7 @@ func ipToBitString(ip string) string {
|
|||||||
ipBytes = parsedIP.To16()
|
ipBytes = parsedIP.To16()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert each byte in the IP address to its 8-bit binary representation
|
return ipBytes
|
||||||
var result []string
|
|
||||||
for _, b := range ipBytes {
|
|
||||||
result = append(result, fmt.Sprintf("%08b", b))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join the binary representation of each byte with dots to form the final bit string
|
|
||||||
return strings.Join(result, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func bitStringToIp(bitString string) string {
|
|
||||||
// Check if the bit string represents an IPv4 or IPv6 address
|
|
||||||
isIPv4 := len(bitString) == 32
|
|
||||||
|
|
||||||
// Split the bit string into 8-bit segments
|
|
||||||
segments := make([]string, 0)
|
|
||||||
if isIPv4 {
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
segments = append(segments, bitString[i*8:(i+1)*8])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := 0; i < 16; i++ {
|
|
||||||
segments = append(segments, bitString[i*8:(i+1)*8])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert each segment to its decimal equivalent
|
|
||||||
decimalSegments := make([]int, len(segments))
|
|
||||||
for i, s := range segments {
|
|
||||||
val, _ := strconv.ParseInt(s, 2, 64)
|
|
||||||
decimalSegments[i] = int(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the IP address string based on the type (IPv4 or IPv6)
|
|
||||||
if isIPv4 {
|
|
||||||
return fmt.Sprintf("%d.%d.%d.%d", decimalSegments[0], decimalSegments[1], decimalSegments[2], decimalSegments[3])
|
|
||||||
} else {
|
|
||||||
ip := make(net.IP, net.IPv6len)
|
|
||||||
for i := 0; i < net.IPv6len; i++ {
|
|
||||||
ip[i] = byte(decimalSegments[i])
|
|
||||||
}
|
|
||||||
return ip.String()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// inititlaizing a new trie
|
// inititlaizing a new trie
|
||||||
@ -83,20 +38,39 @@ func newTrie() *trie {
|
|||||||
|
|
||||||
// Passing words to trie
|
// Passing words to trie
|
||||||
func (t *trie) insert(ipAddr string, cc string) {
|
func (t *trie) insert(ipAddr string, cc string) {
|
||||||
word := ipToBitString(ipAddr)
|
ipBytes := ipToBytes(ipAddr)
|
||||||
current := t.root
|
current := t.root
|
||||||
for _, wr := range word {
|
for _, b := range ipBytes {
|
||||||
index := wr - '0'
|
//For each byte in the ip address
|
||||||
if current.childrens[index] == nil {
|
//each byte is 8 bit
|
||||||
current.childrens[index] = &trie_Node{
|
for j := 0; j < 8; j++ {
|
||||||
childrens: [2]*trie_Node{},
|
bitwise := (b&uint8(math.Pow(float64(2), float64(j))) > 0)
|
||||||
ends: false,
|
bit := 0b0000
|
||||||
cc: cc,
|
if bitwise {
|
||||||
|
bit = 0b0001
|
||||||
}
|
}
|
||||||
|
if current.childrens[bit] == nil {
|
||||||
|
current.childrens[bit] = &trie_Node{
|
||||||
|
childrens: [2]*trie_Node{},
|
||||||
|
cc: cc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current.childrens[bit]
|
||||||
}
|
}
|
||||||
current = current.childrens[index]
|
|
||||||
}
|
}
|
||||||
current.ends = true
|
|
||||||
|
/*
|
||||||
|
for i := 63; i >= 0; i-- {
|
||||||
|
bit := (ipInt64 >> uint(i)) & 1
|
||||||
|
if current.childrens[bit] == nil {
|
||||||
|
current.childrens[bit] = &trie_Node{
|
||||||
|
childrens: [2]*trie_Node{},
|
||||||
|
cc: cc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current.childrens[bit]
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
func isReservedIP(ip string) bool {
|
func isReservedIP(ip string) bool {
|
||||||
@ -126,16 +100,34 @@ func (t *trie) search(ipAddr string) string {
|
|||||||
if isReservedIP(ipAddr) {
|
if isReservedIP(ipAddr) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
word := ipToBitString(ipAddr)
|
|
||||||
|
ipBytes := ipToBytes(ipAddr)
|
||||||
current := t.root
|
current := t.root
|
||||||
for _, wr := range word {
|
for _, b := range ipBytes {
|
||||||
index := wr - '0'
|
//For each byte in the ip address
|
||||||
if current.childrens[index] == nil {
|
//each byte is 8 bit
|
||||||
return current.cc
|
for j := 0; j < 8; j++ {
|
||||||
|
bitwise := (b&uint8(math.Pow(float64(2), float64(j))) > 0)
|
||||||
|
bit := 0b0000
|
||||||
|
if bitwise {
|
||||||
|
bit = 0b0001
|
||||||
|
}
|
||||||
|
if current.childrens[bit] == nil {
|
||||||
|
return current.cc
|
||||||
|
}
|
||||||
|
current = current.childrens[bit]
|
||||||
}
|
}
|
||||||
current = current.childrens[index]
|
|
||||||
}
|
}
|
||||||
if current.ends {
|
/*
|
||||||
|
for i := 63; i >= 0; i-- {
|
||||||
|
bit := (ipInt64 >> uint(i)) & 1
|
||||||
|
if current.childrens[bit] == nil {
|
||||||
|
return current.cc
|
||||||
|
}
|
||||||
|
current = current.childrens[bit]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if len(current.childrens) == 0 {
|
||||||
return current.cc
|
return current.cc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
103
src/mod/info/logger/logger.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Zoraxy Logger
|
||||||
|
|
||||||
|
This script is designed to make a managed log for the Zoraxy
|
||||||
|
and replace the ton of log.Println in the system core
|
||||||
|
*/
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
LogToFile bool //Set enable write to file
|
||||||
|
Prefix string //Prefix for log files
|
||||||
|
LogFolder string //Folder to store the log file
|
||||||
|
CurrentLogFile string //Current writing filename
|
||||||
|
file *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogger(logFilePrefix string, logFolder string, logToFile bool) (*Logger, error) {
|
||||||
|
err := os.MkdirAll(logFolder, 0775)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
thisLogger := Logger{
|
||||||
|
LogToFile: logToFile,
|
||||||
|
Prefix: logFilePrefix,
|
||||||
|
LogFolder: logFolder,
|
||||||
|
}
|
||||||
|
|
||||||
|
logFilePath := thisLogger.getLogFilepath()
|
||||||
|
f, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
thisLogger.CurrentLogFile = logFilePath
|
||||||
|
thisLogger.file = f
|
||||||
|
return &thisLogger, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) getLogFilepath() string {
|
||||||
|
year, month, _ := time.Now().Date()
|
||||||
|
return filepath.Join(l.LogFolder, l.Prefix+"_"+strconv.Itoa(year)+"-"+strconv.Itoa(int(month))+".log")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintAndLog will log the message to file and print the log to STDOUT
|
||||||
|
func (l *Logger) PrintAndLog(title string, message string, originalError error) {
|
||||||
|
go func() {
|
||||||
|
l.Log(title, message, originalError)
|
||||||
|
}()
|
||||||
|
log.Println("[" + title + "] " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println is a fast snap-in replacement for log.Println
|
||||||
|
func (l *Logger) Println(v ...interface{}) {
|
||||||
|
//Convert the array of interfaces into string
|
||||||
|
message := fmt.Sprint(v...)
|
||||||
|
go func() {
|
||||||
|
l.Log("info", string(message), nil)
|
||||||
|
}()
|
||||||
|
log.Println("[INFO] " + string(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Log(title string, errorMessage string, originalError error) {
|
||||||
|
l.ValidateAndUpdateLogFilepath()
|
||||||
|
if l.LogToFile {
|
||||||
|
if originalError == nil {
|
||||||
|
l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [INFO]" + errorMessage + "\n")
|
||||||
|
} else {
|
||||||
|
l.file.WriteString(time.Now().Format("2006-01-02 15:04:05.000000") + "|" + fmt.Sprintf("%-16s", title) + " [ERROR]" + errorMessage + " " + originalError.Error() + "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate if the logging target is still valid (detect any months change)
|
||||||
|
func (l *Logger) ValidateAndUpdateLogFilepath() {
|
||||||
|
expectedCurrentLogFilepath := l.getLogFilepath()
|
||||||
|
if l.CurrentLogFile != expectedCurrentLogFilepath {
|
||||||
|
//Change of month. Update to a new log file
|
||||||
|
l.file.Close()
|
||||||
|
f, err := os.OpenFile(expectedCurrentLogFilepath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[Logger] Unable to create new log. Logging to file disabled.")
|
||||||
|
l.LogToFile = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.CurrentLogFile = expectedCurrentLogFilepath
|
||||||
|
l.file = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Close() {
|
||||||
|
l.file.Close()
|
||||||
|
}
|
122
src/mod/info/logviewer/logviewer.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package logviewer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ViewerOption struct {
|
||||||
|
RootFolder string //The root folder to scan for log
|
||||||
|
Extension string //The extension the root files use, include the . in your ext (e.g. .log)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Viewer struct {
|
||||||
|
option *ViewerOption
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogFile struct {
|
||||||
|
Title string
|
||||||
|
Filename string
|
||||||
|
Fullpath string
|
||||||
|
Filesize int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogViewer(option *ViewerOption) *Viewer {
|
||||||
|
return &Viewer{option: option}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Log Request Handlers
|
||||||
|
*/
|
||||||
|
//List all the log files in the log folder. Return in map[string]LogFile format
|
||||||
|
func (v *Viewer) HandleListLog(w http.ResponseWriter, r *http.Request) {
|
||||||
|
logFiles := v.ListLogFiles(false)
|
||||||
|
js, _ := json.Marshal(logFiles)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read log of a given catergory and filename
|
||||||
|
// Require GET varaible: file and catergory
|
||||||
|
func (v *Viewer) HandleReadLog(w http.ResponseWriter, r *http.Request) {
|
||||||
|
filename, err := utils.GetPara(r, "file")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid filename given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
catergory, err := utils.GetPara(r, "catergory")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid catergory given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := v.LoadLogFile(strings.TrimSpace(filepath.Base(catergory)), strings.TrimSpace(filepath.Base(filename)))
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.SendTextResponse(w, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Log Access Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (v *Viewer) ListLogFiles(showFullpath bool) map[string][]*LogFile {
|
||||||
|
result := map[string][]*LogFile{}
|
||||||
|
filepath.WalkDir(v.option.RootFolder, func(path string, di fs.DirEntry, err error) error {
|
||||||
|
if filepath.Ext(path) == v.option.Extension {
|
||||||
|
catergory := filepath.Base(filepath.Dir(path))
|
||||||
|
logList, ok := result[catergory]
|
||||||
|
if !ok {
|
||||||
|
//this catergory hasn't been scanned before.
|
||||||
|
logList = []*LogFile{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fullpath := filepath.ToSlash(path)
|
||||||
|
if !showFullpath {
|
||||||
|
fullpath = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
st, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logList = append(logList, &LogFile{
|
||||||
|
Title: strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)),
|
||||||
|
Filename: filepath.Base(path),
|
||||||
|
Fullpath: fullpath,
|
||||||
|
Filesize: st.Size(),
|
||||||
|
})
|
||||||
|
|
||||||
|
result[catergory] = logList
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Viewer) LoadLogFile(catergory string, filename string) (string, error) {
|
||||||
|
logFilepath := filepath.Join(v.option.RootFolder, catergory, filename)
|
||||||
|
if utils.FileExists(logFilepath) {
|
||||||
|
//Load it
|
||||||
|
content, err := os.ReadFile(logFilepath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(content), nil
|
||||||
|
} else {
|
||||||
|
return "", errors.New("log file not found")
|
||||||
|
}
|
||||||
|
}
|
@ -226,7 +226,7 @@ func (m *MDNSHost) Scan(timeout int, domainFilter string) []*NetworkHost {
|
|||||||
return discoveredHost
|
return discoveredHost
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get all mac address of all interfaces
|
// Get all mac address of all interfaces
|
||||||
func getMacAddr() ([]string, error) {
|
func getMacAddr() ([]string, error) {
|
||||||
ifas, err := net.Interfaces()
|
ifas, err := net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -213,6 +213,7 @@ func GetNetworkInterfaceStats() (int64, int64, error) {
|
|||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
callbackChan <- wmicResult{0, 0, err}
|
callbackChan <- wmicResult{0, 0, err}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Filter out the first line
|
//Filter out the first line
|
||||||
@ -251,18 +252,16 @@ func GetNetworkInterfaceStats() (int64, int64, error) {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
//Spawn a timer to terminate the cmd process if timeout
|
//Spawn a timer to terminate the cmd process if timeout
|
||||||
var timer *time.Timer
|
time.Sleep(3 * time.Second)
|
||||||
timer = time.AfterFunc(3*time.Second, func() {
|
if cmd != nil && cmd.Process != nil {
|
||||||
timer.Stop()
|
cmd.Process.Kill()
|
||||||
if cmd != nil && cmd.Process != nil {
|
|
||||||
cmd.Process.Kill()
|
|
||||||
}
|
|
||||||
callbackChan <- wmicResult{0, 0, errors.New("wmic execution timeout")}
|
callbackChan <- wmicResult{0, 0, errors.New("wmic execution timeout")}
|
||||||
})
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
result := wmicResult{}
|
result := wmicResult{}
|
||||||
result = <-callbackChan
|
result = <-callbackChan
|
||||||
|
cmd = nil
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
log.Println("Unable to extract NIC info from wmic: " + result.Err.Error())
|
log.Println("Unable to extract NIC info from wmic: " + result.Err.Error())
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
uuid "github.com/satori/go.uuid"
|
"github.com/google/uuid"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ func (h *Handler) HandleAddBlockingPath(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
targetBlockingPath := BlockingPath{
|
targetBlockingPath := BlockingPath{
|
||||||
UUID: uuid.NewV4().String(),
|
UUID: uuid.New().String(),
|
||||||
MatchingPath: matchingPath,
|
MatchingPath: matchingPath,
|
||||||
ExactMatch: exactMatch == "true",
|
ExactMatch: exactMatch == "true",
|
||||||
StatusCode: statusCode,
|
StatusCode: statusCode,
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
uuid "github.com/satori/go.uuid"
|
"github.com/google/uuid"
|
||||||
"imuslab.com/zoraxy/mod/database"
|
"imuslab.com/zoraxy/mod/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ func NewTCProxy(options *Options) *Manager {
|
|||||||
|
|
||||||
func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
|
func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
|
||||||
//Generate a new config from options
|
//Generate a new config from options
|
||||||
configUUID := uuid.NewV4().String()
|
configUUID := uuid.New().String()
|
||||||
thisConfig := ProxyRelayConfig{
|
thisConfig := ProxyRelayConfig{
|
||||||
UUID: configUUID,
|
UUID: configUUID,
|
||||||
Name: config.Name,
|
Name: config.Name,
|
||||||
|
@ -93,8 +93,6 @@ func (m *Monitor) ExecuteUptimeCheck() {
|
|||||||
Latency: laterncy,
|
Latency: laterncy,
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println(thisRecord)
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.Println("Unknown protocol: " + target.Protocol + ". Skipping")
|
log.Println("Unknown protocol: " + target.Protocol + ". Skipping")
|
||||||
continue
|
continue
|
||||||
@ -238,9 +236,11 @@ func getWebsiteStatus(url string) (int, error) {
|
|||||||
}
|
}
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
status_code := resp.StatusCode
|
||||||
|
return status_code, nil
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
status_code := resp.StatusCode
|
status_code := resp.StatusCode
|
||||||
resp.Body.Close()
|
|
||||||
return status_code, nil
|
return status_code, nil
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -40,46 +38,6 @@ func SendOK(w http.ResponseWriter) {
|
|||||||
w.Write([]byte("\"OK\""))
|
w.Write([]byte("\"OK\""))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
The paramter move function (mv)
|
|
||||||
|
|
||||||
You can find similar things in the PHP version of ArOZ Online Beta. You need to pass in
|
|
||||||
r (HTTP Request Object)
|
|
||||||
getParamter (string, aka $_GET['This string])
|
|
||||||
|
|
||||||
Will return
|
|
||||||
Paramter string (if any)
|
|
||||||
Error (if error)
|
|
||||||
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
func Mv(r *http.Request, getParamter string, postMode bool) (string, error) {
|
|
||||||
if postMode == false {
|
|
||||||
//Access the paramter via GET
|
|
||||||
keys, ok := r.URL.Query()[getParamter]
|
|
||||||
|
|
||||||
if !ok || len(keys[0]) < 1 {
|
|
||||||
//log.Println("Url Param " + getParamter +" is missing")
|
|
||||||
return "", errors.New("GET paramter " + getParamter + " not found or it is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query()["key"] will return an array of items,
|
|
||||||
// we only want the single item.
|
|
||||||
key := keys[0]
|
|
||||||
return string(key), nil
|
|
||||||
} else {
|
|
||||||
//Access the parameter via POST
|
|
||||||
r.ParseForm()
|
|
||||||
x := r.Form.Get(getParamter)
|
|
||||||
if len(x) == 0 || x == "" {
|
|
||||||
return "", errors.New("POST paramter " + getParamter + " not found or it is empty")
|
|
||||||
}
|
|
||||||
return string(x), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Get GET parameter
|
// Get GET parameter
|
||||||
func GetPara(r *http.Request, key string) (string, error) {
|
func GetPara(r *http.Request, key string) (string, error) {
|
||||||
keys, ok := r.URL.Query()[key]
|
keys, ok := r.URL.Query()[key]
|
||||||
@ -101,6 +59,40 @@ func PostPara(r *http.Request, key string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get POST paramter as boolean, accept 1 or true
|
||||||
|
func PostBool(r *http.Request, key string) (bool, error) {
|
||||||
|
x, err := PostPara(r, key)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
x = strings.TrimSpace(x)
|
||||||
|
|
||||||
|
if x == "1" || strings.ToLower(x) == "true" {
|
||||||
|
return true, nil
|
||||||
|
} else if x == "0" || strings.ToLower(x) == "false" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errors.New("invalid boolean given")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get POST paramter as int
|
||||||
|
func PostInt(r *http.Request, key string) (int, error) {
|
||||||
|
x, err := PostPara(r, key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
x = strings.TrimSpace(x)
|
||||||
|
rx, err := strconv.Atoi(x)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rx, nil
|
||||||
|
}
|
||||||
|
|
||||||
func FileExists(filename string) bool {
|
func FileExists(filename string) bool {
|
||||||
_, err := os.Stat(filename)
|
_, err := os.Stat(filename)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@ -131,30 +123,6 @@ func TimeToString(targetTime time.Time) string {
|
|||||||
return targetTime.Format("2006-01-02 15:04:05")
|
return targetTime.Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadImageAsBase64(filepath string) (string, error) {
|
|
||||||
if !FileExists(filepath) {
|
|
||||||
return "", errors.New("File not exists")
|
|
||||||
}
|
|
||||||
f, _ := os.Open(filepath)
|
|
||||||
reader := bufio.NewReader(f)
|
|
||||||
content, _ := io.ReadAll(reader)
|
|
||||||
encoded := base64.StdEncoding.EncodeToString(content)
|
|
||||||
return string(encoded), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use for redirections
|
|
||||||
func ConstructRelativePathFromRequestURL(requestURI string, redirectionLocation string) string {
|
|
||||||
if strings.Count(requestURI, "/") == 1 {
|
|
||||||
//Already root level
|
|
||||||
return redirectionLocation
|
|
||||||
}
|
|
||||||
for i := 0; i < strings.Count(requestURI, "/")-1; i++ {
|
|
||||||
redirectionLocation = "../" + redirectionLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirectionLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if given string in a given slice
|
// Check if given string in a given slice
|
||||||
func StringInArray(arr []string, str string) bool {
|
func StringInArray(arr []string, str string) bool {
|
||||||
for _, a := range arr {
|
for _, a := range arr {
|
||||||
|
406
src/mod/webserv/filemanager/filemanager.go
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
package filemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
File Manager
|
||||||
|
|
||||||
|
This is a simple package that handles file management
|
||||||
|
under the web server directory
|
||||||
|
*/
|
||||||
|
|
||||||
|
type FileManager struct {
|
||||||
|
Directory string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new file manager with directory as root
|
||||||
|
func NewFileManager(directory string) *FileManager {
|
||||||
|
return &FileManager{
|
||||||
|
Directory: directory,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle listing of a given directory
|
||||||
|
func (fm *FileManager) HandleList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
directory, err := utils.GetPara(r, "dir")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid directory given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the absolute path to the target directory
|
||||||
|
targetDir := filepath.Join(fm.Directory, directory)
|
||||||
|
|
||||||
|
// Open the target directory
|
||||||
|
dirEntries, err := os.ReadDir(targetDir)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to open directory")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a slice to hold the file information
|
||||||
|
var files []map[string]interface{} = []map[string]interface{}{}
|
||||||
|
|
||||||
|
// Iterate through the directory entries
|
||||||
|
for _, dirEntry := range dirEntries {
|
||||||
|
fileInfo := make(map[string]interface{})
|
||||||
|
fileInfo["filename"] = dirEntry.Name()
|
||||||
|
fileInfo["filepath"] = filepath.Join(directory, dirEntry.Name())
|
||||||
|
fileInfo["isDir"] = dirEntry.IsDir()
|
||||||
|
|
||||||
|
// Get file size and last modified time
|
||||||
|
finfo, err := dirEntry.Info()
|
||||||
|
if err != nil {
|
||||||
|
//unable to load its info. Skip this file
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fileInfo["lastModified"] = finfo.ModTime().Unix()
|
||||||
|
if !dirEntry.IsDir() {
|
||||||
|
// If it's a file, get its size
|
||||||
|
fileInfo["size"] = finfo.Size()
|
||||||
|
} else {
|
||||||
|
// If it's a directory, set size to 0
|
||||||
|
fileInfo["size"] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append file info to the list
|
||||||
|
files = append(files, fileInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the file info slice to JSON
|
||||||
|
jsonData, err := json.Marshal(files)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to marshal JSON")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set response headers and send the JSON response
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(jsonData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle upload of a file (multi-part), 25MB max
|
||||||
|
func (fm *FileManager) HandleUpload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
dir, err := utils.PostPara(r, "dir")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("no dir given")
|
||||||
|
utils.SendErrorResponse(w, "invalid dir given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the multi-part form data
|
||||||
|
err = r.ParseMultipartForm(25 << 20)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to parse form data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the uploaded file
|
||||||
|
file, fheader, err := r.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
utils.SendErrorResponse(w, "unable to get uploaded file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// Specify the directory where you want to save the uploaded file
|
||||||
|
uploadDir := filepath.Join(fm.Directory, dir)
|
||||||
|
if !utils.FileExists(uploadDir) {
|
||||||
|
utils.SendErrorResponse(w, "upload target directory not exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := sanitizeFilename(fheader.Filename)
|
||||||
|
if !isValidFilename(filename) {
|
||||||
|
utils.SendErrorResponse(w, "filename contain invalid or reserved characters")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file on the server
|
||||||
|
filePath := filepath.Join(uploadDir, filepath.Base(filename))
|
||||||
|
out, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to create file on the server")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
// Copy the uploaded file to the server
|
||||||
|
_, err = io.Copy(out, file)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to copy file to server")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Respond with a success message or appropriate response
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle download of a selected file, serve with content dispose header
|
||||||
|
func (fm *FileManager) HandleDownload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
filename, err := utils.GetPara(r, "file")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid filepath given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
previewMode, _ := utils.GetPara(r, "preview")
|
||||||
|
if previewMode == "true" {
|
||||||
|
// Serve the file using http.ServeFile
|
||||||
|
filePath := filepath.Join(fm.Directory, filename)
|
||||||
|
http.ServeFile(w, r, filePath)
|
||||||
|
} else {
|
||||||
|
// Trigger a download with content disposition headers
|
||||||
|
filePath := filepath.Join(fm.Directory, filename)
|
||||||
|
w.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(filename))
|
||||||
|
http.ServeFile(w, r, filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleNewFolder creates a new folder in the specified directory
|
||||||
|
func (fm *FileManager) HandleNewFolder(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Parse the directory name from the request
|
||||||
|
dirName, err := utils.GetPara(r, "path")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid directory name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Prevent path escape
|
||||||
|
dirName = strings.ReplaceAll(dirName, "\\", "/")
|
||||||
|
dirName = strings.ReplaceAll(dirName, "../", "")
|
||||||
|
|
||||||
|
// Specify the directory where you want to create the new folder
|
||||||
|
newFolderPath := filepath.Join(fm.Directory, dirName)
|
||||||
|
|
||||||
|
// Check if the folder already exists
|
||||||
|
if _, err := os.Stat(newFolderPath); os.IsNotExist(err) {
|
||||||
|
// Create the new folder
|
||||||
|
err := os.Mkdir(newFolderPath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to create the new folder")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Respond with a success message or appropriate response
|
||||||
|
utils.SendOK(w)
|
||||||
|
} else {
|
||||||
|
// If the folder already exists, respond with an error
|
||||||
|
utils.SendErrorResponse(w, "folder already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleFileCopy copies a file or directory from the source path to the destination path
|
||||||
|
func (fm *FileManager) HandleFileCopy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Parse the source and destination paths from the request
|
||||||
|
srcPath, err := utils.PostPara(r, "srcpath")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid source path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
destPath, err := utils.PostPara(r, "destpath")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid destination path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and sanitize the source and destination paths
|
||||||
|
srcPath = filepath.Clean(srcPath)
|
||||||
|
destPath = filepath.Clean(destPath)
|
||||||
|
|
||||||
|
// Construct the absolute paths
|
||||||
|
absSrcPath := filepath.Join(fm.Directory, srcPath)
|
||||||
|
absDestPath := filepath.Join(fm.Directory, destPath)
|
||||||
|
|
||||||
|
// Check if the source path exists
|
||||||
|
if _, err := os.Stat(absSrcPath); os.IsNotExist(err) {
|
||||||
|
utils.SendErrorResponse(w, "source path does not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the destination path exists
|
||||||
|
if _, err := os.Stat(absDestPath); os.IsNotExist(err) {
|
||||||
|
utils.SendErrorResponse(w, "destination path does not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Join the name to create final paste filename
|
||||||
|
absDestPath = filepath.Join(absDestPath, filepath.Base(absSrcPath))
|
||||||
|
//Reject opr if already exists
|
||||||
|
if utils.FileExists(absDestPath) {
|
||||||
|
utils.SendErrorResponse(w, "target already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the copy operation based on whether the source is a file or directory
|
||||||
|
if isDir(absSrcPath) {
|
||||||
|
// Recursive copy for directories
|
||||||
|
err := copyDirectory(absSrcPath, absDestPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, fmt.Sprintf("error copying directory: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Copy a single file
|
||||||
|
err := copyFile(absSrcPath, absDestPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, fmt.Sprintf("error copying file: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *FileManager) HandleFileMove(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Parse the source and destination paths from the request
|
||||||
|
srcPath, err := utils.GetPara(r, "srcpath")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid source path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
destPath, err := utils.GetPara(r, "destpath")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid destination path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and sanitize the source and destination paths
|
||||||
|
srcPath = filepath.Clean(srcPath)
|
||||||
|
destPath = filepath.Clean(destPath)
|
||||||
|
|
||||||
|
// Construct the absolute paths
|
||||||
|
absSrcPath := filepath.Join(fm.Directory, srcPath)
|
||||||
|
absDestPath := filepath.Join(fm.Directory, destPath)
|
||||||
|
|
||||||
|
// Check if the source path exists
|
||||||
|
if _, err := os.Stat(absSrcPath); os.IsNotExist(err) {
|
||||||
|
utils.SendErrorResponse(w, "source path does not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the destination path exists
|
||||||
|
if _, err := os.Stat(absDestPath); !os.IsNotExist(err) {
|
||||||
|
utils.SendErrorResponse(w, "destination path already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename the source to the destination
|
||||||
|
err = os.Rename(absSrcPath, absDestPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, fmt.Sprintf("error moving file/directory: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *FileManager) HandleFileProperties(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Parse the target file or directory path from the request
|
||||||
|
filePath, err := utils.GetPara(r, "file")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid file path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the absolute path to the target file or directory
|
||||||
|
absPath := filepath.Join(fm.Directory, filePath)
|
||||||
|
|
||||||
|
// Check if the target path exists
|
||||||
|
_, err = os.Stat(absPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "file or directory does not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a map to hold file properties
|
||||||
|
fileProps := make(map[string]interface{})
|
||||||
|
fileProps["filename"] = filepath.Base(absPath)
|
||||||
|
fileProps["filepath"] = filePath
|
||||||
|
fileProps["isDir"] = isDir(absPath)
|
||||||
|
|
||||||
|
// Get file size and last modified time
|
||||||
|
finfo, err := os.Stat(absPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to retrieve file properties")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileProps["lastModified"] = finfo.ModTime().Unix()
|
||||||
|
if !isDir(absPath) {
|
||||||
|
// If it's a file, get its size
|
||||||
|
fileProps["size"] = finfo.Size()
|
||||||
|
} else {
|
||||||
|
// If it's a directory, calculate its total size containing all child files and folders
|
||||||
|
totalSize, err := calculateDirectorySize(absPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to calculate directory size")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileProps["size"] = totalSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count the number of sub-files and sub-folders
|
||||||
|
numSubFiles, numSubFolders, err := countSubFilesAndFolders(absPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to count sub-files and sub-folders")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileProps["fileCounts"] = numSubFiles
|
||||||
|
fileProps["folderCounts"] = numSubFolders
|
||||||
|
|
||||||
|
// Serialize the file properties to JSON
|
||||||
|
jsonData, err := json.Marshal(fileProps)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "unable to marshal JSON")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set response headers and send the JSON response
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(jsonData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleFileDelete deletes a file or directory
|
||||||
|
func (fm *FileManager) HandleFileDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Parse the target file or directory path from the request
|
||||||
|
filePath, err := utils.PostPara(r, "target")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid file path")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the absolute path to the target file or directory
|
||||||
|
absPath := filepath.Join(fm.Directory, filePath)
|
||||||
|
|
||||||
|
// Check if the target path exists
|
||||||
|
_, err = os.Stat(absPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "file or directory does not exist")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the file or directory
|
||||||
|
err = os.RemoveAll(absPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "error deleting file or directory")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Respond with a success message or appropriate response
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
156
src/mod/webserv/filemanager/utils.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package filemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isValidFilename checks if a given filename is safe and valid.
|
||||||
|
func isValidFilename(filename string) bool {
|
||||||
|
// Define a list of disallowed characters and reserved names
|
||||||
|
disallowedChars := []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"} // Add more if needed
|
||||||
|
reservedNames := []string{"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"} // Add more if needed
|
||||||
|
|
||||||
|
// Check for disallowed characters
|
||||||
|
for _, char := range disallowedChars {
|
||||||
|
if strings.Contains(filename, char) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for reserved names (case-insensitive)
|
||||||
|
lowerFilename := strings.ToUpper(filename)
|
||||||
|
for _, reserved := range reservedNames {
|
||||||
|
if lowerFilename == reserved {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for empty filename
|
||||||
|
if filename == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The filename is considered valid
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitizeFilename sanitizes a given filename by removing disallowed characters.
|
||||||
|
func sanitizeFilename(filename string) string {
|
||||||
|
disallowedChars := []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"} // Add more if needed
|
||||||
|
|
||||||
|
// Replace disallowed characters with underscores
|
||||||
|
for _, char := range disallowedChars {
|
||||||
|
filename = strings.ReplaceAll(filename, char, "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyFile copies a single file from source to destination
|
||||||
|
func copyFile(srcPath, destPath string) error {
|
||||||
|
srcFile, err := os.Open(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer srcFile.Close()
|
||||||
|
|
||||||
|
destFile, err := os.Create(destPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(destFile, srcFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyDirectory recursively copies a directory and its contents from source to destination
|
||||||
|
func copyDirectory(srcPath, destPath string) error {
|
||||||
|
// Create the destination directory
|
||||||
|
err := os.MkdirAll(destPath, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
srcEntryPath := filepath.Join(srcPath, entry.Name())
|
||||||
|
destEntryPath := filepath.Join(destPath, entry.Name())
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
err := copyDirectory(srcEntryPath, destEntryPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := copyFile(srcEntryPath, destEntryPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDir checks if the given path is a directory
|
||||||
|
func isDir(path string) bool {
|
||||||
|
fileInfo, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fileInfo.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateDirectorySize calculates the total size of a directory and its contents
|
||||||
|
func calculateDirectorySize(dirPath string) (int64, error) {
|
||||||
|
var totalSize int64
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
totalSize += info.Size()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return totalSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// countSubFilesAndFolders counts the number of sub-files and sub-folders within a directory
|
||||||
|
func countSubFilesAndFolders(dirPath string) (int, int, error) {
|
||||||
|
var numSubFiles, numSubFolders int
|
||||||
|
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
numSubFolders++
|
||||||
|
} else {
|
||||||
|
numSubFiles++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract 1 from numSubFolders to exclude the root directory itself
|
||||||
|
return numSubFiles, numSubFolders - 1, nil
|
||||||
|
}
|
88
src/mod/webserv/handler.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package webserv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Handler.go
|
||||||
|
|
||||||
|
Handler for web server options change
|
||||||
|
web server is directly listening to the TCP port
|
||||||
|
handlers in this script are for setting change only
|
||||||
|
*/
|
||||||
|
|
||||||
|
type StaticWebServerStatus struct {
|
||||||
|
ListeningPort int
|
||||||
|
EnableDirectoryListing bool
|
||||||
|
WebRoot string
|
||||||
|
Running bool
|
||||||
|
EnableWebDirManager bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle getting current static web server status
|
||||||
|
func (ws *WebServer) HandleGetStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
|
listeningPortInt, _ := strconv.Atoi(ws.option.Port)
|
||||||
|
currentStatus := StaticWebServerStatus{
|
||||||
|
ListeningPort: listeningPortInt,
|
||||||
|
EnableDirectoryListing: ws.option.EnableDirectoryListing,
|
||||||
|
WebRoot: ws.option.WebRoot,
|
||||||
|
Running: ws.isRunning,
|
||||||
|
EnableWebDirManager: ws.option.EnableWebDirManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
js, _ := json.Marshal(currentStatus)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle request for starting the static web server
|
||||||
|
func (ws *WebServer) HandleStartServer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := ws.Start()
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle request for stopping the static web server
|
||||||
|
func (ws *WebServer) HandleStopServer(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := ws.Stop()
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle change server listening port request
|
||||||
|
func (ws *WebServer) HandlePortChange(w http.ResponseWriter, r *http.Request) {
|
||||||
|
newPort, err := utils.PostInt(r, "port")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid port number given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ws.ChangePort(strconv.Itoa(newPort))
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change enable directory listing settings
|
||||||
|
func (ws *WebServer) SetEnableDirectoryListing(w http.ResponseWriter, r *http.Request) {
|
||||||
|
enableList, err := utils.PostBool(r, "enable")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "invalid setting given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.option.EnableDirectoryListing = enableList
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
41
src/mod/webserv/middleware.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package webserv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Convert a request path (e.g. /index.html) into physical path on disk
|
||||||
|
func (ws *WebServer) resolveFileDiskPath(requestPath string) string {
|
||||||
|
fileDiskpath := filepath.Join(ws.option.WebRoot, "html", requestPath)
|
||||||
|
|
||||||
|
//Force convert it to slash even if the host OS is on Windows
|
||||||
|
fileDiskpath = filepath.Clean(fileDiskpath)
|
||||||
|
fileDiskpath = strings.ReplaceAll(fileDiskpath, "\\", "/")
|
||||||
|
return fileDiskpath
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// File server middleware to handle directory listing (and future expansion)
|
||||||
|
func (ws *WebServer) fsMiddleware(h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !ws.option.EnableDirectoryListing {
|
||||||
|
if strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
//This is a folder. Let check if index exists
|
||||||
|
if utils.FileExists(filepath.Join(ws.resolveFileDiskPath(r.URL.Path), "index.html")) {
|
||||||
|
|
||||||
|
} else if utils.FileExists(filepath.Join(ws.resolveFileDiskPath(r.URL.Path), "index.htm")) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
61
src/mod/webserv/templates/index.html
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Hello Zoraxy</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Tahoma, sans-serif;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
color: #2d2e30;
|
||||||
|
}
|
||||||
|
.sectionHeader{
|
||||||
|
background-color: #c4d0d9;
|
||||||
|
padding: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeader h3{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.container{
|
||||||
|
margin: 4em;
|
||||||
|
margin-left: 10em;
|
||||||
|
margin-right: 10em;
|
||||||
|
background-color: #fefefe;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:960px) {
|
||||||
|
.container{
|
||||||
|
margin-left: 1em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeader{
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.textcontainer{
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="sectionHeader">
|
||||||
|
<h3>Welcome to Zoraxy Static Web Server!</h3>
|
||||||
|
</div>
|
||||||
|
<div class="textcontainer">
|
||||||
|
<p>If you see this page, that means your static web server is running.<br>
|
||||||
|
By default, all the html files are stored under <code>./web/html/</code>
|
||||||
|
relative to the zoraxy runtime directory.<br>
|
||||||
|
You can upload your html files to your web directory via the <b>Web Directory Manager</b>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For online documentation, please refer to <a href="//zoraxy.arozos.com">zoraxy.arozos.com</a> or the <a href="https://github.com/tobychui/zoraxy/wiki">project wiki</a>.<br>
|
||||||
|
Thank you for using Zoraxy!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
18
src/mod/webserv/utils.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package webserv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsPortInUse checks if a port is in use.
|
||||||
|
func IsPortInUse(port string) bool {
|
||||||
|
listener, err := net.Listen("tcp", "localhost:"+port)
|
||||||
|
if err != nil {
|
||||||
|
// If there was an error, the port is in use.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
// No error means the port is available.
|
||||||
|
return false
|
||||||
|
}
|
195
src/mod/webserv/webserv.go
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
package webserv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
_ "embed"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"imuslab.com/zoraxy/mod/database"
|
||||||
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
|
"imuslab.com/zoraxy/mod/webserv/filemanager"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Static Web Server package
|
||||||
|
|
||||||
|
This module host a static web server
|
||||||
|
*/
|
||||||
|
|
||||||
|
//go:embed templates/*
|
||||||
|
var templates embed.FS
|
||||||
|
|
||||||
|
type WebServerOptions struct {
|
||||||
|
Port string //Port for listening
|
||||||
|
EnableDirectoryListing bool //Enable listing of directory
|
||||||
|
WebRoot string //Folder for stroing the static web folders
|
||||||
|
EnableWebDirManager bool //Enable web file manager to handle files in web directory
|
||||||
|
Sysdb *database.Database //Database for storing configs
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebServer struct {
|
||||||
|
FileManager *filemanager.FileManager
|
||||||
|
|
||||||
|
mux *http.ServeMux
|
||||||
|
server *http.Server
|
||||||
|
option *WebServerOptions
|
||||||
|
isRunning bool
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWebServer creates a new WebServer instance. One instance only
|
||||||
|
func NewWebServer(options *WebServerOptions) *WebServer {
|
||||||
|
if !utils.FileExists(options.WebRoot) {
|
||||||
|
//Web root folder not exists. Create one with default templates
|
||||||
|
os.MkdirAll(filepath.Join(options.WebRoot, "html"), 0775)
|
||||||
|
os.MkdirAll(filepath.Join(options.WebRoot, "templates"), 0775)
|
||||||
|
indexTemplate, err := templates.ReadFile("templates/index.html")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to read static wev server template file: ", err.Error())
|
||||||
|
} else {
|
||||||
|
os.WriteFile(filepath.Join(options.WebRoot, "html", "index.html"), indexTemplate, 0775)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create a new file manager if it is enabled
|
||||||
|
var newDirManager *filemanager.FileManager
|
||||||
|
if options.EnableWebDirManager {
|
||||||
|
fm := filemanager.NewFileManager(filepath.Join(options.WebRoot, "/html"))
|
||||||
|
newDirManager = fm
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create new table to store the config
|
||||||
|
options.Sysdb.NewTable("webserv")
|
||||||
|
return &WebServer{
|
||||||
|
mux: http.NewServeMux(),
|
||||||
|
FileManager: newDirManager,
|
||||||
|
option: options,
|
||||||
|
isRunning: false,
|
||||||
|
mu: sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the configuration to previous config
|
||||||
|
func (ws *WebServer) RestorePreviousState() {
|
||||||
|
//Set the port
|
||||||
|
port := ws.option.Port
|
||||||
|
ws.option.Sysdb.Read("webserv", "port", &port)
|
||||||
|
ws.option.Port = port
|
||||||
|
|
||||||
|
//Set the enable directory list
|
||||||
|
enableDirList := ws.option.EnableDirectoryListing
|
||||||
|
ws.option.Sysdb.Read("webserv", "dirlist", &enableDirList)
|
||||||
|
ws.option.EnableDirectoryListing = enableDirList
|
||||||
|
|
||||||
|
//Check the running state
|
||||||
|
webservRunning := false
|
||||||
|
ws.option.Sysdb.Read("webserv", "enabled", &webservRunning)
|
||||||
|
if webservRunning {
|
||||||
|
ws.Start()
|
||||||
|
} else {
|
||||||
|
ws.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangePort changes the server's port.
|
||||||
|
func (ws *WebServer) ChangePort(port string) error {
|
||||||
|
if IsPortInUse(port) {
|
||||||
|
return errors.New("Selected port is used by another process")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ws.isRunning {
|
||||||
|
if err := ws.Stop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.option.Port = port
|
||||||
|
ws.server.Addr = ":" + port
|
||||||
|
|
||||||
|
err := ws.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.option.Sysdb.Write("webserv", "port", port)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the web server.
|
||||||
|
func (ws *WebServer) Start() error {
|
||||||
|
ws.mu.Lock()
|
||||||
|
defer ws.mu.Unlock()
|
||||||
|
|
||||||
|
//Check if server already running
|
||||||
|
if ws.isRunning {
|
||||||
|
return fmt.Errorf("web server is already running")
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the port is usable
|
||||||
|
if IsPortInUse(ws.option.Port) {
|
||||||
|
return errors.New("Port already in use or access denied by host OS")
|
||||||
|
}
|
||||||
|
|
||||||
|
//Dispose the old mux and create a new one
|
||||||
|
ws.mux = http.NewServeMux()
|
||||||
|
|
||||||
|
//Create a static web server
|
||||||
|
fs := http.FileServer(http.Dir(filepath.Join(ws.option.WebRoot, "html")))
|
||||||
|
ws.mux.Handle("/", ws.fsMiddleware(fs))
|
||||||
|
|
||||||
|
ws.server = &http.Server{
|
||||||
|
Addr: ":" + ws.option.Port,
|
||||||
|
Handler: ws.mux,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := ws.server.ListenAndServe(); err != nil {
|
||||||
|
if err != http.ErrServerClosed {
|
||||||
|
fmt.Printf("Web server error: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Println("Static Web Server started. Listeing on :" + ws.option.Port)
|
||||||
|
ws.isRunning = true
|
||||||
|
ws.option.Sysdb.Write("webserv", "enabled", true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the web server.
|
||||||
|
func (ws *WebServer) Stop() error {
|
||||||
|
ws.mu.Lock()
|
||||||
|
defer ws.mu.Unlock()
|
||||||
|
|
||||||
|
if !ws.isRunning {
|
||||||
|
return fmt.Errorf("web server is not running")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ws.server.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.isRunning = false
|
||||||
|
ws.option.Sysdb.Write("webserv", "enabled", false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDirectoryListing enables or disables directory listing.
|
||||||
|
func (ws *WebServer) UpdateDirectoryListing(enable bool) {
|
||||||
|
ws.option.EnableDirectoryListing = enable
|
||||||
|
ws.option.Sysdb.Write("webserv", "dirlist", enable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close stops the web server without returning an error.
|
||||||
|
func (ws *WebServer) Close() {
|
||||||
|
ws.Stop()
|
||||||
|
}
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
@ -25,33 +24,43 @@ func ReverseProxtInit() {
|
|||||||
inboundPort := 80
|
inboundPort := 80
|
||||||
if sysdb.KeyExists("settings", "inbound") {
|
if sysdb.KeyExists("settings", "inbound") {
|
||||||
sysdb.Read("settings", "inbound", &inboundPort)
|
sysdb.Read("settings", "inbound", &inboundPort)
|
||||||
log.Println("Serving inbound port ", inboundPort)
|
SystemWideLogger.Println("Serving inbound port ", inboundPort)
|
||||||
} else {
|
} else {
|
||||||
log.Println("Inbound port not set. Using default (80)")
|
SystemWideLogger.Println("Inbound port not set. Using default (80)")
|
||||||
}
|
}
|
||||||
|
|
||||||
useTls := false
|
useTls := false
|
||||||
sysdb.Read("settings", "usetls", &useTls)
|
sysdb.Read("settings", "usetls", &useTls)
|
||||||
if useTls {
|
if useTls {
|
||||||
log.Println("TLS mode enabled. Serving proxxy request with TLS")
|
SystemWideLogger.Println("TLS mode enabled. Serving proxxy request with TLS")
|
||||||
} else {
|
} else {
|
||||||
log.Println("TLS mode disabled. Serving proxy request with plain http")
|
SystemWideLogger.Println("TLS mode disabled. Serving proxy request with plain http")
|
||||||
}
|
}
|
||||||
|
|
||||||
forceLatestTLSVersion := false
|
forceLatestTLSVersion := false
|
||||||
sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion)
|
sysdb.Read("settings", "forceLatestTLS", &forceLatestTLSVersion)
|
||||||
if forceLatestTLSVersion {
|
if forceLatestTLSVersion {
|
||||||
log.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2")
|
SystemWideLogger.Println("Force latest TLS mode enabled. Minimum TLS LS version is set to v1.2")
|
||||||
} else {
|
} else {
|
||||||
log.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0")
|
SystemWideLogger.Println("Force latest TLS mode disabled. Minimum TLS version is set to v1.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
listenOnPort80 := false
|
||||||
|
sysdb.Read("settings", "listenP80", &listenOnPort80)
|
||||||
|
if listenOnPort80 {
|
||||||
|
SystemWideLogger.Println("Port 80 listener enabled")
|
||||||
|
} else {
|
||||||
|
SystemWideLogger.Println("Port 80 listener disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
forceHttpsRedirect := false
|
forceHttpsRedirect := false
|
||||||
sysdb.Read("settings", "redirect", &forceHttpsRedirect)
|
sysdb.Read("settings", "redirect", &forceHttpsRedirect)
|
||||||
if forceHttpsRedirect {
|
if forceHttpsRedirect {
|
||||||
log.Println("Force HTTPS mode enabled")
|
SystemWideLogger.Println("Force HTTPS mode enabled")
|
||||||
|
//Port 80 listener must be enabled to perform http -> https redirect
|
||||||
|
listenOnPort80 = true
|
||||||
} else {
|
} else {
|
||||||
log.Println("Force HTTPS mode disabled")
|
SystemWideLogger.Println("Force HTTPS mode disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
|
dprouter, err := dynamicproxy.NewDynamicProxy(dynamicproxy.RouterOption{
|
||||||
@ -59,14 +68,16 @@ func ReverseProxtInit() {
|
|||||||
Port: inboundPort,
|
Port: inboundPort,
|
||||||
UseTls: useTls,
|
UseTls: useTls,
|
||||||
ForceTLSLatest: forceLatestTLSVersion,
|
ForceTLSLatest: forceLatestTLSVersion,
|
||||||
|
ListenOnPort80: listenOnPort80,
|
||||||
ForceHttpsRedirect: forceHttpsRedirect,
|
ForceHttpsRedirect: forceHttpsRedirect,
|
||||||
TlsManager: tlsCertManager,
|
TlsManager: tlsCertManager,
|
||||||
RedirectRuleTable: redirectTable,
|
RedirectRuleTable: redirectTable,
|
||||||
GeodbStore: geodbStore,
|
GeodbStore: geodbStore,
|
||||||
StatisticCollector: statisticCollector,
|
StatisticCollector: statisticCollector,
|
||||||
|
WebDirectory: *staticWebServerRoot,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err.Error())
|
SystemWideLogger.PrintAndLog("Proxy", "Unable to create dynamic proxy router", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +88,7 @@ func ReverseProxtInit() {
|
|||||||
for _, conf := range confs {
|
for _, conf := range confs {
|
||||||
record, err := LoadReverseProxyConfig(conf)
|
record, err := LoadReverseProxyConfig(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to load "+filepath.Base(conf), err.Error())
|
SystemWideLogger.PrintAndLog("Proxy", "Failed to load config file: "+filepath.Base(conf), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,24 +99,28 @@ func ReverseProxtInit() {
|
|||||||
})
|
})
|
||||||
} else if record.ProxyType == "subd" {
|
} else if record.ProxyType == "subd" {
|
||||||
dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{
|
dynamicProxyRouter.AddSubdomainRoutingService(&dynamicproxy.SubdOptions{
|
||||||
MatchingDomain: record.Rootname,
|
MatchingDomain: record.Rootname,
|
||||||
Domain: record.ProxyTarget,
|
Domain: record.ProxyTarget,
|
||||||
RequireTLS: record.UseTLS,
|
RequireTLS: record.UseTLS,
|
||||||
SkipCertValidations: record.SkipTlsValidation,
|
BypassGlobalTLS: record.BypassGlobalTLS,
|
||||||
RequireBasicAuth: record.RequireBasicAuth,
|
SkipCertValidations: record.SkipTlsValidation,
|
||||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
RequireBasicAuth: record.RequireBasicAuth,
|
||||||
|
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||||
|
BasicAuthExceptionRules: record.BasicAuthExceptionRules,
|
||||||
})
|
})
|
||||||
} else if record.ProxyType == "vdir" {
|
} else if record.ProxyType == "vdir" {
|
||||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
|
dynamicProxyRouter.AddVirtualDirectoryProxyService(&dynamicproxy.VdirOptions{
|
||||||
RootName: record.Rootname,
|
RootName: record.Rootname,
|
||||||
Domain: record.ProxyTarget,
|
Domain: record.ProxyTarget,
|
||||||
RequireTLS: record.UseTLS,
|
RequireTLS: record.UseTLS,
|
||||||
SkipCertValidations: record.SkipTlsValidation,
|
BypassGlobalTLS: record.BypassGlobalTLS,
|
||||||
RequireBasicAuth: record.RequireBasicAuth,
|
SkipCertValidations: record.SkipTlsValidation,
|
||||||
BasicAuthCredentials: record.BasicAuthCredentials,
|
RequireBasicAuth: record.RequireBasicAuth,
|
||||||
|
BasicAuthCredentials: record.BasicAuthCredentials,
|
||||||
|
BasicAuthExceptionRules: record.BasicAuthExceptionRules,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
log.Println("Unsupported endpoint type: " + record.ProxyType + ". Skipping " + filepath.Base(conf))
|
SystemWideLogger.PrintAndLog("Proxy", "Unsupported endpoint type: "+record.ProxyType+". Skipping "+filepath.Base(conf), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +129,7 @@ func ReverseProxtInit() {
|
|||||||
//reverse proxy server in front of this service
|
//reverse proxy server in front of this service
|
||||||
time.Sleep(300 * time.Millisecond)
|
time.Sleep(300 * time.Millisecond)
|
||||||
dynamicProxyRouter.StartProxyService()
|
dynamicProxyRouter.StartProxyService()
|
||||||
log.Println("Dynamic Reverse Proxy service started")
|
SystemWideLogger.Println("Dynamic Reverse Proxy service started")
|
||||||
|
|
||||||
//Add all proxy services to uptime monitor
|
//Add all proxy services to uptime monitor
|
||||||
//Create a uptime monitor service
|
//Create a uptime monitor service
|
||||||
@ -125,7 +140,7 @@ func ReverseProxtInit() {
|
|||||||
Interval: 300, //5 minutes
|
Interval: 300, //5 minutes
|
||||||
MaxRecordsStore: 288, //1 day
|
MaxRecordsStore: 288, //1 day
|
||||||
})
|
})
|
||||||
log.Println("Uptime Monitor background service started")
|
SystemWideLogger.Println("Uptime Monitor background service started")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -177,6 +192,13 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
useTLS := (tls == "true")
|
useTLS := (tls == "true")
|
||||||
|
|
||||||
|
bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
|
||||||
|
if bypassGlobalTLS == "" {
|
||||||
|
bypassGlobalTLS = "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
||||||
|
|
||||||
stv, _ := utils.PostPara(r, "tlsval")
|
stv, _ := utils.PostPara(r, "tlsval")
|
||||||
if stv == "" {
|
if stv == "" {
|
||||||
stv = "false"
|
stv = "false"
|
||||||
@ -237,6 +259,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
RootName: vdir,
|
RootName: vdir,
|
||||||
Domain: endpoint,
|
Domain: endpoint,
|
||||||
RequireTLS: useTLS,
|
RequireTLS: useTLS,
|
||||||
|
BypassGlobalTLS: useBypassGlobalTLS,
|
||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: skipTlsValidation,
|
||||||
RequireBasicAuth: requireBasicAuth,
|
RequireBasicAuth: requireBasicAuth,
|
||||||
BasicAuthCredentials: basicAuthCredentials,
|
BasicAuthCredentials: basicAuthCredentials,
|
||||||
@ -254,6 +277,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
MatchingDomain: subdomain,
|
MatchingDomain: subdomain,
|
||||||
Domain: endpoint,
|
Domain: endpoint,
|
||||||
RequireTLS: useTLS,
|
RequireTLS: useTLS,
|
||||||
|
BypassGlobalTLS: useBypassGlobalTLS,
|
||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: skipTlsValidation,
|
||||||
RequireBasicAuth: requireBasicAuth,
|
RequireBasicAuth: requireBasicAuth,
|
||||||
BasicAuthCredentials: basicAuthCredentials,
|
BasicAuthCredentials: basicAuthCredentials,
|
||||||
@ -278,11 +302,12 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
Rootname: rootname,
|
Rootname: rootname,
|
||||||
ProxyTarget: endpoint,
|
ProxyTarget: endpoint,
|
||||||
UseTLS: useTLS,
|
UseTLS: useTLS,
|
||||||
|
BypassGlobalTLS: useBypassGlobalTLS,
|
||||||
SkipTlsValidation: skipTlsValidation,
|
SkipTlsValidation: skipTlsValidation,
|
||||||
RequireBasicAuth: requireBasicAuth,
|
RequireBasicAuth: requireBasicAuth,
|
||||||
BasicAuthCredentials: basicAuthCredentials,
|
BasicAuthCredentials: basicAuthCredentials,
|
||||||
}
|
}
|
||||||
SaveReverseProxyConfig(&thisProxyConfigRecord)
|
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
||||||
|
|
||||||
//Update utm if exists
|
//Update utm if exists
|
||||||
if uptimeMonitor != nil {
|
if uptimeMonitor != nil {
|
||||||
@ -329,9 +354,15 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
if stv == "" {
|
if stv == "" {
|
||||||
stv = "false"
|
stv = "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
skipTlsValidation := (stv == "true")
|
skipTlsValidation := (stv == "true")
|
||||||
|
|
||||||
|
//Load bypass TLS option
|
||||||
|
bpgtls, _ := utils.PostPara(r, "bpgtls")
|
||||||
|
if bpgtls == "" {
|
||||||
|
bpgtls = "false"
|
||||||
|
}
|
||||||
|
bypassGlobalTLS := (bpgtls == "true")
|
||||||
|
|
||||||
rba, _ := utils.PostPara(r, "bauth")
|
rba, _ := utils.PostPara(r, "bauth")
|
||||||
if rba == "" {
|
if rba == "" {
|
||||||
rba = "false"
|
rba = "false"
|
||||||
@ -351,11 +382,12 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
RootName: targetProxyEntry.RootOrMatchingDomain,
|
RootName: targetProxyEntry.RootOrMatchingDomain,
|
||||||
Domain: endpoint,
|
Domain: endpoint,
|
||||||
RequireTLS: useTLS,
|
RequireTLS: useTLS,
|
||||||
|
BypassGlobalTLS: false,
|
||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: skipTlsValidation,
|
||||||
RequireBasicAuth: requireBasicAuth,
|
RequireBasicAuth: requireBasicAuth,
|
||||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||||
}
|
}
|
||||||
dynamicProxyRouter.RemoveProxy("vdir", thisOption.RootName)
|
targetProxyEntry.Remove()
|
||||||
dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
|
dynamicProxyRouter.AddVirtualDirectoryProxyService(&thisOption)
|
||||||
|
|
||||||
} else if eptype == "subd" {
|
} else if eptype == "subd" {
|
||||||
@ -363,11 +395,12 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
MatchingDomain: targetProxyEntry.RootOrMatchingDomain,
|
MatchingDomain: targetProxyEntry.RootOrMatchingDomain,
|
||||||
Domain: endpoint,
|
Domain: endpoint,
|
||||||
RequireTLS: useTLS,
|
RequireTLS: useTLS,
|
||||||
|
BypassGlobalTLS: bypassGlobalTLS,
|
||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: skipTlsValidation,
|
||||||
RequireBasicAuth: requireBasicAuth,
|
RequireBasicAuth: requireBasicAuth,
|
||||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||||
}
|
}
|
||||||
dynamicProxyRouter.RemoveProxy("subd", thisOption.MatchingDomain)
|
targetProxyEntry.Remove()
|
||||||
dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
|
dynamicProxyRouter.AddSubdomainRoutingService(&thisOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,7 +414,11 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
RequireBasicAuth: requireBasicAuth,
|
RequireBasicAuth: requireBasicAuth,
|
||||||
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
BasicAuthCredentials: targetProxyEntry.BasicAuthCredentials,
|
||||||
}
|
}
|
||||||
SaveReverseProxyConfig(&thisProxyConfigRecord)
|
SaveReverseProxyConfigToFile(&thisProxyConfigRecord)
|
||||||
|
|
||||||
|
//Update uptime monitor
|
||||||
|
UpdateUptimeMonitorTargets()
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,13 +435,15 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dynamicProxyRouter.RemoveProxy(ptype, ep)
|
//Remove the config from runtime
|
||||||
|
err = dynamicProxyRouter.RemoveProxyEndpointByRootname(ptype, ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveReverseProxyConfig(ep)
|
//Remove the config from file
|
||||||
|
RemoveReverseProxyConfigFile(ep)
|
||||||
|
|
||||||
//Update utm if exists
|
//Update utm if exists
|
||||||
if uptimeMonitor != nil {
|
if uptimeMonitor != nil {
|
||||||
@ -412,6 +451,9 @@ func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
uptimeMonitor.CleanRecords()
|
uptimeMonitor.CleanRecords()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Update uptime monitor
|
||||||
|
UpdateUptimeMonitorTargets()
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,19 +570,10 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
|||||||
targetProxy.BasicAuthCredentials = mergedCredentials
|
targetProxy.BasicAuthCredentials = mergedCredentials
|
||||||
|
|
||||||
//Save it to file
|
//Save it to file
|
||||||
thisProxyConfigRecord := Record{
|
SaveReverseProxyEndpointToFile(targetProxy)
|
||||||
ProxyType: ptype,
|
|
||||||
Rootname: targetProxy.RootOrMatchingDomain,
|
|
||||||
ProxyTarget: targetProxy.Domain,
|
|
||||||
UseTLS: targetProxy.RequireTLS,
|
|
||||||
SkipTlsValidation: targetProxy.SkipCertValidations,
|
|
||||||
RequireBasicAuth: targetProxy.RequireBasicAuth,
|
|
||||||
BasicAuthCredentials: targetProxy.BasicAuthCredentials,
|
|
||||||
}
|
|
||||||
SaveReverseProxyConfig(&thisProxyConfigRecord)
|
|
||||||
|
|
||||||
//Replace runtime configuration
|
//Replace runtime configuration
|
||||||
dynamicProxyRouter.SaveProxy(ptype, ep, targetProxy)
|
targetProxy.UpdateToRuntime()
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
} else {
|
} else {
|
||||||
http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
|
http.Error(w, "invalid usage", http.StatusMethodNotAllowed)
|
||||||
@ -548,6 +581,147 @@ func UpdateProxyBasicAuthCredentials(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List, Update or Remove the exception paths for basic auth.
|
||||||
|
func ListProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
ep, err := utils.GetPara(r, "ep")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid ep given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ptype, err := utils.GetPara(r, "ptype")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load the target proxy object from router
|
||||||
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//List all the exception paths for this proxy
|
||||||
|
results := targetProxy.BasicAuthExceptionRules
|
||||||
|
if results == nil {
|
||||||
|
//It is a config from a really old version of zoraxy. Overwrite it with empty array
|
||||||
|
results = []*dynamicproxy.BasicAuthExceptionRule{}
|
||||||
|
}
|
||||||
|
js, _ := json.Marshal(results)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ep, err := utils.PostPara(r, "ep")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid ep given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ptype, err := utils.PostPara(r, "ptype")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load the target proxy object from router
|
||||||
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the prefix starts with /. If not, prepend it
|
||||||
|
if !strings.HasPrefix(matchingPrefix, "/") {
|
||||||
|
matchingPrefix = "/" + matchingPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add a new exception rule if it is not already exists
|
||||||
|
alreadyExists := false
|
||||||
|
for _, thisExceptionRule := range targetProxy.BasicAuthExceptionRules {
|
||||||
|
if thisExceptionRule.PathPrefix == matchingPrefix {
|
||||||
|
alreadyExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if alreadyExists {
|
||||||
|
utils.SendErrorResponse(w, "This matching path already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetProxy.BasicAuthExceptionRules = append(targetProxy.BasicAuthExceptionRules, &dynamicproxy.BasicAuthExceptionRule{
|
||||||
|
PathPrefix: strings.TrimSpace(matchingPrefix),
|
||||||
|
})
|
||||||
|
|
||||||
|
//Save configs to runtime and file
|
||||||
|
targetProxy.UpdateToRuntime()
|
||||||
|
SaveReverseProxyEndpointToFile(targetProxy)
|
||||||
|
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveProxyBasicAuthExceptionPaths(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Delete a rule
|
||||||
|
ep, err := utils.PostPara(r, "ep")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid ep given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ptype, err := utils.PostPara(r, "ptype")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid ptype given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matchingPrefix, err := utils.PostPara(r, "prefix")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid matching prefix given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the target proxy object from router
|
||||||
|
targetProxy, err := dynamicProxyRouter.LoadProxy(ptype, ep)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newExceptionRuleList := []*dynamicproxy.BasicAuthExceptionRule{}
|
||||||
|
matchingExists := false
|
||||||
|
for _, thisExceptionalRule := range targetProxy.BasicAuthExceptionRules {
|
||||||
|
if thisExceptionalRule.PathPrefix != matchingPrefix {
|
||||||
|
newExceptionRuleList = append(newExceptionRuleList, thisExceptionalRule)
|
||||||
|
} else {
|
||||||
|
matchingExists = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matchingExists {
|
||||||
|
utils.SendErrorResponse(w, "target matching rule not exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetProxy.BasicAuthExceptionRules = newExceptionRuleList
|
||||||
|
|
||||||
|
// Save configs to runtime and file
|
||||||
|
targetProxy.UpdateToRuntime()
|
||||||
|
SaveReverseProxyEndpointToFile(targetProxy)
|
||||||
|
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
|
func ReverseProxyStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
js, _ := json.Marshal(dynamicProxyRouter)
|
js, _ := json.Marshal(dynamicProxyRouter)
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
@ -594,6 +768,35 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle port 80 incoming traffics
|
||||||
|
func HandleUpdatePort80Listener(w http.ResponseWriter, r *http.Request) {
|
||||||
|
enabled, err := utils.GetPara(r, "enable")
|
||||||
|
if err != nil {
|
||||||
|
//Load the current status
|
||||||
|
currentEnabled := false
|
||||||
|
err = sysdb.Read("settings", "listenP80", ¤tEnabled)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
js, _ := json.Marshal(currentEnabled)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
} else {
|
||||||
|
if enabled == "true" {
|
||||||
|
sysdb.Write("settings", "listenP80", true)
|
||||||
|
SystemWideLogger.Println("Enabling port 80 listener")
|
||||||
|
dynamicProxyRouter.UpdatePort80ListenerState(true)
|
||||||
|
} else if enabled == "false" {
|
||||||
|
sysdb.Write("settings", "listenP80", false)
|
||||||
|
SystemWideLogger.Println("Disabling port 80 listener")
|
||||||
|
dynamicProxyRouter.UpdatePort80ListenerState(true)
|
||||||
|
} else {
|
||||||
|
utils.SendErrorResponse(w, "invalid mode given: "+enabled)
|
||||||
|
}
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle https redirect
|
// Handle https redirect
|
||||||
func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
|
func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
useRedirect, err := utils.GetPara(r, "set")
|
useRedirect, err := utils.GetPara(r, "set")
|
||||||
@ -614,11 +817,11 @@ func HandleUpdateHttpsRedirect(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
if useRedirect == "true" {
|
if useRedirect == "true" {
|
||||||
sysdb.Write("settings", "redirect", true)
|
sysdb.Write("settings", "redirect", true)
|
||||||
log.Println("Updating force HTTPS redirection to true")
|
SystemWideLogger.Println("Updating force HTTPS redirection to true")
|
||||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
|
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
|
||||||
} else if useRedirect == "false" {
|
} else if useRedirect == "false" {
|
||||||
sysdb.Write("settings", "redirect", false)
|
sysdb.Write("settings", "redirect", false)
|
||||||
log.Println("Updating force HTTPS redirection to false")
|
SystemWideLogger.Println("Updating force HTTPS redirection to false")
|
||||||
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
|
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,11 +847,18 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
|
newIncomingPortInt, err := strconv.Atoi(newIncomingPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "invalid incoming port given")
|
utils.SendErrorResponse(w, "Invalid incoming port given")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if it is identical as proxy root (recursion!)
|
//Check if it is identical as proxy root (recursion!)
|
||||||
|
if dynamicProxyRouter.Root == nil || dynamicProxyRouter.Root.Domain == "" {
|
||||||
|
//Check if proxy root is set before checking recursive listen
|
||||||
|
//Fixing issue #43
|
||||||
|
utils.SendErrorResponse(w, "Set Proxy Root before changing inbound port")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
|
proxyRoot := strings.TrimSuffix(dynamicProxyRouter.Root.Domain, "/")
|
||||||
if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
|
if strings.HasPrefix(proxyRoot, "localhost:"+strconv.Itoa(newIncomingPortInt)) || strings.HasPrefix(proxyRoot, "127.0.0.1:"+strconv.Itoa(newIncomingPortInt)) {
|
||||||
//Listening port is same as proxy root
|
//Listening port is same as proxy root
|
||||||
@ -671,3 +881,34 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle list of root route options
|
||||||
|
func HandleRootRouteOptionList(w http.ResponseWriter, r *http.Request) {
|
||||||
|
js, _ := json.Marshal(dynamicProxyRouter.RootRoutingOptions)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle update of the root route edge case options. See dynamicproxy/rootRoute.go
|
||||||
|
func HandleRootRouteOptionsUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
enableUnsetSubdomainRedirect, err := utils.PostBool(r, "unsetRedirect")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
unsetRedirectTarget, _ := utils.PostPara(r, "unsetRedirectTarget")
|
||||||
|
|
||||||
|
newRootOption := dynamicproxy.RootRoutingOptions{
|
||||||
|
EnableRedirectForUnsetRules: enableUnsetSubdomainRedirect,
|
||||||
|
UnsetRuleRedirectTarget: unsetRedirectTarget,
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicProxyRouter.RootRoutingOptions = &newRootOption
|
||||||
|
err = newRootOption.SaveToFile()
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
111
src/start.go
@ -14,6 +14,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
"imuslab.com/zoraxy/mod/dynamicproxy/redirection"
|
||||||
"imuslab.com/zoraxy/mod/ganserv"
|
"imuslab.com/zoraxy/mod/ganserv"
|
||||||
"imuslab.com/zoraxy/mod/geodb"
|
"imuslab.com/zoraxy/mod/geodb"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
"imuslab.com/zoraxy/mod/mdns"
|
"imuslab.com/zoraxy/mod/mdns"
|
||||||
"imuslab.com/zoraxy/mod/netstat"
|
"imuslab.com/zoraxy/mod/netstat"
|
||||||
"imuslab.com/zoraxy/mod/pathrule"
|
"imuslab.com/zoraxy/mod/pathrule"
|
||||||
@ -22,6 +23,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/statistic/analytic"
|
"imuslab.com/zoraxy/mod/statistic/analytic"
|
||||||
"imuslab.com/zoraxy/mod/tcpprox"
|
"imuslab.com/zoraxy/mod/tcpprox"
|
||||||
"imuslab.com/zoraxy/mod/tlscert"
|
"imuslab.com/zoraxy/mod/tlscert"
|
||||||
|
"imuslab.com/zoraxy/mod/webserv"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -76,7 +78,10 @@ func startupSequence() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Create a geodb store
|
//Create a geodb store
|
||||||
geodbStore, err = geodb.NewGeoDb(sysdb)
|
geodbStore, err = geodb.NewGeoDb(sysdb, &geodb.StoreOptions{
|
||||||
|
AllowSlowIpv4LookUp: !*enableHighSpeedGeoIPLookup,
|
||||||
|
AllowSloeIpv6Lookup: !*enableHighSpeedGeoIPLookup,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -89,10 +94,17 @@ func startupSequence() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create a system wide logger
|
||||||
|
l, err := logger.NewLogger("zr", "./log", *logOutputToFile)
|
||||||
|
if err == nil {
|
||||||
|
SystemWideLogger = l
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
//Create a netstat buffer
|
//Create a netstat buffer
|
||||||
netstatBuffers, err = netstat.NewNetStatBuffer(300)
|
netstatBuffers, err = netstat.NewNetStatBuffer(300)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to load network statistic info")
|
SystemWideLogger.PrintAndLog("Network", "Failed to load network statistic info", err)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,46 +126,49 @@ func startupSequence() {
|
|||||||
This discover nearby ArozOS Nodes or other services
|
This discover nearby ArozOS Nodes or other services
|
||||||
that provide mDNS discovery with domain (e.g. Synology NAS)
|
that provide mDNS discovery with domain (e.g. Synology NAS)
|
||||||
*/
|
*/
|
||||||
portInt, err := strconv.Atoi(strings.Split(handler.Port, ":")[1])
|
|
||||||
if err != nil {
|
|
||||||
portInt = 8000
|
|
||||||
}
|
|
||||||
mdnsScanner, err = mdns.NewMDNS(mdns.NetworkHost{
|
|
||||||
HostName: "zoraxy_" + nodeUUID,
|
|
||||||
Port: portInt,
|
|
||||||
Domain: "zoraxy.imuslab.com",
|
|
||||||
Model: "Network Gateway",
|
|
||||||
UUID: nodeUUID,
|
|
||||||
Vendor: "imuslab.com",
|
|
||||||
BuildVersion: version,
|
|
||||||
}, "")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start initial scanning
|
if *allowMdnsScanning {
|
||||||
go func() {
|
portInt, err := strconv.Atoi(strings.Split(handler.Port, ":")[1])
|
||||||
hosts := mdnsScanner.Scan(30, "")
|
if err != nil {
|
||||||
previousmdnsScanResults = hosts
|
portInt = 8000
|
||||||
log.Println("mDNS Startup scan completed")
|
}
|
||||||
}()
|
mdnsScanner, err = mdns.NewMDNS(mdns.NetworkHost{
|
||||||
|
HostName: "zoraxy_" + nodeUUID,
|
||||||
//Create a ticker to update mDNS results every 5 minutes
|
Port: portInt,
|
||||||
ticker := time.NewTicker(15 * time.Minute)
|
Domain: "zoraxy.arozos.com",
|
||||||
stopChan := make(chan bool)
|
Model: "Network Gateway",
|
||||||
go func() {
|
UUID: nodeUUID,
|
||||||
for {
|
Vendor: "imuslab.com",
|
||||||
select {
|
BuildVersion: version,
|
||||||
case <-stopChan:
|
}, "")
|
||||||
ticker.Stop()
|
if err != nil {
|
||||||
case <-ticker.C:
|
SystemWideLogger.Println("Unable to startup mDNS service. Disabling mDNS services")
|
||||||
|
} else {
|
||||||
|
//Start initial scanning
|
||||||
|
go func() {
|
||||||
hosts := mdnsScanner.Scan(30, "")
|
hosts := mdnsScanner.Scan(30, "")
|
||||||
previousmdnsScanResults = hosts
|
previousmdnsScanResults = hosts
|
||||||
log.Println("mDNS scan result updated")
|
SystemWideLogger.Println("mDNS Startup scan completed")
|
||||||
}
|
}()
|
||||||
|
|
||||||
|
//Create a ticker to update mDNS results every 5 minutes
|
||||||
|
ticker := time.NewTicker(15 * time.Minute)
|
||||||
|
stopChan := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopChan:
|
||||||
|
ticker.Stop()
|
||||||
|
case <-ticker.C:
|
||||||
|
hosts := mdnsScanner.Scan(30, "")
|
||||||
|
previousmdnsScanResults = hosts
|
||||||
|
SystemWideLogger.Println("mDNS scan result updated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
mdnsTickerStop = stopChan
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
mdnsTickerStop = stopChan
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Global Area Network
|
Global Area Network
|
||||||
@ -164,7 +179,7 @@ func startupSequence() {
|
|||||||
if usingZtAuthToken == "" {
|
if usingZtAuthToken == "" {
|
||||||
usingZtAuthToken, err = ganserv.TryLoadorAskUserForAuthkey()
|
usingZtAuthToken, err = ganserv.TryLoadorAskUserForAuthkey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to load ZeroTier controller API authtoken")
|
SystemWideLogger.Println("Failed to load ZeroTier controller API authtoken")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ganManager = ganserv.NewNetworkManager(&ganserv.NetworkManagerOptions{
|
ganManager = ganserv.NewNetworkManager(&ganserv.NetworkManagerOptions{
|
||||||
@ -197,11 +212,29 @@ func startupSequence() {
|
|||||||
|
|
||||||
Obtaining certificates from ACME Server
|
Obtaining certificates from ACME Server
|
||||||
*/
|
*/
|
||||||
|
//Create a table just to store acme related preferences
|
||||||
|
sysdb.NewTable("acmepref")
|
||||||
acmeHandler = initACME()
|
acmeHandler = initACME()
|
||||||
acmeAutoRenewer, err = acme.NewAutoRenewer("./conf/acme_conf.json", "./conf/certs/", int64(*acmeAutoRenewInterval), acmeHandler)
|
acmeAutoRenewer, err = acme.NewAutoRenewer("./conf/acme_conf.json", "./conf/certs/", int64(*acmeAutoRenewInterval), acmeHandler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Static Web Server
|
||||||
|
|
||||||
|
Start the static web server
|
||||||
|
*/
|
||||||
|
|
||||||
|
staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
|
||||||
|
Sysdb: sysdb,
|
||||||
|
Port: "5487", //Default Port
|
||||||
|
WebRoot: *staticWebServerRoot,
|
||||||
|
EnableDirectoryListing: true,
|
||||||
|
EnableWebDirManager: *allowWebFileManager,
|
||||||
|
})
|
||||||
|
//Restore the web server to previous shutdown state
|
||||||
|
staticWebServer.RestorePreviousState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This sequence start after everything is initialized
|
// This sequence start after everything is initialized
|
||||||
|
@ -308,7 +308,7 @@
|
|||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
<i class="ui info circle icon"></i> IP Address support the following formats
|
<i class="ui info circle icon"></i> IP Address support the following formats
|
||||||
<div class="ui bulleted list">
|
<div class="ui bulleted list">
|
||||||
<div class="item">Fixed IP Address (e.g. 192.128.4.100)</div>
|
<div class="item">Fixed IP Address (e.g. 192.128.4.100 or fe80::210:5aff:feaa:20a2)</div>
|
||||||
<div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
|
<div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
|
||||||
<div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
|
<div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
|
||||||
</div>
|
</div>
|
||||||
@ -625,7 +625,7 @@
|
|||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
<i class="ui info circle icon"></i> IP Address support the following formats
|
<i class="ui info circle icon"></i> IP Address support the following formats
|
||||||
<div class="ui bulleted list">
|
<div class="ui bulleted list">
|
||||||
<div class="item">Fixed IP Address (e.g. 192.128.4.100)</div>
|
<div class="item">Fixed IP Address (e.g. 192.128.4.100 or fe80::210:5aff:feaa:20a2)</div>
|
||||||
<div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
|
<div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
|
||||||
<div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
|
<div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
<style>
|
||||||
|
.expired.certdate{
|
||||||
|
font-weight: bolder;
|
||||||
|
color: #bd001c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.valid.certdate{
|
||||||
|
color: #31c071;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<h2>TLS / SSL Certificates</h2>
|
<h2>TLS / SSL Certificates</h2>
|
||||||
@ -55,19 +65,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<table class="ui sortable unstackable celled table">
|
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em;">
|
||||||
<thead>
|
<table class="ui sortable unstackable celled table">
|
||||||
<tr><th>Domain</th>
|
<thead>
|
||||||
<th>Last Update</th>
|
<tr><th>Domain</th>
|
||||||
<th>Expire At</th>
|
<th>Last Update</th>
|
||||||
<th class="no-sort">Remove</th>
|
<th>Expire At</th>
|
||||||
</tr></thead>
|
<th class="no-sort">Remove</th>
|
||||||
<tbody id="certifiedDomainList">
|
</tr></thead>
|
||||||
|
<tbody id="certifiedDomainList">
|
||||||
</tbody>
|
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button class="ui basic button" onclick="initManagedDomainCertificateList();"><i class="green refresh icon"></i> Refresh List</button>
|
<button class="ui basic button" onclick="initManagedDomainCertificateList();"><i class="green refresh icon"></i> Refresh List</button>
|
||||||
<button class="ui basic button" onclick="openACMEManager();"><i class="yellow refresh icon"></i> Auto Renew (ACME) Settings</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ui message">
|
<div class="ui message">
|
||||||
<h4><i class="info circle icon"></i> Sub-domain Certificates</h4>
|
<h4><i class="info circle icon"></i> Sub-domain Certificates</h4>
|
||||||
@ -75,11 +87,49 @@
|
|||||||
depending on your certificates coverage, you might need to setup them one by one (i.e. having two seperate certificate for <code>a.example.com</code> and <code>b.example.com</code>).<br>
|
depending on your certificates coverage, you might need to setup them one by one (i.e. having two seperate certificate for <code>a.example.com</code> and <code>b.example.com</code>).<br>
|
||||||
If you have a wildcard certificate that covers <code>*.example.com</code>, you can just enter <code>example.com</code> as server name in the form below to add a certificate.
|
If you have a wildcard certificate that covers <code>*.example.com</code>, you can just enter <code>example.com</code> as server name in the form below to add a certificate.
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<h4>Certificate Authority (CA) and Auto Renew (ACME)</h4>
|
||||||
|
<p>Management features regarding CA and ACME</p>
|
||||||
|
<p>The default CA to use when create a new subdomain proxy endpoint with TLS certificate</p>
|
||||||
|
<div class="ui fluid form">
|
||||||
|
<div class="field">
|
||||||
|
<label>Preferred CA</label>
|
||||||
|
<div class="ui selection dropdown" id="defaultCA">
|
||||||
|
<input type="hidden" name="defaultCA">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="default text">Let's Encrypt</div>
|
||||||
|
<div class="menu">
|
||||||
|
<div class="item" data-value="Let's Encrypt">Let's Encrypt</div>
|
||||||
|
<div class="item" data-value="Buypass">Buypass</div>
|
||||||
|
<div class="item" data-value="ZeroSSL">ZeroSSL</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>ACME Email</label>
|
||||||
|
<input id="prefACMEEmail" type="text" placeholder="ACME Email">
|
||||||
|
</div>
|
||||||
|
<button class="ui basic icon button" onclick="saveDefaultCA();"><i class="ui blue save icon"></i> Save Settings</button>
|
||||||
|
</div><br>
|
||||||
|
<h5>Certificate Renew / Generation (ACME) Settings</h5>
|
||||||
|
<div class="ui basic segment">
|
||||||
|
<h4 class="ui header" id="acmeAutoRenewer">
|
||||||
|
<i class="red circle icon"></i>
|
||||||
|
<div class="content">
|
||||||
|
<span id="acmeAutoRenewerStatus">Disabled</span>
|
||||||
|
<div class="sub header">Auto-Renewer Status</div>
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<p>This tool provide you a graphical interface to setup auto certificate renew on your (sub)domains. You can also manually generate a certificate if one of your domain do not have certificate.</p>
|
||||||
|
<button class="ui basic button" onclick="openACMEManager();"><i class="yellow external icon"></i> Open ACME Tool</button>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var uploadPendingPublicKey = undefined;
|
var uploadPendingPublicKey = undefined;
|
||||||
var uploadPendingPrivateKey = undefined;
|
var uploadPendingPrivateKey = undefined;
|
||||||
|
|
||||||
|
$("#defaultCA").dropdown();
|
||||||
|
|
||||||
//Delete the certificate by its domain
|
//Delete the certificate by its domain
|
||||||
function deleteCertificate(domain){
|
function deleteCertificate(domain){
|
||||||
if (confirm("Confirm delete certificate for " + domain + " ?")){
|
if (confirm("Confirm delete certificate for " + domain + " ?")){
|
||||||
@ -100,6 +150,62 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initAcmeStatus(){
|
||||||
|
//Initialize the current default CA options
|
||||||
|
$.get("/api/acme/autoRenew/email", function(data){
|
||||||
|
$("#prefACMEEmail").val(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
$.get("/api/acme/autoRenew/ca", function(data){
|
||||||
|
$("#defaultCA").dropdown("set value", data);
|
||||||
|
});
|
||||||
|
|
||||||
|
$.get("/api/acme/autoRenew/enable", function(data){
|
||||||
|
setACMEEnableStates(data);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//Set the status of the acme enable icon
|
||||||
|
function setACMEEnableStates(enabled){
|
||||||
|
$("#acmeAutoRenewerStatus").text(enabled?"Enabled":"Disabled");
|
||||||
|
$("#acmeAutoRenewer").find("i").attr("class", enabled?"green circle icon":"red circle icon");
|
||||||
|
}
|
||||||
|
initAcmeStatus();
|
||||||
|
|
||||||
|
function saveDefaultCA(){
|
||||||
|
let newDefaultEmail = $("#prefACMEEmail").val().trim();
|
||||||
|
let newDefaultCA = $("#defaultCA").dropdown("get value");
|
||||||
|
|
||||||
|
if (newDefaultEmail == ""){
|
||||||
|
msgbox("Invalid acme email given", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/acme/autoRenew/email",
|
||||||
|
method: "POST",
|
||||||
|
data: {"set": newDefaultEmail},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/acme/autoRenew/ca",
|
||||||
|
data: {"set": newDefaultCA},
|
||||||
|
method: "POST",
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
msgbox("Settings updated");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//List the stored certificates
|
//List the stored certificates
|
||||||
function initManagedDomainCertificateList(){
|
function initManagedDomainCertificateList(){
|
||||||
$.get("/api/cert/list?date=true", function(data){
|
$.get("/api/cert/list?date=true", function(data){
|
||||||
@ -111,10 +217,12 @@
|
|||||||
return a.Domain > b.Domain
|
return a.Domain > b.Domain
|
||||||
});
|
});
|
||||||
data.forEach(entry => {
|
data.forEach(entry => {
|
||||||
|
let isExpired = entry.RemainingDays <= 0;
|
||||||
|
|
||||||
$("#certifiedDomainList").append(`<tr>
|
$("#certifiedDomainList").append(`<tr>
|
||||||
<td>${entry.Domain}</td>
|
<td>${entry.Domain}</td>
|
||||||
<td>${entry.LastModifiedDate}</td>
|
<td>${entry.LastModifiedDate}</td>
|
||||||
<td>${entry.ExpireDate} (${entry.RemainingDays} days left)</td>
|
<td class="${isExpired?"expired":"valid"} certdate">${entry.ExpireDate} (${!isExpired?entry.RemainingDays+" days left":"Expired"})</td>
|
||||||
<td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td>
|
<td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td>
|
||||||
</tr>`);
|
</tr>`);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<h2>Set Proxy Root</h2>
|
<h2>Set Proxy Root</h2>
|
||||||
<p>For all routing not found in the proxy rules, request will be redirected to the proxy root server.</p>
|
<p>The default routing point for all incoming traffics. For all routing not found in the proxy rules, request will be redirected to the proxy root server.</p>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Proxy Root</label>
|
<label>Proxy Root</label>
|
||||||
@ -10,17 +10,86 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" id="rootReqTLS" >
|
<input type="checkbox" id="rootReqTLS">
|
||||||
<label>Root require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
|
<label>Root require TLS connection <br><small>Check this if your proxy root URL starts with https://</small></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui horizontal divider">OR</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="useStaticWebServer" onchange="handleUseStaticWebServerAsRoot()">
|
||||||
|
<label>Use Static Web Server as Root <br><small>Check this if you prefer a more Apache Web Server like experience</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<button class="ui basic button" onclick="setProxyRoot()"><i class="teal home icon" ></i> Update Proxy Root</button>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="field">
|
||||||
|
<h4>Root Routing Options</h4>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="unsetRedirect">
|
||||||
|
<label>Enable redirect for unset subdomains <br><small>Redirect subdomain that is not found to custom domain</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui basic segment" id="unsetRedirectDomainWrapper" style="background-color: #f7f7f7; border-radius: 1em; margin-left: 2em; padding-left: 2em; display:none;">
|
||||||
|
<div style="
|
||||||
|
position: absolute;
|
||||||
|
top:0;
|
||||||
|
left: 1em;
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
margin-top: -10px;
|
||||||
|
border-left: 10px solid transparent;
|
||||||
|
border-right: 10px solid transparent;
|
||||||
|
border-bottom: 10px solid #f7f7f7;">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Redirect target domain</label>
|
||||||
|
<div class="ui input">
|
||||||
|
<input id="unsetRedirectDomain" type="text" placeholder="http://example.com">
|
||||||
|
</div>
|
||||||
|
<small>Unset subdomain will be redirected to the link above. Remember to include the protocol (e.g. http:// or https://)<br>
|
||||||
|
Leave empty for redirecting to upper level domain (e.g. notfound.example.com <i class="right arrow icon"></i> example.com)</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<button class="ui basic button" onclick="updateRootOptions()"><i class="blue save icon" ></i> Save Root Options</button>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<button class="ui basic button" onclick="setProxyRoot()"><i class="teal home icon" ></i> Update Proxy Root</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
function initRootInfo(){
|
$("#advanceRootSettings").accordion();
|
||||||
|
|
||||||
|
function handleUseStaticWebServerAsRoot(){
|
||||||
|
let useStaticWebServer = $("#useStaticWebServer")[0].checked;
|
||||||
|
if (useStaticWebServer){
|
||||||
|
let staticWebServerURL = "127.0.0.1:" + $("#webserv_listenPort").val();
|
||||||
|
$("#proxyRoot").val(staticWebServerURL);
|
||||||
|
$("#proxyRoot").parent().addClass("disabled");
|
||||||
|
$("#rootReqTLS").parent().checkbox("set unchecked");
|
||||||
|
$("#rootReqTLS").parent().addClass("disabled");
|
||||||
|
|
||||||
|
//Check if web server is enabled. If not, ask if the user want to enable it
|
||||||
|
/*if (!$("#webserv_enable").parent().checkbox("is checked")){
|
||||||
|
confirmBox("Enable static web server now?", function(choice){
|
||||||
|
if (choice == true){
|
||||||
|
$("#webserv_enable").parent().checkbox("set checked");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
}else{
|
||||||
|
$("#rootReqTLS").parent().removeClass("disabled");
|
||||||
|
$("#proxyRoot").parent().removeClass("disabled");
|
||||||
|
initRootInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initRootInfo(callback=undefined){
|
||||||
$.get("/api/proxy/list?type=root", function(data){
|
$.get("/api/proxy/list?type=root", function(data){
|
||||||
if (data == null){
|
if (data == null){
|
||||||
|
|
||||||
@ -28,11 +97,91 @@
|
|||||||
$("#proxyRoot").val(data.Domain);
|
$("#proxyRoot").val(data.Domain);
|
||||||
checkRootRequireTLS(data.Domain);
|
checkRootRequireTLS(data.Domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (callback != undefined){
|
||||||
|
callback();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
initRootInfo();
|
initRootInfo(function(){
|
||||||
|
updateWebServerLinkSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Update the current web server port settings
|
||||||
|
function updateWebServerLinkSettings(){
|
||||||
|
isUsingStaticWebServerAsRoot(function(isUsingWebServ){
|
||||||
|
if (isUsingWebServ){
|
||||||
|
$(".webservRootDisabled").addClass("disabled");
|
||||||
|
$("#useStaticWebServer").parent().checkbox("set checked");
|
||||||
|
}else{
|
||||||
|
$(".webservRootDisabled").removeClass("disabled");
|
||||||
|
$("#useStaticWebServer").parent().checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUsingStaticWebServerAsRoot(callback){
|
||||||
|
let currentProxyRoot = $("#proxyRoot").val().trim();
|
||||||
|
$.get("/api/webserv/status", function(webservStatus){
|
||||||
|
if (currentProxyRoot == "127.0.0.1:" + webservStatus.ListeningPort || currentProxyRoot == "localhost:" + webservStatus.ListeningPort){
|
||||||
|
return callback(true);
|
||||||
|
}
|
||||||
|
return callback(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRootSettingStates(){
|
||||||
|
$.get("/api/cert/tls", function(data){
|
||||||
|
if (data == true){
|
||||||
|
$("#disableRootTLS").parent().removeClass('disabled').attr("title", "");
|
||||||
|
}else{
|
||||||
|
$("#disableRootTLS").parent().addClass('disabled').attr("title", "TLS listener is not enabled");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bind event to tab switch
|
||||||
|
tabSwitchEventBind["setroot"] = function(){
|
||||||
|
//On switch over to this page, update root info
|
||||||
|
updateRootSettingStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Toggle the display status of the input box for domain setting
|
||||||
|
function updateRedirectionDomainSettingInputBox(useRedirect){
|
||||||
|
if(useRedirect){
|
||||||
|
$("#unsetRedirectDomainWrapper").stop().finish().slideDown("fast");
|
||||||
|
}else{
|
||||||
|
$("#unsetRedirectDomainWrapper").stop().finish().slideUp("fast");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCustomRedirectForUnsetSubd(){
|
||||||
|
$.get("/api/proxy/root/listOptions", function(data){
|
||||||
|
$("#unsetRedirect")[0].checked = data.EnableRedirectForUnsetRules || false;
|
||||||
|
$("#unsetRedirectDomain").val(data.UnsetRuleRedirectTarget);
|
||||||
|
updateRedirectionDomainSettingInputBox(data.EnableRedirectForUnsetRules);
|
||||||
|
|
||||||
|
//Bind event to the checkbox
|
||||||
|
$("#unsetRedirect").off("change").on("change", function(){
|
||||||
|
let useRedirect = $("#unsetRedirect")[0].checked;
|
||||||
|
updateRedirectionDomainSettingInputBox(useRedirect);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
checkCustomRedirectForUnsetSubd();
|
||||||
|
|
||||||
|
//Check if the given domain will redirect to https
|
||||||
function checkRootRequireTLS(targetDomain){
|
function checkRootRequireTLS(targetDomain){
|
||||||
|
//Trim off the http or https from the origin
|
||||||
|
if (targetDomain.startsWith("http://")){
|
||||||
|
targetDomain = targetDomain.substring(7);
|
||||||
|
$("#proxyRoot").val(targetDomain);
|
||||||
|
}else if (targetDomain.startsWith("https://")){
|
||||||
|
targetDomain = targetDomain.substring(8);
|
||||||
|
$("#proxyRoot").val(targetDomain);
|
||||||
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/proxy/tlscheck",
|
url: "/api/proxy/tlscheck",
|
||||||
data: {url: targetDomain},
|
data: {url: targetDomain},
|
||||||
@ -44,11 +193,13 @@
|
|||||||
}else if (data == "http"){
|
}else if (data == "http"){
|
||||||
$("#rootReqTLS").parent().checkbox("set unchecked");
|
$("#rootReqTLS").parent().checkbox("set unchecked");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Set the new proxy root option
|
||||||
function setProxyRoot(){
|
function setProxyRoot(){
|
||||||
var newpr = $("#proxyRoot").val();
|
var newpr = $("#proxyRoot").val();
|
||||||
if (newpr.trim() == ""){
|
if (newpr.trim() == ""){
|
||||||
@ -66,14 +217,51 @@
|
|||||||
data: {"type": "root", tls: rootReqTls, ep: newpr},
|
data: {"type": "root", tls: rootReqTls, ep: newpr},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
alert(data.error);
|
msgbox(data.error, false, 5000);
|
||||||
}else{
|
}else{
|
||||||
//OK
|
//OK
|
||||||
initRootInfo();
|
initRootInfo(function(){
|
||||||
msgbox("Proxy Root Updated")
|
//Check if WebServ is enabled
|
||||||
|
isUsingStaticWebServerAsRoot(function(isUsingWebServ){
|
||||||
|
if (isUsingWebServ){
|
||||||
|
//Force enable static web server
|
||||||
|
//See webserv.html for details
|
||||||
|
setWebServerRunningState(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
//Update the checkbox
|
||||||
|
updateWebServerLinkSettings();
|
||||||
|
msgbox("Proxy Root Updated");
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateRootOptions(){
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/api/proxy/root/updateOptions",
|
||||||
|
data: {
|
||||||
|
unsetRedirect: $("#unsetRedirect")[0].checked,
|
||||||
|
unsetRedirectTarget: $("#unsetRedirectDomain").val().trim(),
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}else{
|
||||||
|
msgbox("Root Routing Options updated");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
console.log("Error:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
@ -8,7 +8,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Proxy Type</label>
|
<label>Proxy Type</label>
|
||||||
<div class="ui selection dropdown">
|
<div class="ui selection dropdown">
|
||||||
<input type="hidden" id="ptype" value="subd">
|
<input type="hidden" id="ptype" value="subd" onchange="handleProxyTypeOptionChange(this.value)">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
<div class="default text">Proxy Type</div>
|
<div class="default text">Proxy Type</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
@ -22,7 +22,7 @@
|
|||||||
<input type="text" id="rootname" placeholder="s1.mydomain.com">
|
<input type="text" id="rootname" placeholder="s1.mydomain.com">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>IP Address or Domain Name with port</label>
|
<label>Target IP Address or Domain Name with port</label>
|
||||||
<input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
|
<input type="text" id="proxyDomain" onchange="autoCheckTls(this.value);">
|
||||||
<small>E.g. 192.168.0.101:8000 or example.com</small>
|
<small>E.g. 192.168.0.101:8000 or example.com</small>
|
||||||
</div>
|
</div>
|
||||||
@ -44,7 +44,13 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" id="skipTLSValidation">
|
<input type="checkbox" id="skipTLSValidation">
|
||||||
<label>Ignore TLS/SSL Verification Error<br><small>E.g. self-signed, expired certificate (Not Recommended)</small></label>
|
<label>Ignore TLS/SSL Verification Error<br><small>For targets that is using self-signed, expired certificate (Not Recommended)</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="bypassGlobalTLS">
|
||||||
|
<label>Allow plain HTTP access<br><small>Allow this subdomain to be connected without TLS (Require HTTP server enabled on port 80)</small></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@ -123,6 +129,7 @@
|
|||||||
var proxyDomain = $("#proxyDomain").val();
|
var proxyDomain = $("#proxyDomain").val();
|
||||||
var useTLS = $("#reqTls")[0].checked;
|
var useTLS = $("#reqTls")[0].checked;
|
||||||
var skipTLSValidation = $("#skipTLSValidation")[0].checked;
|
var skipTLSValidation = $("#skipTLSValidation")[0].checked;
|
||||||
|
var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
||||||
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
||||||
|
|
||||||
if (type === "vdir") {
|
if (type === "vdir") {
|
||||||
@ -162,6 +169,7 @@
|
|||||||
tls: useTLS,
|
tls: useTLS,
|
||||||
ep: proxyDomain,
|
ep: proxyDomain,
|
||||||
tlsval: skipTLSValidation,
|
tlsval: skipTLSValidation,
|
||||||
|
bypassGlobalTLS: bypassGlobalTLS,
|
||||||
bauth: requireBasicAuth,
|
bauth: requireBasicAuth,
|
||||||
cred: JSON.stringify(credentials),
|
cred: JSON.stringify(credentials),
|
||||||
},
|
},
|
||||||
@ -172,19 +180,48 @@
|
|||||||
//OK
|
//OK
|
||||||
listVdirs();
|
listVdirs();
|
||||||
listSubd();
|
listSubd();
|
||||||
msgbox("Proxy Endpoint Added");
|
|
||||||
|
|
||||||
//Clear old data
|
//Clear old data
|
||||||
$("#rootname").val("");
|
$("#rootname").val("");
|
||||||
$("#proxyDomain").val("");
|
$("#proxyDomain").val("");
|
||||||
credentials = [];
|
credentials = [];
|
||||||
updateTable();
|
updateTable();
|
||||||
|
|
||||||
|
//Check if it is a new subdomain and TLS enabled
|
||||||
|
if (type == "subd" && $("#tls").checkbox("is checked")){
|
||||||
|
confirmBox("Request new SSL Cert for this subdomain?", function(choice){
|
||||||
|
if (choice == true){
|
||||||
|
//Load the prefer CA from TLS page
|
||||||
|
let defaultCA = $("#defaultCA").dropdown("get value");
|
||||||
|
if (defaultCA.trim() == ""){
|
||||||
|
defaultCA = "Let's Encrypt";
|
||||||
|
}
|
||||||
|
//Get a new cert using ACME
|
||||||
|
msgbox("Requesting certificate via " + defaultCA +"...");
|
||||||
|
console.log("Trying to get a new certificate via ACME");
|
||||||
|
obtainCertificate(rootname, defaultCA.trim());
|
||||||
|
}else{
|
||||||
|
msgbox("Proxy Endpoint Added");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
msgbox("Proxy Endpoint Added");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleProxyTypeOptionChange(newType){
|
||||||
|
if (newType == "subd"){
|
||||||
|
$("#bypassGlobalTLS").parent().removeClass("disabled");
|
||||||
|
}else if (newType == "vdir"){
|
||||||
|
$("#bypassGlobalTLS").parent().addClass("disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Generic functions for delete rp endpoints
|
//Generic functions for delete rp endpoints
|
||||||
function deleteEndpoint(ptype, epoint){
|
function deleteEndpoint(ptype, epoint){
|
||||||
if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
|
if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
|
||||||
@ -310,7 +347,7 @@
|
|||||||
var columns = row.find('td[data-label]');
|
var columns = row.find('td[data-label]');
|
||||||
var payload = $(row).attr("payload");
|
var payload = $(row).attr("payload");
|
||||||
payload = JSON.parse(decodeURIComponent(payload));
|
payload = JSON.parse(decodeURIComponent(payload));
|
||||||
|
console.log(payload);
|
||||||
//console.log(payload);
|
//console.log(payload);
|
||||||
columns.each(function(index) {
|
columns.each(function(index) {
|
||||||
var column = $(this);
|
var column = $(this);
|
||||||
@ -326,34 +363,37 @@
|
|||||||
var datatype = $(this).attr("datatype");
|
var datatype = $(this).attr("datatype");
|
||||||
if (datatype == "domain"){
|
if (datatype == "domain"){
|
||||||
let domain = payload.Domain;
|
let domain = payload.Domain;
|
||||||
|
//Target require TLS for proxying
|
||||||
let tls = payload.RequireTLS;
|
let tls = payload.RequireTLS;
|
||||||
if (tls){
|
if (tls){
|
||||||
tls = "checked";
|
tls = "checked";
|
||||||
}else{
|
}else{
|
||||||
tls = "";
|
tls = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Require TLS validation
|
||||||
|
let skipTLSValidation = payload.SkipCertValidations;
|
||||||
|
let checkstate = "";
|
||||||
|
if (skipTLSValidation){
|
||||||
|
checkstate = "checked";
|
||||||
|
}
|
||||||
|
|
||||||
input = `
|
input = `
|
||||||
<div class="ui mini fluid input">
|
<div class="ui mini fluid input">
|
||||||
<input type="text" class="Domain" value="${domain}">
|
<input type="text" class="Domain" value="${domain}">
|
||||||
</div>
|
</div>
|
||||||
<div class="ui checkbox" style="margin-top: 0.4em;">
|
<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||||
<input type="checkbox" class="RequireTLS" ${tls}>
|
<input type="checkbox" class="RequireTLS" ${tls}>
|
||||||
<label>Require TLS</label>
|
<label>Require TLS<br>
|
||||||
|
<small>Proxy target require HTTPS connection</small></label>
|
||||||
|
</div><br>
|
||||||
|
<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||||
|
<input type="checkbox" class="SkipCertValidations" ${checkstate}>
|
||||||
|
<label>Skip Verification<br>
|
||||||
|
<small>Check this if proxy target is using self signed certificates</small></label>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
column.empty().append(input);
|
column.empty().append(input);
|
||||||
|
|
||||||
}else if (datatype == "skipver"){
|
|
||||||
let skipTLSValidation = payload.SkipCertValidations;
|
|
||||||
let checkstate = "";
|
|
||||||
if (skipTLSValidation){
|
|
||||||
checkstate = "checked";
|
|
||||||
}
|
|
||||||
column.empty().append(`<div class="ui checkbox" style="margin-top: 0.4em;">
|
|
||||||
<input type="checkbox" class="SkipCertValidations" ${checkstate}>
|
|
||||||
<label>Skip Verification</label>
|
|
||||||
<small>Check this if you are using self signed certificates</small>
|
|
||||||
</div>`);
|
|
||||||
}else if (datatype == "basicauth"){
|
}else if (datatype == "basicauth"){
|
||||||
let requireBasicAuth = payload.RequireBasicAuth;
|
let requireBasicAuth = payload.RequireBasicAuth;
|
||||||
let checkstate = "";
|
let checkstate = "";
|
||||||
@ -363,13 +403,24 @@
|
|||||||
column.empty().append(`<div class="ui checkbox" style="margin-top: 0.4em;">
|
column.empty().append(`<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||||
<input type="checkbox" class="RequireBasicAuth" ${checkstate}>
|
<input type="checkbox" class="RequireBasicAuth" ${checkstate}>
|
||||||
<label>Require Basic Auth</label>
|
<label>Require Basic Auth</label>
|
||||||
</div> <button class="ui basic tiny button" style="margin-left: 0.4em;" onclick="editBasicAuthCredentials('${endpointType}','${uuid}');"><i class="ui blue lock icon"></i> Edit Credentials</button>`);
|
</div>
|
||||||
|
<button class="ui basic tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editBasicAuthCredentials('${endpointType}','${uuid}');"><i class="ui blue lock icon"></i> Edit Settings</button>`);
|
||||||
|
|
||||||
}else if (datatype == 'action'){
|
}else if (datatype == 'action'){
|
||||||
column.empty().append(`
|
column.empty().append(`
|
||||||
<button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
|
<button title="Cancel" onclick="exitProxyInlineEdit('${endpointType}');" class="ui basic small circular icon button"><i class="ui remove icon"></i></button>
|
||||||
<button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
|
<button title="Save" onclick="saveProxyInlineEdit('${uuid}');" class="ui basic small circular icon button"><i class="ui green save icon"></i></button>
|
||||||
`);
|
`);
|
||||||
|
}else if (datatype == "inbound" && payload.ProxyType == 0){
|
||||||
|
let originalContent = $(column).html();
|
||||||
|
column.empty().append(`${originalContent}
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="ui checkbox" style="margin-top: 0.4em;">
|
||||||
|
<input type="checkbox" class="BypassGlobalTLS" ${payload.BypassGlobalTLS?"checked":""}>
|
||||||
|
<label>Allow plain HTTP access<br>
|
||||||
|
<small>Allow inbound connections without TLS/SSL</small></label>
|
||||||
|
</div><br>
|
||||||
|
`);
|
||||||
}else{
|
}else{
|
||||||
//Unknown field. Leave it untouched
|
//Unknown field. Leave it untouched
|
||||||
}
|
}
|
||||||
@ -401,6 +452,7 @@
|
|||||||
let requireTLS = $(row).find(".RequireTLS")[0].checked;
|
let requireTLS = $(row).find(".RequireTLS")[0].checked;
|
||||||
let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked;
|
let skipCertValidations = $(row).find(".SkipCertValidations")[0].checked;
|
||||||
let requireBasicAuth = $(row).find(".RequireBasicAuth")[0].checked;
|
let requireBasicAuth = $(row).find(".RequireBasicAuth")[0].checked;
|
||||||
|
let bypassGlobalTLS = $(row).find(".BypassGlobalTLS")[0].checked;
|
||||||
|
|
||||||
console.log(newDomain, requireTLS, skipCertValidations, requireBasicAuth)
|
console.log(newDomain, requireTLS, skipCertValidations, requireBasicAuth)
|
||||||
|
|
||||||
@ -411,6 +463,7 @@
|
|||||||
"type": epttype,
|
"type": epttype,
|
||||||
"rootname": uuid,
|
"rootname": uuid,
|
||||||
"ep":newDomain,
|
"ep":newDomain,
|
||||||
|
"bpgtls": bypassGlobalTLS,
|
||||||
"tls" :requireTLS,
|
"tls" :requireTLS,
|
||||||
"tlsval": skipCertValidations,
|
"tlsval": skipCertValidations,
|
||||||
"bauth" :requireBasicAuth,
|
"bauth" :requireBasicAuth,
|
||||||
@ -437,4 +490,67 @@
|
|||||||
}));
|
}));
|
||||||
showSideWrapper("snippet/basicAuthEditor.html?t=" + Date.now() + "#" + payload);
|
showSideWrapper("snippet/basicAuthEditor.html?t=" + Date.now() + "#" + payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Obtain Certificate via ACME
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Load the ACME email from server side
|
||||||
|
let acmeEmail = "";
|
||||||
|
$.get("/api/acme/autoRenew/email", function(data){
|
||||||
|
if (data != "" && data != undefined && data != null){
|
||||||
|
acmeEmail = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Obtain certificate from API, only support one domain
|
||||||
|
function obtainCertificate(domains, usingCa = "Let's Encrypt") {
|
||||||
|
let filename = "";
|
||||||
|
let email = acmeEmail;
|
||||||
|
if (acmeEmail == ""){
|
||||||
|
let rootDomain = domains.split(".").pop();
|
||||||
|
email = "admin@" + rootDomain;
|
||||||
|
}
|
||||||
|
if (filename.trim() == "" && !domains.includes(",")){
|
||||||
|
//Zoraxy filename are the matching name for domains.
|
||||||
|
//Use the same as domains
|
||||||
|
filename = domains;
|
||||||
|
}else if (filename != "" && !domains.includes(",")){
|
||||||
|
//Invalid settings. Force the filename to be same as domain
|
||||||
|
//if there are only 1 domain
|
||||||
|
filename = domains;
|
||||||
|
}else{
|
||||||
|
parent.msgbox("Filename cannot be empty for certs containing multiple domains.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/acme/obtainCert",
|
||||||
|
method: "GET",
|
||||||
|
data: {
|
||||||
|
domains: domains,
|
||||||
|
filename: filename,
|
||||||
|
email: email,
|
||||||
|
ca: usingCa,
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.error) {
|
||||||
|
console.log("Error:", response.error);
|
||||||
|
// Show error message
|
||||||
|
msgbox(response.error, false, 12000);
|
||||||
|
} else {
|
||||||
|
console.log("Certificate installed successfully");
|
||||||
|
// Show success message
|
||||||
|
msgbox("Certificate installed successfully");
|
||||||
|
|
||||||
|
// Renew the parent certificate list
|
||||||
|
initManagedDomainCertificateList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(error) {
|
||||||
|
console.log("Failed to install certificate:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
@ -72,10 +72,15 @@
|
|||||||
<label>Use TLS to serve proxy request</label>
|
<label>Use TLS to serve proxy request</label>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;">
|
<div id="listenP80" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em;" >
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<label>Force redirect HTTP request to HTTPS<br>
|
<label>Enable HTTP server on port 80<br>
|
||||||
<small>(Only apply when listening port is not 80)</small></label>
|
<small>(Only apply when TLS enabled and not using port 80)</small></label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div id="redirect" class="ui toggle notloopbackOnly tlsEnabledOnly checkbox" style="margin-top: 0.6em; padding-left: 2em;">
|
||||||
|
<input type="checkbox">
|
||||||
|
<label>Force redirect HTTP request to HTTPS</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
|
||||||
<div class="ui accordion advanceSettings">
|
<div class="ui accordion advanceSettings">
|
||||||
@ -181,6 +186,7 @@
|
|||||||
$("#serverstatus").removeClass("green");
|
$("#serverstatus").removeClass("green");
|
||||||
}
|
}
|
||||||
$("#incomingPort").val(data.Option.Port);
|
$("#incomingPort").val(data.Option.Port);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -305,6 +311,27 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleP80ListenerStateChange(enabled){
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/proxy/listenPort80",
|
||||||
|
data: {"enable": enabled},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
console.log(data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (enabled){
|
||||||
|
$("#redirect").show();
|
||||||
|
msgbox("Port 80 listener enabled");
|
||||||
|
}else{
|
||||||
|
$("#redirect").hide();
|
||||||
|
msgbox("Port 80 listener disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function handlePortChange(){
|
function handlePortChange(){
|
||||||
var newPortValue = $("#incomingPort").val();
|
var newPortValue = $("#incomingPort").val();
|
||||||
@ -323,6 +350,25 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initPort80ListenerSetting(){
|
||||||
|
$.get("/api/proxy/listenPort80", function(data){
|
||||||
|
if (data){
|
||||||
|
$("#listenP80").checkbox("set checked");
|
||||||
|
$("#redirect").show();
|
||||||
|
}else{
|
||||||
|
$("#listenP80").checkbox("set unchecked");
|
||||||
|
$("#redirect").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#listenP80").find("input").on("change", function(){
|
||||||
|
let enabled = $(this)[0].checked;
|
||||||
|
handleP80ListenerStateChange(enabled);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
initPort80ListenerSetting();
|
||||||
|
|
||||||
function initHTTPtoHTTPSRedirectSetting(){
|
function initHTTPtoHTTPSRedirectSetting(){
|
||||||
$.get("/api/proxy/useHttpsRedirect", function(data){
|
$.get("/api/proxy/useHttpsRedirect", function(data){
|
||||||
if (data == true){
|
if (data == true){
|
||||||
@ -356,8 +402,6 @@
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
initHTTPtoHTTPSRedirectSetting();
|
initHTTPtoHTTPSRedirectSetting();
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Matching Domain</th>
|
<th>Matching Domain</th>
|
||||||
<th>Proxy To</th>
|
<th>Proxy To</th>
|
||||||
<th>TLS/SSL Verification</th>
|
|
||||||
<th>Basic Auth</th>
|
<th>Basic Auth</th>
|
||||||
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -41,19 +40,14 @@
|
|||||||
let subdData = encodeURIComponent(JSON.stringify(subd));
|
let subdData = encodeURIComponent(JSON.stringify(subd));
|
||||||
if (subd.RequireTLS){
|
if (subd.RequireTLS){
|
||||||
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
||||||
}
|
if (subd.SkipCertValidations){
|
||||||
|
tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
|
||||||
let tlsVerificationField = "";
|
}
|
||||||
if (subd.RequireTLS){
|
|
||||||
tlsVerificationField = !subd.SkipCertValidations?`<i class="ui green check icon"></i>`:`<i class="ui yellow exclamation circle icon" title="TLS/SSL Verification will be skipped on this host"></i>`
|
|
||||||
}else{
|
|
||||||
tlsVerificationField = "N/A"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#subdList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
$("#subdList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
||||||
<td data-label="" editable="false"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a></td>
|
<td data-label="" editable="true" datatype="inbound"><a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a></td>
|
||||||
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
||||||
<td data-label="" editable="true" datatype="skipver">${tlsVerificationField}</td>
|
|
||||||
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
<td data-label="" editable="true" datatype="basicauth">${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
||||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
<td class="center aligned" editable="true" datatype="action" data-label="">
|
||||||
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("subd","${subd.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
||||||
|
@ -86,7 +86,7 @@
|
|||||||
|
|
||||||
let id = value[0].ID;
|
let id = value[0].ID;
|
||||||
let name = value[0].Name;
|
let name = value[0].Name;
|
||||||
let url = value[0].URL;
|
let url = value[value.length - 1].URL;
|
||||||
let protocol = value[0].Protocol;
|
let protocol = value[0].Protocol;
|
||||||
|
|
||||||
//Generate the status dot
|
//Generate the status dot
|
||||||
@ -112,6 +112,9 @@
|
|||||||
if (thisStatus.StatusCode >= 500 && thisStatus.StatusCode < 600){
|
if (thisStatus.StatusCode >= 500 && thisStatus.StatusCode < 600){
|
||||||
//Special type of error, cause by downstream reverse proxy
|
//Special type of error, cause by downstream reverse proxy
|
||||||
dotType = "error";
|
dotType = "error";
|
||||||
|
}else if (thisStatus.StatusCode == 401){
|
||||||
|
//Unauthorized error
|
||||||
|
dotType = "error";
|
||||||
}else{
|
}else{
|
||||||
dotType = "offline";
|
dotType = "offline";
|
||||||
}
|
}
|
||||||
@ -141,6 +144,28 @@
|
|||||||
currentOnlineStatus = `<i class="exclamation circle icon"></i> Misconfigured`;
|
currentOnlineStatus = `<i class="exclamation circle icon"></i> Misconfigured`;
|
||||||
onlineStatusCss = `color: #f38020;`;
|
onlineStatusCss = `color: #f38020;`;
|
||||||
reminderEle = `<small style="${onlineStatusCss}">Downstream proxy server is online with misconfigured settings</small>`;
|
reminderEle = `<small style="${onlineStatusCss}">Downstream proxy server is online with misconfigured settings</small>`;
|
||||||
|
}else if (value[value.length - 1].StatusCode >= 400 && value[value.length - 1].StatusCode <= 405){
|
||||||
|
switch(value[value.length - 1].StatusCode){
|
||||||
|
case 400:
|
||||||
|
currentOnlineStatus = `<i class="exclamation circle icon"></i> Bad Request`;
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
currentOnlineStatus = `<i class="exclamation circle icon"></i> Unauthorized`;
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
currentOnlineStatus = `<i class="exclamation circle icon"></i> Forbidden`;
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
currentOnlineStatus = `<i class="exclamation circle icon"></i> Not Found`;
|
||||||
|
break;
|
||||||
|
case 405:
|
||||||
|
currentOnlineStatus = `<i class="exclamation circle icon"></i> Method Not Allowed`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
onlineStatusCss = `color: #f38020;`;
|
||||||
|
reminderEle = `<small style="${onlineStatusCss}">Target online but not accessible</small>`;
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
currentOnlineStatus = `<i class="circle icon"></i> Offline`;
|
currentOnlineStatus = `<i class="circle icon"></i> Offline`;
|
||||||
onlineStatusCss = `color: #df484a;`;
|
onlineStatusCss = `color: #df484a;`;
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Virtual Directory</th>
|
<th>Virtual Directory</th>
|
||||||
<th>Proxy To</th>
|
<th>Proxy To</th>
|
||||||
<th>TLS/SSL Verification</th>
|
|
||||||
<th>Basic Auth</th>
|
<th>Basic Auth</th>
|
||||||
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
<th class="no-sort" style="min-width: 7.2em;">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -43,6 +42,9 @@
|
|||||||
let vdirData = encodeURIComponent(JSON.stringify(vdir));
|
let vdirData = encodeURIComponent(JSON.stringify(vdir));
|
||||||
if (vdir.RequireTLS){
|
if (vdir.RequireTLS){
|
||||||
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
||||||
|
if (vdir.SkipCertValidations){
|
||||||
|
tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tlsVerificationField = "";
|
let tlsVerificationField = "";
|
||||||
@ -55,7 +57,6 @@
|
|||||||
$("#vdirList").append(`<tr eptuuid="${vdir.RootOrMatchingDomain}" payload="${vdirData}" class="vdirEntry">
|
$("#vdirList").append(`<tr eptuuid="${vdir.RootOrMatchingDomain}" payload="${vdirData}" class="vdirEntry">
|
||||||
<td data-label="" editable="false">${vdir.RootOrMatchingDomain}</td>
|
<td data-label="" editable="false">${vdir.RootOrMatchingDomain}</td>
|
||||||
<td data-label="" editable="true" datatype="domain">${vdir.Domain} ${tlsIcon}</td>
|
<td data-label="" editable="true" datatype="domain">${vdir.Domain} ${tlsIcon}</td>
|
||||||
<td data-label="" editable="true" datatype="skipver">${tlsVerificationField}</td>
|
|
||||||
<td data-label="" editable="true" datatype="basicauth">${vdir.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
<td data-label="" editable="true" datatype="basicauth">${vdir.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}</td>
|
||||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
<td class="center aligned" editable="true" datatype="action" data-label="">
|
||||||
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("vdir","${vdir.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
<button class="ui circular mini basic icon button editBtn" onclick='editEndpoint("vdir","${vdir.RootOrMatchingDomain}")'><i class="edit icon"></i></button>
|
||||||
|
229
src/web/components/webserv.html
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<div class="standardContainer">
|
||||||
|
<div class="ui basic segment">
|
||||||
|
<h2>Static Web Server</h2>
|
||||||
|
<p>A simple static web server that serve html css and js files</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="ui basic segment">
|
||||||
|
<h4 class="ui header" id="webservRunningState">
|
||||||
|
<i class="green circle icon"></i>
|
||||||
|
<div class="content">
|
||||||
|
<span class="webserv_status">Running</span>
|
||||||
|
<div class="sub header">Listen port :<span class="webserv_port">8081</span></div>
|
||||||
|
</div>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Web Server Settings</h3>
|
||||||
|
<div class="ui form">
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui toggle checkbox webservRootDisabled">
|
||||||
|
<input id="webserv_enable" type="checkbox" class="hidden">
|
||||||
|
<label>Enable Static Web Server</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="inline field">
|
||||||
|
<div class="ui toggle checkbox">
|
||||||
|
<input id="webserv_enableDirList" type="checkbox" class="hidden">
|
||||||
|
<label>Enable Directory Listing</label>
|
||||||
|
<small>If this folder do not contains any index files, list the directory of this folder.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Document Root Folder</label>
|
||||||
|
<input id="webserv_docRoot" type="text" readonly="true">
|
||||||
|
<small>
|
||||||
|
The web server root folder can only be changed via startup flags of zoraxy for security reasons.
|
||||||
|
See the -webserv flag for more details.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="field webservRootDisabled">
|
||||||
|
<label>Port Number</label>
|
||||||
|
<input id="webserv_listenPort" type="number" step="1" min="0" max="65535" value="8081" onchange="updateWebServLinkExample(this.value);">
|
||||||
|
<small>Use <code>http://127.0.0.1:<span class="webserv_port">8081</span></code> in proxy rules to access the web server</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small><i class="ui blue save icon"></i> Changes are saved automatically</small>
|
||||||
|
<br>
|
||||||
|
<div class="ui message">
|
||||||
|
<div class="ui accordion webservhelp">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
How to access the static web server?
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
There are three ways to access the static web server. <br>
|
||||||
|
<div class="ui ordered list">
|
||||||
|
<div class="item">
|
||||||
|
If you are using Zoraxy as your gateway reverse proxy server,
|
||||||
|
you can add a new subdomain proxy rule that points to
|
||||||
|
<a>http://127.0.0.1:<span class="webserv_port">8081</span></a>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
If you are using Zoraxy under another reverse proxy server,
|
||||||
|
add <a>http://127.0.0.1:<span class="webserv_port">8081</span></a> to the config of your upper layer reverse proxy server's config file.
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
Directly access the web server via <a>http://{zoraxy_host_ip}:<span class="webserv_port">8081</span></a> (Not recommended)
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="ui basic segment">
|
||||||
|
<h2>Web Directory Manager</h2>
|
||||||
|
<p>Manage your files inside your web directory</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui basic segment" style="display:none;" id="webdirManDisabledNotice">
|
||||||
|
<h4 class="ui header">
|
||||||
|
<i class="ui red times icon"></i>
|
||||||
|
<div class="content">
|
||||||
|
Web Directory Manager Disabled
|
||||||
|
<div class="sub header">Web Directory Manager has been disabled by the system administrator</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<iframe id="webserv_dirManager" src="tools/fs.html" style="width: 100%; height: 800px; border: 0px; overflow-y: hidden;">
|
||||||
|
|
||||||
|
</iframe>
|
||||||
|
<small>If you do not want to enable web access to your web directory, you can disable this feature with <code>-webfm=false</code> startup paramter</small>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(".webservhelp").accordion();
|
||||||
|
$(".ui.checkbox").checkbox();
|
||||||
|
|
||||||
|
function setWebServerRunningState(running){
|
||||||
|
if (running){
|
||||||
|
$("#webserv_enable").parent().checkbox("set checked");
|
||||||
|
$("#webservRunningState").find("i").attr("class", "green circle icon");
|
||||||
|
$("#webservRunningState").find(".webserv_status").text("Running");
|
||||||
|
}else{
|
||||||
|
$("#webserv_enable").parent().checkbox("set unchecked");
|
||||||
|
$("#webservRunningState").find("i").attr("class", "red circle icon");
|
||||||
|
$("#webservRunningState").find(".webserv_status").text("Stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWebServState(){
|
||||||
|
$.get("/api/webserv/status", function(data){
|
||||||
|
//Clear all event listeners
|
||||||
|
$("#webserv_enableDirList").off("change");
|
||||||
|
$("#webserv_enable").off("change");
|
||||||
|
$("#webserv_listenPort").off("change");
|
||||||
|
|
||||||
|
setWebServerRunningState(data.Running);
|
||||||
|
|
||||||
|
if (data.EnableDirectoryListing){
|
||||||
|
$("#webserv_enableDirList").parent().checkbox("set checked");
|
||||||
|
}else{
|
||||||
|
$("#webserv_enableDirList").parent().checkbox("set unchecked");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#webserv_docRoot").val(data.WebRoot + "/html/");
|
||||||
|
|
||||||
|
if (!data.EnableWebDirManager){
|
||||||
|
$("#webdirManDisabledNotice").show();
|
||||||
|
$("#webserv_dirManager").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#webserv_listenPort").val(data.ListeningPort);
|
||||||
|
updateWebServLinkExample(data.ListeningPort);
|
||||||
|
|
||||||
|
//Bind checkbox events
|
||||||
|
$("#webserv_enable").off("change").on("change", function(){
|
||||||
|
let enable = $(this)[0].checked;
|
||||||
|
if (enable){
|
||||||
|
$.get("/api/webserv/start", function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}else{
|
||||||
|
msgbox("Static web server started");
|
||||||
|
setWebServerRunningState(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
$.get("/api/webserv/stop", function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}else{
|
||||||
|
msgbox("Static web server stopped");
|
||||||
|
setWebServerRunningState(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#webserv_enableDirList").off("change").on("change", function(){
|
||||||
|
let enable = $(this)[0].checked;
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/webserv/setDirList",
|
||||||
|
method: "POST",
|
||||||
|
data: {"enable": enable},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}else{
|
||||||
|
msgbox("Directory listing setting updated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
$("#webserv_listenPort").off("change").on("change", function(){
|
||||||
|
let newPort = $(this).val();
|
||||||
|
|
||||||
|
//Check if the new value is same as listening port
|
||||||
|
let rpListeningPort = $("#incomingPort").val();
|
||||||
|
if (rpListeningPort == newPort){
|
||||||
|
confirmBox("This setting might cause port conflict. Continue Anyway?", function(choice){
|
||||||
|
if (choice == true){
|
||||||
|
//Continue anyway
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/webserv/setPort",
|
||||||
|
method: "POST",
|
||||||
|
data: {"port": newPort},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}else{
|
||||||
|
msgbox("Listening port updated");
|
||||||
|
}
|
||||||
|
updateWebServState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
//Cancel. Restore to previous value
|
||||||
|
updateWebServState();
|
||||||
|
msgbox("Setting restored");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/webserv/setPort",
|
||||||
|
method: "POST",
|
||||||
|
data: {"port": newPort},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
msgbox(data.error, false);
|
||||||
|
}else{
|
||||||
|
msgbox("Listening port updated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
updateWebServState();
|
||||||
|
|
||||||
|
function updateWebServLinkExample(newport){
|
||||||
|
$(".webserv_port").text(newport);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</div>
|
10
src/web/components/zgrok.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<div class="standardContainer">
|
||||||
|
<div class="ui basic segment">
|
||||||
|
<h2>Service Expose Proxy</h2>
|
||||||
|
<p>Expose your local test-site on the internet with single command</p>
|
||||||
|
</div>
|
||||||
|
<div class="ui message">
|
||||||
|
<h4>Work In Progress</h4>
|
||||||
|
We are looking for someone to help with implementing this feature in Zoraxy. <br>If you know how to write Golang and want to contribute, feel free to create a pull request to this feature!
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -39,7 +39,7 @@
|
|||||||
<h3 style="margin-top: 1em;">403 - Forbidden</h3>
|
<h3 style="margin-top: 1em;">403 - Forbidden</h3>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<p>You do not have permission to view this directory or page. <br>
|
<p>You do not have permission to view this directory or page. <br>
|
||||||
This might cause by the region limit setting of this site.</p>
|
This might be caused by the region limit setting of this site.</p>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div style="text-align: left;">
|
<div style="text-align: left;">
|
||||||
<small>Request time: <span id="reqtime"></span></small><br>
|
<small>Request time: <span id="reqtime"></span></small><br>
|
||||||
@ -52,4 +52,4 @@
|
|||||||
$("#requrl").text(window.location.href);
|
$("#requrl").text(window.location.href);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -154,4 +154,4 @@
|
|||||||
$("#host").text(location.href);
|
$("#host").text(location.href);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="ui divider menudivider">Access & Connections</div>
|
<div class="ui divider menudivider">Access & Connections</div>
|
||||||
<a class="item" tag="cert">
|
<a class="item" tag="cert">
|
||||||
<i class="simplistic lock icon"></i> TLS / SSL certificate
|
<i class="simplistic lock icon"></i> TLS / SSL certificates
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="redirectset">
|
<a class="item" tag="redirectset">
|
||||||
<i class="simplistic level up alternate icon"></i> Redirection
|
<i class="simplistic level up alternate icon"></i> Redirection
|
||||||
@ -62,13 +62,16 @@
|
|||||||
<a class="item" tag="gan">
|
<a class="item" tag="gan">
|
||||||
<i class="simplistic globe icon"></i> Global Area Network
|
<i class="simplistic globe icon"></i> Global Area Network
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="">
|
<a class="item" tag="zgrok">
|
||||||
<i class="simplistic podcast icon"></i> Service Expose Proxy
|
<i class="simplistic podcast icon"></i> Service Expose Proxy
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="tcpprox">
|
<a class="item" tag="tcpprox">
|
||||||
<i class="simplistic exchange icon"></i> TCP Proxy
|
<i class="simplistic exchange icon"></i> TCP Proxy
|
||||||
</a>
|
</a>
|
||||||
<div class="ui divider menudivider">Others</div>
|
<div class="ui divider menudivider">Others</div>
|
||||||
|
<a class="item" tag="webserv">
|
||||||
|
<i class="simplistic globe icon"></i> Static Web Server
|
||||||
|
</a>
|
||||||
<a class="item" tag="utm">
|
<a class="item" tag="utm">
|
||||||
<i class="simplistic time icon"></i> Uptime Monitor
|
<i class="simplistic time icon"></i> Uptime Monitor
|
||||||
</a>
|
</a>
|
||||||
@ -114,9 +117,15 @@
|
|||||||
<!-- Global Area Networking -->
|
<!-- Global Area Networking -->
|
||||||
<div id="gan" class="functiontab" target="gan.html"></div>
|
<div id="gan" class="functiontab" target="gan.html"></div>
|
||||||
|
|
||||||
|
<!-- Service Expose Proxy -->
|
||||||
|
<div id="zgrok" class="functiontab" target="zgrok.html"></div>
|
||||||
|
|
||||||
<!-- TCP Proxy -->
|
<!-- TCP Proxy -->
|
||||||
<div id="tcpprox" class="functiontab" target="tcpprox.html"></div>
|
<div id="tcpprox" class="functiontab" target="tcpprox.html"></div>
|
||||||
|
|
||||||
|
<!-- Web Server -->
|
||||||
|
<div id="webserv" class="functiontab" target="webserv.html"></div>
|
||||||
|
|
||||||
<!-- Up Time Monitor -->
|
<!-- Up Time Monitor -->
|
||||||
<div id="utm" class="functiontab" target="uptime.html"></div>
|
<div id="utm" class="functiontab" target="uptime.html"></div>
|
||||||
|
|
||||||
@ -141,14 +150,13 @@
|
|||||||
<br><br>
|
<br><br>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui container" style="color: grey; font-size: 90%">
|
<div class="ui container" style="color: grey; font-size: 90%">
|
||||||
<p>CopyRight Zoraxy project and its author, 2022 - <span class="year"></span></p>
|
<p>CopyRight Zoraxy Project and its authors © 2021 - <span class="year"></span></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="messageBox" class="ui green floating big compact message">
|
<div id="messageBox" class="ui green floating big compact message">
|
||||||
<p><i class="green check circle icon"></i> There are no message</p>
|
<p><i class="green check circle icon"></i> There are no messages</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="confirmBox" style="display:none;">
|
<div id="confirmBox" style="display:none;">
|
||||||
<div class="ui top attached progress">
|
<div class="ui top attached progress">
|
||||||
<div class="bar" style="width: 100%; min-width: 0px;"></div>
|
<div class="bar" style="width: 100%; min-width: 0px;"></div>
|
||||||
@ -342,7 +350,7 @@
|
|||||||
$("#confirmBox").hide();
|
$("#confirmBox").hide();
|
||||||
|
|
||||||
//Unset the event listener
|
//Unset the event listener
|
||||||
$("#confirmBox .ui.red.button").off("click");
|
$("#confirmBox .ui.red.button").off("click");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show the confirm box
|
// Show the confirm box
|
||||||
@ -383,4 +391,4 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -116,7 +116,7 @@ body{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#confirmBox .ui.progress .bar{
|
#confirmBox .ui.progress .bar{
|
||||||
background: #ffe32b !important;
|
background: #a9d1f3 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#confirmBox .confirmBoxBody .button{
|
#confirmBox .confirmBoxBody .button{
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
<h2 class="diagramHeader">Host</h2>
|
<h2 class="diagramHeader">Host</h2>
|
||||||
<p style="font-weight: 500; color: #bd2426;">Error</p>
|
<p style="font-weight: 500; color: #bd2426;">Error</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -140,7 +140,7 @@
|
|||||||
<div class="item">Visit the Reverse Proxy management interface to correct any setting errors</div>
|
<div class="item">Visit the Reverse Proxy management interface to correct any setting errors</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
@ -155,4 +155,4 @@
|
|||||||
$("#host").text(location.href);
|
$("#host").text(location.href);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -83,8 +83,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<h3>Manual Renew</h3>
|
<h3>Generate New Certificate</h3>
|
||||||
<p>Pick a certificate below to force renew</p>
|
<p>Enter a new / existing domain(s) to request new certificate(s)</p>
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Domain(s)</label>
|
<label>Domain(s)</label>
|
||||||
@ -109,11 +109,22 @@
|
|||||||
<div class="item" data-value="Let's Encrypt">Let's Encrypt</div>
|
<div class="item" data-value="Let's Encrypt">Let's Encrypt</div>
|
||||||
<div class="item" data-value="Buypass">Buypass</div>
|
<div class="item" data-value="Buypass">Buypass</div>
|
||||||
<div class="item" data-value="ZeroSSL">ZeroSSL</div>
|
<div class="item" data-value="ZeroSSL">ZeroSSL</div>
|
||||||
|
<div class="item" data-value="Custom ACME Server">Custom ACME Server</div>
|
||||||
<!-- <div class="item" data-value="Google">Google</div> -->
|
<!-- <div class="item" data-value="Google">Google</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="obtainButton" class="ui basic button" type="submit"><i class="yellow refresh icon"></i> Renew Certificate</button>
|
<div class="field" id="caInput" style="display:none;">
|
||||||
|
<label>ACME Server URL</label>
|
||||||
|
<input id="caURL" type="text" placeholder="https://example.com/acme/dictionary">
|
||||||
|
</div>
|
||||||
|
<div class="field" id="skipTLS" style="display:none;">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<input type="checkbox" id="skipTLSCheckbox">
|
||||||
|
<label>Ignore TLS/SSL Verification Error<br><small>E.g. self-signed, expired certificate (Not Recommended)</small></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="obtainButton" class="ui basic button" type="submit"><i class="yellow refresh icon"></i> Get Certificate</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<small>First time setting up HTTPS?<br>Try out our <a href="../tools/https.html" target="_blank">wizard</a></small>
|
<small>First time setting up HTTPS?<br>Try out our <a href="../tools/https.html" target="_blank">wizard</a></small>
|
||||||
@ -126,6 +137,7 @@
|
|||||||
let enableTrigerOnChangeEvent = true;
|
let enableTrigerOnChangeEvent = true;
|
||||||
$(".accordion").accordion();
|
$(".accordion").accordion();
|
||||||
$(".dropdown").dropdown();
|
$(".dropdown").dropdown();
|
||||||
|
$(".checkbox").checkbox();
|
||||||
|
|
||||||
function setAutoRenewIfCASupportMode(useAutoMode = true){
|
function setAutoRenewIfCASupportMode(useAutoMode = true){
|
||||||
if (useAutoMode){
|
if (useAutoMode){
|
||||||
@ -206,6 +218,11 @@
|
|||||||
$("#enableToggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
|
$("#enableToggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (parent && parent.setACMEEnableStates){
|
||||||
|
parent.setACMEEnableStates(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Render the domains table that exists in this zoraxy host
|
//Render the domains table that exists in this zoraxy host
|
||||||
@ -295,13 +312,24 @@
|
|||||||
obtainCertificate();
|
obtainCertificate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("input[name=ca]").on('change', function() {
|
||||||
|
if(this.value == "Custom ACME Server") {
|
||||||
|
$("#caInput").show();
|
||||||
|
$("#skipTLS").show();
|
||||||
|
} else {
|
||||||
|
$("#caInput").hide();
|
||||||
|
$("#skipTLS").hide();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Obtain certificate from API
|
// Obtain certificate from API
|
||||||
function obtainCertificate() {
|
function obtainCertificate() {
|
||||||
var domains = $("#domainsInput").val();
|
var domains = $("#domainsInput").val();
|
||||||
var filename = $("#filenameInput").val();
|
var filename = $("#filenameInput").val();
|
||||||
var email = $("#caRegisterEmail").val();
|
var email = $("#caRegisterEmail").val();
|
||||||
if (email == ""){
|
if (email == ""){
|
||||||
parent.msgbox("ACME renew email is not set")
|
parent.msgbox("ACME renew email is not set", false)
|
||||||
|
$("#obtainButton").removeClass("loading").removeClass("disabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (filename.trim() == "" && !domains.includes(",")){
|
if (filename.trim() == "" && !domains.includes(",")){
|
||||||
@ -312,11 +340,21 @@
|
|||||||
//Invalid settings. Force the filename to be same as domain
|
//Invalid settings. Force the filename to be same as domain
|
||||||
//if there are only 1 domain
|
//if there are only 1 domain
|
||||||
filename = domains;
|
filename = domains;
|
||||||
}else{
|
}else if (filename == "" && domains.includes(",")){
|
||||||
parent.msgbox("Filename cannot be empty for certs containing multiple domains.")
|
parent.msgbox("Filename cannot be empty for certs containing multiple domains.", false, 5000);
|
||||||
|
$("#obtainButton").removeClass("loading").removeClass("disabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ca = $("#ca").dropdown("get value");
|
var ca = $("#ca").dropdown("get value");
|
||||||
|
var caURL = "";
|
||||||
|
if (ca == "Custom ACME Server") {
|
||||||
|
ca = "custom";
|
||||||
|
caURL = $("#caURL").val();
|
||||||
|
}
|
||||||
|
|
||||||
|
var skipTLSValue = $("#skipTLSCheckbox")[0].checked;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/acme/obtainCert",
|
url: "/api/acme/obtainCert",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@ -325,6 +363,8 @@
|
|||||||
filename: filename,
|
filename: filename,
|
||||||
email: email,
|
email: email,
|
||||||
ca: ca,
|
ca: ca,
|
||||||
|
caURL: caURL,
|
||||||
|
skipTLS: skipTLSValue,
|
||||||
},
|
},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
$("#obtainButton").removeClass("loading").removeClass("disabled");
|
$("#obtainButton").removeClass("loading").removeClass("disabled");
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
Basic Auth Credential
|
Basic Auth Settings
|
||||||
<div class="sub header" id="epname"></div>
|
<div class="sub header" id="epname"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<h3 class="ui header">Basic Auth Credential</h3>
|
||||||
<div class="scrolling content ui form">
|
<div class="scrolling content ui form">
|
||||||
<div id="inlineEditBasicAuthCredentials" class="field">
|
<div id="inlineEditBasicAuthCredentials" class="field">
|
||||||
<p>Enter the username and password for allowing them to access this proxy endpoint</p>
|
<p>Enter the username and password for allowing them to access this proxy endpoint</p>
|
||||||
@ -40,15 +42,54 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field" >
|
<div class="field" >
|
||||||
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="blue add icon"></i> Add Credential</button>
|
<button class="ui basic button" onclick="addCredentialsToEditingList();"><i class="blue add icon"></i> Add Credential</button>
|
||||||
|
<button class="ui basic button" style="float: right;" onclick="saveCredentials();"><i class="green save icon"></i> Save Credential</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="field" >
|
|
||||||
<button class="ui basic button" style="float: right;" onclick="saveCredentials();"><i class="green save icon"></i> Save</button>
|
|
||||||
<button class="ui basic button" style="float: right;" onclick="cancelCredentialEdit();"><i class="remove icon"></i> Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<h3 class="ui header">Authentication Exclusion Paths</h3>
|
||||||
|
<div class="scrolling content ui form">
|
||||||
|
<p>Exclude specific directories / paths which contains the following subpath prefix from authentication. Useful if you are hosting services require remote API access.</p>
|
||||||
|
<table class="ui very basic compacted unstackable celled table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Path Prefix</th>
|
||||||
|
<th>Remove</th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody id="exclusionPaths">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><i class="ui green circle check icon"></i> No Path Excluded</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="field">
|
||||||
|
<input id="newExclusionPath" type="text" placeholder="/public/api/" autocomplete="off">
|
||||||
|
<small>Make sure you add the tailing slash for only selecting the files / folder inside that path.</small>
|
||||||
|
</div>
|
||||||
|
<div class="field" >
|
||||||
|
<button class="ui basic button" onclick="addExceptionPath();"><i class="blue add icon"></i> Add Exception</button>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui basic message">
|
||||||
|
<h4>How to use set excluded paths?</h4>
|
||||||
|
<p>All request URI that contains the given prefix will be allowed to bypass authentication and <b>the prefix must start with a slash.</b> For example, given the following prefix.<br>
|
||||||
|
<code>/public/res/</code><br>
|
||||||
|
<br>
|
||||||
|
Zoraxy will allow authentication bypass of any subdirectories or resources under the /public/res/ directory. For example, the following paths access will be able to bypass basic auth mechanism under this setting.<br>
|
||||||
|
<code>/public/res/photo.png</code><br>
|
||||||
|
<code>/public/res/far/boo/</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="field" >
|
||||||
|
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br><br><br><br>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
let editingCredentials = [];
|
let editingCredentials = [];
|
||||||
@ -124,6 +165,80 @@
|
|||||||
updateEditingCredentialList();
|
updateEditingCredentialList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addExceptionPath(){
|
||||||
|
// Retrieve the username and password input values
|
||||||
|
var newExclusionPathMatchingPrefix = $('#newExclusionPath').val().trim();
|
||||||
|
if (newExclusionPathMatchingPrefix == ""){
|
||||||
|
parent.msgbox("Matching prefix cannot be empty!", false, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/proxy/auth/exceptions/add",
|
||||||
|
data:{
|
||||||
|
ptype: editingEndpoint.ept,
|
||||||
|
ep: editingEndpoint.ep,
|
||||||
|
prefix: newExclusionPathMatchingPrefix
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
parent.msgbox(data.error, false, 5000);
|
||||||
|
}else{
|
||||||
|
initExceptionPaths();
|
||||||
|
parent.msgbox("New exception path added", true);
|
||||||
|
$('#newExclusionPath').val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeExceptionPath(object){
|
||||||
|
let matchingPrefix = $(object).attr("prefix");
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/proxy/auth/exceptions/delete",
|
||||||
|
data:{
|
||||||
|
ptype: editingEndpoint.ept,
|
||||||
|
ep: editingEndpoint.ep,
|
||||||
|
prefix: matchingPrefix
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
parent.msgbox(data.error, false, 5000);
|
||||||
|
}else{
|
||||||
|
initExceptionPaths();
|
||||||
|
parent.msgbox("Exception path removed", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Load exception paths from server
|
||||||
|
function initExceptionPaths(){
|
||||||
|
$.get(`/api/proxy/auth/exceptions/list?ptype=${editingEndpoint.ept}&ep=${editingEndpoint.ep}`, function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
parent.msgbox(data.error, false, 5000);
|
||||||
|
}else{
|
||||||
|
if (data.length == 0){
|
||||||
|
$("#exclusionPaths").html(` <tr>
|
||||||
|
<td colspan="2"><i class="ui green circle check icon"></i> No Path Excluded</td>
|
||||||
|
</tr>`);
|
||||||
|
}else{
|
||||||
|
$("#exclusionPaths").html("");
|
||||||
|
data.forEach(function(rule){
|
||||||
|
$("#exclusionPaths").append(` <tr>
|
||||||
|
<td>${rule.PathPrefix}</td>
|
||||||
|
<td><button class="ui red basic mini icon button" onclick="removeExceptionPath(this);" prefix="${rule.PathPrefix}"><i class="ui red times icon"></i></button></td>
|
||||||
|
</tr>`);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
initExceptionPaths();
|
||||||
|
|
||||||
function updateEditingCredentialList() {
|
function updateEditingCredentialList() {
|
||||||
var tableBody = $('#inlineEditBasicAuthCredentialTable');
|
var tableBody = $('#inlineEditBasicAuthCredentialTable');
|
||||||
tableBody.empty();
|
tableBody.empty();
|
||||||
@ -168,7 +283,7 @@
|
|||||||
return isExists;
|
return isExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelCredentialEdit(){
|
function closeThisWrapper(){
|
||||||
parent.hideSideWrapper(true);
|
parent.hideSideWrapper(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +301,7 @@
|
|||||||
parent.msgbox(data.error, false, 6000);
|
parent.msgbox(data.error, false, 6000);
|
||||||
}else{
|
}else{
|
||||||
parent.msgbox("Credentials Updated");
|
parent.msgbox("Credentials Updated");
|
||||||
parent.hideSideWrapper(true);
|
//parent.hideSideWrapper(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
1388
src/web/tools/fs.css
Normal file
1057
src/web/tools/fs.html
Normal file
8
src/web/tools/img/arrow-left.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<polygon fill="#FFFFFF" stroke="#231815" stroke-width="3" stroke-miterlimit="10" points="105.518,113.641 20.981,64.833
|
||||||
|
105.518,16.027 "/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 618 B |
8
src/web/tools/img/arrow-right.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<polygon fill="#FFFFFF" stroke="#231815" stroke-width="3" stroke-miterlimit="10" points="20.981,16.026 105.518,64.833
|
||||||
|
20.981,113.64 "/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 616 B |
8
src/web/tools/img/eq.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
|
||||||
|
<path fill="#4f4f4f" d="M13.95,36.15v-24.3h3.3v24.3H13.95z M22.35,44.15V3.85h3.3v40.3H22.35z M5.6,28.15v-8.3h3.25v8.3H5.6z
|
||||||
|
M30.75,36.15v-24.3h3.3v24.3H30.75z M39.15,28.15v-8.3h3.25v8.3H39.15z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 669 B |
3442
src/web/tools/img/file.svg
Normal file
After Width: | Height: | Size: 251 KiB |
12
src/web/tools/img/folder.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="128px"
|
||||||
|
height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<g id="圖層_2">
|
||||||
|
<polygon fill="#DBAC50" points="104.03,38.089 104.03,101.074 22.388,101.074 22.388,27.94 49.702,27.75 55.666,38.345 "/>
|
||||||
|
</g>
|
||||||
|
<g id="圖層_3">
|
||||||
|
<polygon fill="#E5BD64" points="104.328,101.97 22.836,101.97 38.209,52.716 118.806,52.716 "/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 728 B |
11
src/web/tools/img/icon.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="200px" height="50px" viewBox="0 0 200 50" enable-background="new 0 0 200 50" xml:space="preserve">
|
||||||
|
<polygon fill="#DBDCDC" points="18.325,42.875 37.912,6.593 57.5,42.875 "/>
|
||||||
|
<polygon fill="#EEEEEF" points="66.771,6.594 94.125,24.744 66.771,42.895 "/>
|
||||||
|
<polygon fill="#9E9E9F" points="180.347,6.594 165.384,25 150.421,6.594 "/>
|
||||||
|
<polygon fill="#9E9E9F" points="150.422,42.875 165.384,24.469 180.347,42.875 "/>
|
||||||
|
<circle fill="#DBDCDC" cx="122.75" cy="25.156" r="17.875"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 843 B |
18
src/web/tools/img/network.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="128px"
|
||||||
|
height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<g id="圖層_2">
|
||||||
|
<polygon fill="#95D5F4" points="110.998,23.424 110.998,84.064 17.001,84.064 17.001,13.652 48.449,13.469 55.315,23.669 "/>
|
||||||
|
</g>
|
||||||
|
<g id="圖層_3">
|
||||||
|
<polygon fill="#6BC2EC" points="110.998,84.064 17.001,84.064 17.087,31.401 110.57,31.401 "/>
|
||||||
|
</g>
|
||||||
|
<g id="圖層_4">
|
||||||
|
<rect x="17.001" y="103.51" fill="#B5B5B6" width="93.997" height="4.691"/>
|
||||||
|
<rect x="60.985" y="84.064" fill="#B5B5B6" width="6.029" height="19.445"/>
|
||||||
|
<path fill="#C9CACA" d="M72.935,110.512c0,2.221-1.8,4.02-4.021,4.02h-9.827c-2.221,0-4.021-1.799-4.021-4.02v-9.828
|
||||||
|
c0-2.221,1.8-4.021,4.021-4.021h9.827c2.221,0,4.021,1.801,4.021,4.021V110.512z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
24
src/web/tools/img/opr/copy.svg
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<polygon fill="#FFFFFF" stroke="#9FA0A0" stroke-width="3" stroke-miterlimit="10" points="78.125,32.875 78.125,83.25
|
||||||
|
30.125,83.25 30.125,22 68.625,22 "/>
|
||||||
|
<polyline fill="none" stroke="#9FA0A0" stroke-width="3" stroke-miterlimit="10" points="78.125,35.75 65.125,35.75 65.125,22 "/>
|
||||||
|
<line fill="none" stroke="#65D0FF" stroke-width="3" stroke-miterlimit="10" x1="35.75" y1="38.667" x2="60.417" y2="38.667"/>
|
||||||
|
<line fill="none" stroke="#65D0FF" stroke-width="3" stroke-miterlimit="10" x1="35.75" y1="45.167" x2="73.25" y2="45.167"/>
|
||||||
|
<line fill="none" stroke="#65D0FF" stroke-width="3" stroke-miterlimit="10" x1="35.75" y1="51.25" x2="73.25" y2="51.25"/>
|
||||||
|
<line fill="none" stroke="#65D0FF" stroke-width="3" stroke-miterlimit="10" x1="35.75" y1="57.833" x2="73.25" y2="57.833"/>
|
||||||
|
<line fill="none" stroke="#65D0FF" stroke-width="3" stroke-miterlimit="10" x1="35.417" y1="64" x2="73.25" y2="64"/>
|
||||||
|
<line fill="none" stroke="#65D0FF" stroke-width="3" stroke-miterlimit="10" x1="35.417" y1="70.75" x2="73.25" y2="70.75"/>
|
||||||
|
<polygon fill="#FFFFFF" stroke="#9FA0A0" stroke-width="3" stroke-miterlimit="10" points="104.5,57.916 104.5,108.291
|
||||||
|
56.5,108.291 56.5,47.041 95,47.041 "/>
|
||||||
|
<polyline fill="none" stroke="#9FA0A0" stroke-width="3" stroke-miterlimit="10" points="104.5,60.791 91.5,60.791 91.5,47.041 "/>
|
||||||
|
<line fill="none" stroke="#2EA7E0" stroke-width="3" stroke-miterlimit="10" x1="62.125" y1="63.708" x2="86.791" y2="63.708"/>
|
||||||
|
<line fill="none" stroke="#2EA7E0" stroke-width="3" stroke-miterlimit="10" x1="62.125" y1="70.207" x2="99.625" y2="70.207"/>
|
||||||
|
<line fill="none" stroke="#2EA7E0" stroke-width="3" stroke-miterlimit="10" x1="62.125" y1="76.291" x2="99.625" y2="76.291"/>
|
||||||
|
<line fill="none" stroke="#2EA7E0" stroke-width="3" stroke-miterlimit="10" x1="62.125" y1="82.875" x2="99.625" y2="82.875"/>
|
||||||
|
<line fill="none" stroke="#2EA7E0" stroke-width="3" stroke-miterlimit="10" x1="61.792" y1="89.041" x2="99.625" y2="89.041"/>
|
||||||
|
<line fill="none" stroke="#2EA7E0" stroke-width="3" stroke-miterlimit="10" x1="61.792" y1="95.791" x2="99.625" y2="95.791"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
8
src/web/tools/img/opr/delete.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<polygon fill="#EC0013" stroke="#C30D23" stroke-miterlimit="10" points="95.338,37.37 88.63,30.662 64,55.292 39.37,30.662
|
||||||
|
32.662,37.37 57.292,62 32.662,86.63 39.369,93.338 64,68.707 88.63,93.338 95.338,86.631 70.707,62 "/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 702 B |
154
src/web/tools/img/opr/download.svg
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<image display="none" overflow="visible" width="347" height="333" xlink:href="data:image/jpeg;base64,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
|
||||||
|
nadlsSrfYQ6QEBAdwdp2X//aAAgBAQABBQDlTMqjEoYlDEoYlDEoYlDEoYlDEoYlDEoYlDEoYlDE
|
||||||
|
oYlDEoYlDEoYlDEoYlDEoGpQxKGJQxKGJQxKGJQxKGJQxKGJQxKGJQxKGJQxKGJQxKGJQp1KzOW3
|
||||||
|
H+U/8tPM5bcf5TtQES9anmctuPUMElRllrGWsZaxlrGWsZaxlrGWsZaxlrGWsGhZDCsYFxNKkn6t
|
||||||
|
PM5bcepeKVtJs5aBlJGUgZSBlIGUgZSBlIGUgZSBlIGUgZSBlIjWUiXUqQpCrMbdPM5bceoV1JJh
|
||||||
|
07juO/o+6vlVUqXiUg0q9OnmctuLEfQK6kL9IL1VXmYqqVLqVoU2ZGR9I+hTzOW3FiHoEKSQC9U0
|
||||||
|
kYwEMBDlEIQyUIejTzOW3Fk+3SIj1O72pC/T68BAcwRaZtRkViPSIiQiKeZy24sRD3LULS/N0I83
|
||||||
|
Qg+doB56gB8/xwP7DxxEf2XjCTxz6H6T/Dz77bFEjmaEi8xRguYox5ijHl6MeWox5ajDXIUrysPS
|
||||||
|
nmctuLHMPGzREk1GdI/HSVAOjqAdFUg6GqB8fVhzjatRcA2pvi+sREREREYiEREEcRGz9tQpfFt0
|
||||||
|
70NM+NO8Mh4ZDwyXQllyKYpOjdN2mFPM5bcWOdMioac4PmZR7CBCBD8h3BQIcXDR9T6LWlBVPMNN
|
||||||
|
hzl6pR+TqIt81VIOk5lh8JURpM+xWPsh/wDzk44RER36kfc+58eUKMjFMf7OW3FjntjTl+8yKMC6
|
||||||
|
mZgzUCiY4rZ2FuE2mur1PKMzizTuPn42rFQwthfvxXKm2slEoiu6/ZP5yY2k3+9Af/GKY/2ctuLH
|
||||||
|
PbCnnHfZSOL2ZdTHL1WEoQNV/Awx9hzsNWLj4SsU/TpKBdfsn88rSbz+XH7QU0zltxY57YU8477J
|
||||||
|
Di9n1O6vUa6gzgZmQ4E4uGOcL/rMGfbhHjbrU3dfsn88j6xEREJvO+g2gppnLbixz2wp5x32DBDi
|
||||||
|
9n1X8Xo5xin406hPG8e7TKHI8U9UvVfHqoyVhhxkfIFd1+yfzytJvO+g2gppnLbmxz2wp5x32DBD
|
||||||
|
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
|
||||||
|
G91n8gmbbZvEll3eX9KOiKjpjYioqKioqKin+MYLD66jL2aD08ztuTeAb3WfyCZttkefirmZrmhs
|
||||||
|
Bfguc6ZXOdMrnOmVznTK5rplc50yuc6ZXNdMrmvmVzXTK5rplc50yuc6ZXOdMrnOmVznTK5zplFm
|
||||||
|
Zmuc0+ZWOoy9mg9PM7bk3gG91k8QTSYKpVKpVKtVhcwLmBcwLmBVqtVrB6rCqCqVSqVSqVSqVSqV
|
||||||
|
SqUVFRUdJKy9mh3Tze25N4BvdZIAJPuEEPtdIql0iqXSKpdIql0iqXyKpfIql8iqXyKpfIql8iqX
|
||||||
|
yKpfIql8iqXyKpfIql8iqXyKpdIql0iqXSKpdIql0iqXSKpdIql0iqXSKpdIqDpFQdIql0iqXSKp
|
||||||
|
dIql0ij9rpFZYOFw0Hp5vbcm8A3us+1w9wONxV4Y2QVDZBUNkFQJKgSWLGyC5bZBctsguW2QXLbI
|
||||||
|
LltkFy2yC5bZBUNkFQ2QVDZBctsguW2QXLbIKhsguW2QXLbIKhsgqGyC5bZBctslQ2QVDZBUNkFQ
|
||||||
|
2QVDZBUNkFQ2SobIK72tuPyCc0YBpuu0O6eb23JvAN7rMVHTBQ1nmoKChpgoavDE+CefnDQ7p5vb
|
||||||
|
cm8A3u/iAsy6F+h3Tze25N4BvdZvKiAoqKioqKiqwqwqgqgqgqgrwcF5273G5VhVhVhVhVhVhVhV
|
||||||
|
hVhVhVhX+8K8Ovw8EXGJ0Hp5vbcm8A3utQtwULGKubC1FYL715KIUQohRCiFEKIUVFRWJw0YHQen
|
||||||
|
m9tybwDe74IDzWIULWEFePBY62/Qenm9tybwDe74G/wQdd9gxQ+SvtlvmnAj7fBY+Oud083tuTeA
|
||||||
|
b3fAgNFzfEr2Dw8dF2oIMfAotfj5HXO6eb23JvAN7vgQ1rbj4lXXYeagoKCgoKCgoKCuuu8kBdj5
|
||||||
|
653Tze25N4Bvd8bdfrndPN7bk3gG938W7p5vbcm8A3u/i3dPN7bl/9k=" transform="matrix(0.3272 0 0 0.3272 5.9824 3.0469)">
|
||||||
|
</image>
|
||||||
|
<rect x="19.833" y="28.5" fill="#322912" width="92.667" height="52.5"/>
|
||||||
|
<rect x="19.833" y="81" fill="#DCDDDD" width="92.667" height="10.667"/>
|
||||||
|
<rect x="50.167" y="101.167" fill="#DCDDDD" width="32.5" height="3.666"/>
|
||||||
|
<rect x="59.833" y="91.667" fill="#C9CACA" width="13.5" height="9.5"/>
|
||||||
|
<rect x="23.333" y="31.667" fill="#00A0E9" width="85.833" height="46"/>
|
||||||
|
<polyline fill="none" stroke="#FFFFFF" stroke-width="8" stroke-miterlimit="10" points="55.591,51.929 66.042,63.104
|
||||||
|
77.577,51.929 "/>
|
||||||
|
<line fill="none" stroke="#FFFFFF" stroke-width="8" stroke-miterlimit="10" x1="66.042" y1="63.104" x2="66.043" y2="28.5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 12 KiB |
15
src/web/tools/img/opr/new_folder.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<rect x="26.2" y="17.403" fill="#F5D370" width="71.08" height="88.553"/>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polygon fill="#FFE79C" points="59.98,72.606 59.98,22.099 26.2,17.403 26.2,105.956 49.486,109.192 49.486,76.212 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<polygon fill="#FFFFFF" stroke="#727171" stroke-miterlimit="10" points="109.001,90.244 94.813,90.244 94.813,76.057
|
||||||
|
87.313,76.057 87.313,90.244 73.126,90.244 73.126,97.744 87.313,97.744 87.313,111.932 94.813,111.932 94.813,97.744
|
||||||
|
109.001,97.744 "/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 943 B |
16
src/web/tools/img/opr/open.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<g id="圖層_2">
|
||||||
|
<g>
|
||||||
|
<polygon fill="#FADA79" points="104.029,38.089 104.029,101.074 22.388,101.074 22.388,27.94 49.702,27.75 55.666,38.345 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="圖層_3">
|
||||||
|
<g>
|
||||||
|
<polygon fill="#FFE79E" points="104.328,101.971 22.836,101.971 38.209,52.716 118.807,52.716 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 772 B |
102
src/web/tools/img/opr/paste.svg
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<image display="none" overflow="visible" width="256" height="256" xlink:href="data:image/jpeg;base64,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
|
||||||
|
ia5zSMNU0ziHoY7S5SxdOQZmRX5SGeoZ6hnqGeoZ6hnqGeoZ6hnqGeoZ6hlEozlKS70xdUuawia5
|
||||||
|
zSMNU0ziGoY7S5SxfBRdUuawia5zSMNU0ziGoY7S5SxdOUSTUfYQ2KuvcNirr3DYq69w2KuvcNir
|
||||||
|
r3DYq69w2KuvcNirr3DYq69w2KuvcNirr3A5UGiTt6YuqXNYRNc5pGGqaZxDUMdpcpYvgouqXNYR
|
||||||
|
Nc5pGGqaZxD0MdpcpYvgouqXNYRNc5pGELv5KiPAcoZUh1KFoKT7xyENuzvCHEM7whxDO8IcQzvC
|
||||||
|
HEM7whxDO8IcQzvCHEM7wg4RKSssq+g8or3b05BmZfMhnqGeoZ6hnqGeoZ6hnqGeoZ6hnqGeoHIo
|
||||||
|
zl7emLqlzWETXOaR9B/pOKRLfyTkF19fqMbZeExtl4TG2XhMbZeExtl4TG2XhMbZeEwpTijUr9VV
|
||||||
|
07p/BRdUuawia5zSO1nXK6cokmr5ENirr3DYq69w2KuvcNirr3DYq69w2KuvcNirr3DYq69w2Kuv
|
||||||
|
cNirr3DYq69wOVBok7emLqlzWETXOaR2s65XwUXVLmsImuc0jtZ1yvgouqXMdhE1zmkdrOuX8FF1
|
||||||
|
K5rCJrnNI7WdcoXheF4XheF4XheF4XheF4XheF4XheEXUrmsImuc0jtf6TEQ403LLkpUZFKf0HGP
|
||||||
|
esxxj3rMcY96zHGPesxxj3rMcY96zHGPesxxj3rMcY96zHGPesxxj3rMcY96zHGPesxxj3rMcY96
|
||||||
|
zHGPesxxj3rMGRxbpkdwyNZ2ETXOaR/8D//Z" transform="matrix(0.459 0 0 0.459 5.25 4.5)">
|
||||||
|
</image>
|
||||||
|
<path fill="#D0B065" stroke="#B28247" stroke-width="2" stroke-miterlimit="10" d="M81.61,90.593c0,0.923-0.748,1.671-1.671,1.671
|
||||||
|
H24.671c-0.923,0-1.672-0.748-1.672-1.671V20.698c0-0.924,0.749-1.672,1.672-1.672h55.268c0.923,0,1.671,0.749,1.671,1.672V90.593z"
|
||||||
|
/>
|
||||||
|
<rect x="41.492" y="18.085" fill="#DCDDDD" stroke="#9FA0A0" stroke-width="2" stroke-miterlimit="10" width="22.985" height="10.552"/>
|
||||||
|
<polygon fill="#FFFFFF" stroke="#9FA0A0" stroke-width="3" stroke-miterlimit="10" points="105.714,53.116 105.714,106.334
|
||||||
|
55.004,106.334 55.004,41.627 95.679,41.627 "/>
|
||||||
|
<polyline fill="none" stroke="#9FA0A0" stroke-width="3" stroke-miterlimit="10" points="105.714,56.153 91.98,56.153 91.98,41.627
|
||||||
|
"/>
|
||||||
|
<line fill="none" stroke="#DCDDDD" stroke-width="3" stroke-miterlimit="10" x1="60.947" y1="59.234" x2="87.006" y2="59.234"/>
|
||||||
|
<line fill="none" stroke="#DCDDDD" stroke-width="3" stroke-miterlimit="10" x1="60.947" y1="66.101" x2="100.563" y2="66.101"/>
|
||||||
|
<line fill="none" stroke="#DCDDDD" stroke-width="3" stroke-miterlimit="10" x1="60.947" y1="72.527" x2="100.563" y2="72.527"/>
|
||||||
|
<line fill="none" stroke="#DCDDDD" stroke-width="3" stroke-miterlimit="10" x1="60.947" y1="79.484" x2="100.563" y2="79.484"/>
|
||||||
|
<line fill="none" stroke="#DCDDDD" stroke-width="3" stroke-miterlimit="10" x1="60.595" y1="85.998" x2="100.563" y2="85.998"/>
|
||||||
|
<line fill="none" stroke="#DCDDDD" stroke-width="3" stroke-miterlimit="10" x1="60.595" y1="93.129" x2="100.563" y2="93.129"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 8.1 KiB |
11
src/web/tools/img/opr/share.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<circle fill="#24CC29" cx="84.5" cy="42.833" r="13.833"/>
|
||||||
|
<circle fill="#24CC29" cx="36.5" cy="67.334" r="13.833"/>
|
||||||
|
<circle fill="#24CC29" cx="84.5" cy="92.167" r="13.833"/>
|
||||||
|
<line fill="none" stroke="#24CC29" stroke-width="9" stroke-miterlimit="10" x1="36.5" y1="67.334" x2="84.5" y2="42.833"/>
|
||||||
|
<line fill="none" stroke="#24CC29" stroke-width="9" stroke-miterlimit="10" x1="36.5" y1="67.334" x2="84.5" y2="92.166"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 894 B |
171
src/web/tools/img/opr/upload.svg
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<circle fill="#86D1EF" cx="37.667" cy="71.896" r="25.083"/>
|
||||||
|
<circle fill="#86D1EF" cx="64.917" cy="58.895" r="36.249"/>
|
||||||
|
<circle fill="#86D1EF" cx="94.999" cy="76.729" r="20.417"/>
|
||||||
|
<path fill="#86D1EF" d="M96.416,95.146c0,1.104-0.777,2-1.738,2H37.156c-0.961,0-1.739-0.896-1.739-2v-31c0-1.106,0.778-2,1.739-2
|
||||||
|
h57.521c0.961,0,1.738,0.895,1.738,2V95.146z"/>
|
||||||
|
</g>
|
||||||
|
<image display="none" overflow="visible" width="512" height="512" xlink:href="data:image/jpeg;base64,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
|
||||||
|
790emIr6aHOHZbfNRw9ran8dujZUNiwzQ9hBBvJkdts2Ht6HXfS7ZVhpZB+bGNRw9ran8dujZU94
|
||||||
|
0wIliIPig9hpa4Ugi6D6VfFJ75sMHS4pz3mlzjST7TUcPa2p/Hbo2VQlJl34XfI4+E9F5Ui16TL3
|
||||||
|
mhrRSSehEt8lliGPjU8Pa2p/Hbo2VbZWbdTDtMiHw+w+xBzTSDZBHpGk2ALZRlZY/hae+4eI/tVc
|
||||||
|
Pa2p/Hbo2cwIUWmJL9F1t5CLAeHtPRbF/wBHl8Rwa1tkk2kZeVJbAtOfdf8A6VfD2tqfx26NnMiJ
|
||||||
|
AeWG6LhvhCHNj7MT6vAf2QcwhzTaIs+jPyO7US5DbZJVDz2IQ+WGLXX08xw9ran8dujZzdMCIQPo
|
||||||
|
NluBBs3CLT9bLIwIfajNJPhJoOAqkWfQ1LiGjpNhEfc+68eFln32kWQB9hhuiy7Ci5xLnG2TZPM8
|
||||||
|
Pa2p/Hbo2c9+KM9o6KSRgKsxA/GaPhQu9Dhm9SF5LMJXkMwleQzCV5DMJXkMwleQzCV5DMJXkMwl
|
||||||
|
eQzCV5DMJXkMwleQzCV5DMJXkMwleQzCV5DMJXkMwleQzCV5DMJXkMwleQzCV5DMJXkMwleQzCV5
|
||||||
|
DMJXkMwleQzCV5DMJXkMwleQzCV5DMJXkMwleQzCV3YcNt8ErzQwfxAH60qmNFc/GJI5zh7W1P47
|
||||||
|
dGz1A4e1tT+O3Rs9QOHtbU/jt0bPUDh7W1P47dGz1A4e1tT+O3Rs9QOHtbU/jt0bPUDh7W1P47dG
|
||||||
|
z1A4e1tT+O3Rs9QOHtbU/jt0bPUDh7W1P47dGz1A4e1tRti2x/l+faHmLd5bvLd5bvLd5bvLd5bv
|
||||||
|
Ld5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvL
|
||||||
|
d5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5bvLd5f/AJX+H/t8zQf+
|
||||||
|
S//Z" transform="matrix(0.2174 0 0 0.2174 8.354 3.207)">
|
||||||
|
</image>
|
||||||
|
<polyline fill="none" stroke="#FFFFFF" stroke-width="10" stroke-miterlimit="10" points="78.459,66.957 64.917,52.479
|
||||||
|
49.973,66.957 "/>
|
||||||
|
<line fill="none" stroke="#FFFFFF" stroke-width="10" stroke-miterlimit="10" x1="64.917" y1="52.479" x2="64.917" y2="97.313"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 13 KiB |
13
src/web/tools/img/wifi.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
||||||
|
<circle fill="#231815" stroke="#231815" stroke-miterlimit="10" cx="64" cy="98.551" r="8.801"/>
|
||||||
|
<path fill="none" stroke="#231815" stroke-width="14" stroke-miterlimit="10" d="M10.296,49.66
|
||||||
|
c29.139-29.141,76.297-29.141,105.436,0"/>
|
||||||
|
<path fill="none" stroke="#231815" stroke-width="14" stroke-miterlimit="10" d="M28.269,65.352
|
||||||
|
c19.917-19.917,52.152-19.917,72.068,0"/>
|
||||||
|
<path fill="none" stroke="#231815" stroke-width="14" stroke-miterlimit="10" d="M45.548,81.838
|
||||||
|
c10.367-10.367,27.145-10.367,37.509,0"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 981 B |
1
src/web/tools/img/zoom-in.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#FFFFFF"><path d="M0 0h24v24H0V0z" fill="none"/><path stroke="#231815" stroke-width="0.4px" d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zm.5-7H9v2H7v1h2v2h1v-2h2V9h-2z"/></svg>
|
After Width: | Height: | Size: 462 B |
1
src/web/tools/img/zoom-out.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0V0z" fill="none"/><path stroke="#231815" stroke-width="0.4px" d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"/></svg>
|
After Width: | Height: | Size: 442 B |
@ -96,6 +96,13 @@
|
|||||||
$('<th>').text('Vendor')
|
$('<th>').text('Vendor')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (data.error != undefined){
|
||||||
|
$('#mdns-hosts').html(`<table class="ui celled unstackable table"><tbody></tbody></table>`);
|
||||||
|
$('#mdns-hosts').find("tbody").append(`<tr><td colspan="5"><i class="ui red circle times icon"></i> ${data.error}</td></tr>`);
|
||||||
|
$("#discover").addClass("disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create table body
|
// Create table body
|
||||||
var tableBody = $('<tbody>');
|
var tableBody = $('<tbody>');
|
||||||
|
@ -19,7 +19,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -104,7 +103,21 @@ func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) {
|
|||||||
/*
|
/*
|
||||||
Up Time Monitor
|
Up Time Monitor
|
||||||
*/
|
*/
|
||||||
//Generate uptime monitor targets from reverse proxy rules
|
|
||||||
|
// Update uptime monitor targets after rules updated
|
||||||
|
// See https://github.com/tobychui/zoraxy/issues/77
|
||||||
|
func UpdateUptimeMonitorTargets() {
|
||||||
|
if uptimeMonitor != nil {
|
||||||
|
uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
|
||||||
|
go func() {
|
||||||
|
uptimeMonitor.ExecuteUptimeCheck()
|
||||||
|
}()
|
||||||
|
|
||||||
|
SystemWideLogger.PrintAndLog("Uptime", "Uptime monitor config updated", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate uptime monitor targets from reverse proxy rules
|
||||||
func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
|
func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
|
||||||
subds := dp.GetSDProxyEndpointsAsMap()
|
subds := dp.GetSDProxyEndpointsAsMap()
|
||||||
vdirs := dp.GetVDProxyEndpointsAsMap()
|
vdirs := dp.GetVDProxyEndpointsAsMap()
|
||||||
@ -156,11 +169,19 @@ func HandleUptimeMonitorListing(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Handle listing current registered mdns nodes
|
// Handle listing current registered mdns nodes
|
||||||
func HandleMdnsListing(w http.ResponseWriter, r *http.Request) {
|
func HandleMdnsListing(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if mdnsScanner == nil {
|
||||||
|
utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
|
||||||
|
return
|
||||||
|
}
|
||||||
js, _ := json.Marshal(previousmdnsScanResults)
|
js, _ := json.Marshal(previousmdnsScanResults)
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) {
|
func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if mdnsScanner == nil {
|
||||||
|
utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
|
||||||
|
return
|
||||||
|
}
|
||||||
domain, err := utils.PostPara(r, "domain")
|
domain, err := utils.PostPara(r, "domain")
|
||||||
var hosts []*mdns.NetworkHost
|
var hosts []*mdns.NetworkHost
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -255,7 +276,7 @@ func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("[WoL] Sending Wake on LAN magic packet to " + wake)
|
SystemWideLogger.PrintAndLog("WoL", "Sending Wake on LAN magic packet to "+wake, nil)
|
||||||
err := wakeonlan.WakeTarget(wake)
|
err := wakeonlan.WakeTarget(wake)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
@ -327,3 +348,20 @@ func HandleZoraxyInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
js, _ := json.MarshalIndent(info, "", " ")
|
js, _ := json.MarshalIndent(info, "", " ")
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HandleGeoIpLookup(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ip, err := utils.GetPara(r, "ip")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "ip not given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := geodbStore.ResolveCountryCodeFromIP(ip)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
js, _ := json.Marshal(cc)
|
||||||
|
utils.SendJSONResponse(w, string(js))
|
||||||
|
}
|
||||||
|