diff --git a/.gitignore b/.gitignore
index 1a6e609..8461e6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,4 +48,5 @@ coverage
*.sw?
*.tsbuildinfo
-.vite/*
\ No newline at end of file
+.vite/*
+
diff --git a/Dockerfile b/Dockerfile
index 8b2fec8..9d45e70 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,35 @@
+FROM golang:1.23 AS compiler
+WORKDIR /go
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ git make bash build-essential \
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
+
+RUN git clone --depth=1 https://github.com/amnezia-vpn/amneziawg-tools.git && \
+ git clone --depth=1 https://github.com/amnezia-vpn/amneziawg-go.git
+RUN cd /go/amneziawg-tools/src && make
+
+RUN cd /go/amneziawg-go && \
+ go get -u ./... && \
+ go mod tidy && \
+ make && \
+ chmod +x /go/amneziawg-go/amneziawg-go /go/amneziawg-tools/src/wg /go/amneziawg-tools/src/wg-quick/linux.bash
+RUN echo "DONE AmneziaWG"
+
+### INTERMEDIATE STAGE
+FROM scratch AS bins
+COPY --from=compiler /go/amneziawg-go/amneziawg-go /amneziawg-go
+COPY --from=compiler /go/amneziawg-tools/src/wg /awg
+COPY --from=compiler /go/amneziawg-tools/src/wg-quick/linux.bash /awg-quick
+
+# FINAL STAGE
FROM alpine:latest
LABEL maintainer="dselen@nerthus.nl"
+COPY --from=bins /amneziawg-go /usr/bin/amneziawg-go
+COPY --from=bins /awg /usr/bin/awg
+COPY --from=bins /awg-quick /usr/bin/awg-quick
+
# Declaring environment variables, change Peernet to an address you like, standard is a 24 bit subnet.
ARG wg_net="10.0.0.1"
ARG wg_port="51820"
@@ -9,7 +38,8 @@ ARG wg_port="51820"
ENV TZ="Europe/Amsterdam"
ENV global_dns="1.1.1.1"
ENV isolate="none"
-ENV public_ip="0.0.0.0"
+ENV public_ip=""
+ENV wgd_port="10086"
# Doing package management operations, such as upgrading
RUN apk update \
@@ -19,14 +49,15 @@ RUN apk update \
&& apk upgrade
# Using WGDASH -- like wg_net functionally as a ARG command. But it is needed in entrypoint.sh so it needs to be exported as environment variable.
-ENV WGDASH=/opt/wireguarddashboard
+ENV WGDASH=/opt/wgdashboard
# Removing the Linux Image package to preserve space on the image, for this reason also deleting apt lists, to be able to install packages: run apt update.
# Doing WireGuard Dashboard installation measures. Modify the git clone command to get the preferred version, with a specific branch for example.
RUN mkdir /data \
&& mkdir /configs \
- && mkdir -p ${WGDASH}/src
+ && mkdir -p ${WGDASH}/src \
+ && mkdir -p /etc/amnezia/amneziawg
COPY ./src ${WGDASH}/src
# Generate basic WireGuard interface. Echoing the WireGuard interface config for readability, adjust if you want it for efficiency.
@@ -54,5 +85,6 @@ COPY entrypoint.sh /entrypoint.sh
# Exposing the default WireGuard Dashboard port for web access.
EXPOSE 10086
+WORKDIR $WGDASH
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 3d1c91b..a81a079 100644
--- a/README.md
+++ b/README.md
@@ -4,11 +4,11 @@
> [!NOTE]
> **Help Wanted 🎉**: Localizing WGDashboard to other languages! If you're willing to help, please visit https://github.com/donaldzou/WGDashboard/issues/397. Many thanks!
+
-
-
+
WGDashboard
@@ -54,8 +54,7 @@
-> [!NOTE]
-> To better manage documentation for this project. I've moved it to its own [repo](https://github.com/donaldzou/WGDashboard-Documentation). I will keep updating over there and leave this README only with important information.
+# [Official Documentation](https://donaldzou.dev/WGDashboard-Documentations)
- [💡 Features](https://donaldzou.github.io/WGDashboard-Documentation/features.html)
- [📝 Requirements](https://donaldzou.github.io/WGDashboard-Documentation/requirements.html)
@@ -63,3 +62,16 @@
- [🪜 Usage](https://donaldzou.github.io/WGDashboard-Documentation/usage.html)
- [📖 API Documentation](https://donaldzou.github.io/WGDashboard-Documentation/api-documentation.html)
- [And much more...](https://donaldzou.github.io/WGDashboard-Documentation/)
+
+# Screenshots
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docker/compose.yaml b/docker/compose.yaml
index 64dcde6..8ebef6b 100644
--- a/docker/compose.yaml
+++ b/docker/compose.yaml
@@ -8,6 +8,7 @@ services:
#- global_dns= # <--- Set global DNS address, default: 1.1.1.1.
#- isolate= # <--- Set the interfaces that will disallow peer communication, default: 'none'.
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
+ #- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
ports:
- 10086:10086/tcp
- 51820:51820/udp
diff --git a/entrypoint.sh b/entrypoint.sh
index c39fe87..da3f585 100644
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -3,6 +3,14 @@
# Path to the configuration file (exists because of previous function).
config_file="/data/wg-dashboard.ini"
+trap 'stop_service' SIGTERM
+
+stop_service() {
+ echo "[WGDashboard] Stopping WGDashboard..."
+ bash ./wgd.sh stop
+ exit 0
+}
+
echo "------------------------- START ----------------------------"
echo "Starting the WireGuard Dashboard Docker container."
@@ -10,6 +18,12 @@ ensure_installation() {
# When using a custom directory to store the files, this part moves over and makes sure the installation continues.
echo "Quick-installing..."
+ chmod +x "${WGDASH}"/src/wgd.sh
+ cd "${WGDASH}"/src || exit
+
+ echo "Removing clear command from wgd.sh for better Docker logging."
+ sed -i '/clear/d' ./wgd.sh
+
if [ ! -d "/data/db" ]; then
echo "Creating database dir"
mkdir /data/db
@@ -18,7 +32,7 @@ ensure_installation() {
if [ ! -d "${WGDASH}/src/db" ]; then
ln -s /data/db "${WGDASH}/src/db"
fi
-
+
if [ ! -f "${config_file}" ]; then
echo "Creating wg-dashboard.ini file"
touch "${config_file}"
@@ -33,13 +47,10 @@ ensure_installation() {
echo "Moving PIP dependency from ephemerality to runtime environment: psutil"
mv /usr/lib/python3.12/site-packages/psutil* "${WGDASH}"/src/venv/lib/python3.12/site-packages
-
+
echo "Moving PIP dependency from ephemerality to runtime environment: bcrypt"
mv /usr/lib/python3.12/site-packages/bcrypt* "${WGDASH}"/src/venv/lib/python3.12/site-packages
-
- chmod +x "${WGDASH}"/src/wgd.sh
- cd "${WGDASH}"/src || exit
./wgd.sh install
echo "Looks like the installation succeeded. Moving on."
@@ -69,13 +80,14 @@ set_envvars() {
# Check if the file is empty
if [ ! -s "${config_file}" ]; then
echo "Config file is empty. Creating [Peers] section."
-
+
# Create [Peers] section with initial values
{
echo "[Peers]"
echo "peer_global_dns = ${global_dns}"
echo "remote_endpoint = ${public_ip}"
- #echo -e "\n[Server]"
+ echo -e "\n[Server]"
+ echo "app_port = ${wgd_port}"
} > "${config_file}"
else
@@ -87,24 +99,32 @@ set_envvars() {
# Check and update the DNS if it has changed
current_dns=$(grep "peer_global_dns = " "${config_file}" | awk '{print $NF}')
if [ "${global_dns}" == "$current_dns" ]; then
- echo "DNS is correct, moving on."
-
+ echo "DNS is set correctly, moving on."
+
else
echo "Changing default DNS..."
sed -i "s/^peer_global_dns = .*/peer_global_dns = ${global_dns}/" "${config_file}"
fi
- if [ "${public_ip}" == "0.0.0.0" ]; then
-
+ current_public_ip=$(grep "remote_endpoint = " "${config_file}" | awk '{print $NF}')
+ if [ "${public_ip}" == "" ]; then
default_ip=$(curl -s ifconfig.me)
echo "Trying to fetch the Public-IP using ifconfig.me: ${default_ip}"
sed -i "s/^remote_endpoint = .*/remote_endpoint = ${default_ip}/" "${config_file}"
-
+ elif [ "${current_public_ip}" != "${public_ip}" ]; then
+ sed -i "s/^remote_endpoint = .*/remote_endpoint = ${public_ip}/" "${config_file}"
else
echo "Public-IP is correct, moving on."
fi
+ current_wgd_port=$(grep "app_port = " "${config_file}" | awk '{print $NF}')
+ if [ "${current_wgd_port}" == "${wgd_port}" ]; then
+ echo "Current WGD port is set correctly, moving on."
+ else
+ echo "Changing default WGD port..."
+ sed -i "s/^app_port = .*/app_port = ${wgd_port}/" "${config_file}"
+ fi
}
# === CORE SERVICES ===
@@ -112,99 +132,7 @@ start_core() {
printf "\n---------------------- STARTING CORE -----------------------\n"
echo "Activating Python venv and executing the WireGuard Dashboard service."
-
- . "${WGDASH}"/src/venv/bin/activate
- cd "${WGDASH}"/src || return
- bash wgd.sh start
-
- # Isolated peers feature, first converting the existing configuration files and the given names to arrays.
- #
- # WILL BE REMOVED IN FUTURE WHEN WGDASHBOARD ITSELF SUPPORTS THIS!!
- #
-
- local configurations=(/etc/wireguard/*)
- IFS=',' read -r -a do_isolate <<< "${isolate}"
- non_isolate=()
-
- # Checking if there are matches between the two arrays.
- for config in "${configurations[@]}"; do
- config=$(echo "$config" | sed -e 's|.*/etc/wireguard/||' -e 's|\.conf$||')
-
- local found
- found=false
-
- for interface in "${do_isolate[@]}"; do
-
- if [[ "$config" == "$interface" ]]; then
- found=true
- break
- fi
-
- done
-
- if [ "$found" = false ]; then
- non_isolate+=("$config")
- fi
-
- done
-
- # Isolating the matches.
- noneFound=0
-
- for interface in "${do_isolate[@]}"; do
-
- if [ "$interface" = "none" ] || [ "$interface" = "" ]; then
- echo "Found none, stopping isolation checking."
- noneFound=1
- break
-
- else
-
- if [ ! -f "/etc/wireguard/${interface}.conf" ]; then
- echo "Ignoring ${interface}"
-
- elif [ -f "/etc/wireguard/${interface}.conf" ]; then
-
-
- echo "Isolating interface:" "$interface"
-
- upblocking=$(grep -c "PostUp = iptables -I FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf)
- downblocking=$(grep -c "PreDown = iptables -D FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf)
-
- if [ "$upblocking" -lt 1 ] && [ "$downblocking" -lt 1 ]; then
- sed -i "/PostUp =/a PostUp = iptables -I FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf
- sed -i "/PreDown =/a PreDown = iptables -D FORWARD -i ${interface} -o ${interface} -j DROP" /etc/wireguard/"${interface}".conf
- fi
-
- else
- echo "Configuration for $interface in enforce isolation does not seem to exist, continuing."
- fi
-
- fi
-
- done
-
- # Removing isolation for the configurations that did not match.
-
-
- for interface in "${non_isolate[@]}"; do
- if [ $noneFound -eq 1 ]; then
- break
-
- elif [ ! -f "/etc/wireguard/${interface}.conf" ]; then
- echo "Ignoring ${interface}"
-
- elif [ -f "/etc/wireguard/${interface}.conf" ]; then
- echo "Removing isolation, if isolation is present for:" "$interface"
-
- sed -i "/PostUp = iptables -I FORWARD -i ${interface} -o ${interface} -j DROP/d" /etc/wireguard/"${interface}".conf
- sed -i "/PreDown = iptables -D FORWARD -i ${interface} -o ${interface} -j DROP/d" /etc/wireguard/"${interface}".conf
- else
- echo "Configuration for $interface in removing isolation does not seem to exist, continuing."
- fi
-
- done
-
+ bash ./wgd.sh start
}
ensure_blocking() {
@@ -212,24 +140,21 @@ ensure_blocking() {
echo -e "\nEnsuring container continuation."
# Find and tail the latest error and access logs if they exist
- local logdir="/opt/wireguarddashboard/src/log"
-
+ local logdir="${WGDASH}/src/log"
+
latestErrLog=$(find "$logdir" -name "error_*.log" -type f -print | sort -r | head -n 1)
- latestAccLog=$(find "$logdir" -name "access_*.log" -type f -print | sort -r | head -n 1)
# Only tail the logs if they are found
- if [ -n "$latestErrLog" ] || [ -n "$latestAccLog" ]; then
- tail -f "$latestErrLog" "$latestAccLog"
+ if [ -n "$latestErrLog" ]; then
+ tail -f "$latestErrLog" &
+ wait $!
else
- echo "No log files found to tail."
+ echo "No log files found to tail. Something went wrong, exiting..."
fi
-
- # Blocking command to keep the container running as a last resort.
- sleep infinity
}
# Execute functions for the WireGuard Dashboard services, then set the environment variables
ensure_installation
set_envvars
start_core
-ensure_blocking
\ No newline at end of file
+ensure_blocking
diff --git a/package-lock.json b/package-lock.json
index 7bc317b..3c274ec 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,373 +5,139 @@
"packages": {
"": {
"dependencies": {
- "@volar/language-server": "2.4.0-alpha.18",
- "@vue/language-server": "2.0.28",
- "ag-charts-vue3": "^10.3.1",
- "dayjs": "^1.11.12"
+ "marked": "^15.0.7",
+ "openai": "^4.89.0",
+ "pinia-plugin-persistedstate": "^4.2.0"
}
},
- "node_modules/@babel/helper-string-parser": {
- "version": "7.25.9",
- "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
- "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.25.9",
- "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
- "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/parser": {
- "version": "7.26.2",
- "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.2.tgz",
- "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
- "dependencies": {
- "@babel/types": "^7.26.0"
- },
- "bin": {
- "parser": "bin/babel-parser.js"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/types": {
- "version": "7.26.0",
- "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.26.0.tgz",
- "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
- "dependencies": {
- "@babel/helper-string-parser": "^7.25.9",
- "@babel/helper-validator-identifier": "^7.25.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@emmetio/abbreviation": {
- "version": "2.3.3",
- "resolved": "https://registry.npmmirror.com/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz",
- "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==",
- "dependencies": {
- "@emmetio/scanner": "^1.0.4"
- }
- },
- "node_modules/@emmetio/css-abbreviation": {
- "version": "2.1.8",
- "resolved": "https://registry.npmmirror.com/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz",
- "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==",
- "dependencies": {
- "@emmetio/scanner": "^1.0.4"
- }
- },
- "node_modules/@emmetio/css-parser": {
- "version": "0.4.0",
- "resolved": "https://registry.npmmirror.com/@emmetio/css-parser/-/css-parser-0.4.0.tgz",
- "integrity": "sha512-z7wkxRSZgrQHXVzObGkXG+Vmj3uRlpM11oCZ9pbaz0nFejvCDmAiNDpY75+wgXOcffKpj4rzGtwGaZxfJKsJxw==",
- "dependencies": {
- "@emmetio/stream-reader": "^2.2.0",
- "@emmetio/stream-reader-utils": "^0.1.0"
- }
- },
- "node_modules/@emmetio/html-matcher": {
- "version": "1.3.0",
- "resolved": "https://registry.npmmirror.com/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz",
- "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==",
- "dependencies": {
- "@emmetio/scanner": "^1.0.0"
- }
- },
- "node_modules/@emmetio/scanner": {
- "version": "1.0.4",
- "resolved": "https://registry.npmmirror.com/@emmetio/scanner/-/scanner-1.0.4.tgz",
- "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA=="
- },
- "node_modules/@emmetio/stream-reader": {
- "version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz",
- "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw=="
- },
- "node_modules/@emmetio/stream-reader-utils": {
- "version": "0.1.0",
- "resolved": "https://registry.npmmirror.com/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz",
- "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A=="
- },
- "node_modules/@johnsoncodehk/pug-beautify": {
- "version": "0.2.2",
- "resolved": "https://registry.npmmirror.com/@johnsoncodehk/pug-beautify/-/pug-beautify-0.2.2.tgz",
- "integrity": "sha512-qqNS/YD0Nck5wtQLCPHAfGVgWbbGafxSPjNh0ekYPFSNNqnDH2kamnduzYly8IiADmeVx/MfAE1njMEjVeHTMA=="
- },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "peer": true
+ "license": "MIT"
},
- "node_modules/@volar/language-core": {
- "version": "2.4.0-alpha.18",
- "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.0-alpha.18.tgz",
- "integrity": "sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==",
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "license": "MIT",
"dependencies": {
- "@volar/source-map": "2.4.0-alpha.18"
- }
- },
- "node_modules/@volar/language-server": {
- "version": "2.4.0-alpha.18",
- "resolved": "https://registry.npmmirror.com/@volar/language-server/-/language-server-2.4.0-alpha.18.tgz",
- "integrity": "sha512-dciHEE/R5kzI0bY71QfkoCVQ3cQI6g9MHfA4oIP6UhnJy0CdleUalWSygOXoD3Nq7Yk6wn2BRrb1PP5MsadY/Q==",
- "dependencies": {
- "@volar/language-core": "2.4.0-alpha.18",
- "@volar/language-service": "2.4.0-alpha.18",
- "@volar/snapshot-document": "2.4.0-alpha.18",
- "@volar/typescript": "2.4.0-alpha.18",
- "path-browserify": "^1.0.1",
- "request-light": "^0.7.0",
- "vscode-languageserver": "^9.0.1",
- "vscode-languageserver-protocol": "^3.17.5",
- "vscode-languageserver-textdocument": "^1.0.11",
- "vscode-uri": "^3.0.8"
- }
- },
- "node_modules/@volar/language-service": {
- "version": "2.4.0-alpha.18",
- "resolved": "https://registry.npmmirror.com/@volar/language-service/-/language-service-2.4.0-alpha.18.tgz",
- "integrity": "sha512-EuetrtbEtudi9buinWAG5U3Jam5dY27zXd/7GYnx542kBwanWOBM8i4DAQd0z7M11fOxXgybxPA933uaSyaOog==",
- "dependencies": {
- "@volar/language-core": "2.4.0-alpha.18",
- "vscode-languageserver-protocol": "^3.17.5",
- "vscode-languageserver-textdocument": "^1.0.11",
- "vscode-uri": "^3.0.8"
- }
- },
- "node_modules/@volar/snapshot-document": {
- "version": "2.4.0-alpha.18",
- "resolved": "https://registry.npmmirror.com/@volar/snapshot-document/-/snapshot-document-2.4.0-alpha.18.tgz",
- "integrity": "sha512-JAeclEly/wnILhR4Pu9MpgBLInZJH49O1zoy8fU+pk5I+zpv7JIEby5z2UFAS60+sIDnxBdAGd7rZ5VibE70vg==",
- "dependencies": {
- "vscode-languageserver-protocol": "^3.17.5",
- "vscode-languageserver-textdocument": "^1.0.11"
- }
- },
- "node_modules/@volar/source-map": {
- "version": "2.4.0-alpha.18",
- "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.0-alpha.18.tgz",
- "integrity": "sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g=="
- },
- "node_modules/@volar/typescript": {
- "version": "2.4.0-alpha.18",
- "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.0-alpha.18.tgz",
- "integrity": "sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==",
- "dependencies": {
- "@volar/language-core": "2.4.0-alpha.18",
- "path-browserify": "^1.0.1",
- "vscode-uri": "^3.0.8"
- }
- },
- "node_modules/@vscode/emmet-helper": {
- "version": "2.9.3",
- "resolved": "https://registry.npmmirror.com/@vscode/emmet-helper/-/emmet-helper-2.9.3.tgz",
- "integrity": "sha512-rB39LHWWPQYYlYfpv9qCoZOVioPCftKXXqrsyqN1mTWZM6dTnONT63Db+03vgrBbHzJN45IrgS/AGxw9iiqfEw==",
- "dependencies": {
- "emmet": "^2.4.3",
- "jsonc-parser": "^2.3.0",
- "vscode-languageserver-textdocument": "^1.0.1",
- "vscode-languageserver-types": "^3.15.1",
- "vscode-uri": "^2.1.2"
- }
- },
- "node_modules/@vscode/emmet-helper/node_modules/vscode-uri": {
- "version": "2.1.2",
- "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-2.1.2.tgz",
- "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A=="
- },
- "node_modules/@vscode/l10n": {
- "version": "0.0.18",
- "resolved": "https://registry.npmmirror.com/@vscode/l10n/-/l10n-0.0.18.tgz",
- "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="
- },
- "node_modules/@vue/compiler-core": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz",
- "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==",
- "dependencies": {
- "@babel/parser": "^7.25.3",
- "@vue/shared": "3.5.12",
- "entities": "^4.5.0",
- "estree-walker": "^2.0.2",
- "source-map-js": "^1.2.0"
- }
- },
- "node_modules/@vue/compiler-dom": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz",
- "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==",
- "dependencies": {
- "@vue/compiler-core": "3.5.12",
- "@vue/shared": "3.5.12"
- }
- },
- "node_modules/@vue/compiler-sfc": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz",
- "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==",
- "peer": true,
- "dependencies": {
- "@babel/parser": "^7.25.3",
- "@vue/compiler-core": "3.5.12",
- "@vue/compiler-dom": "3.5.12",
- "@vue/compiler-ssr": "3.5.12",
- "@vue/shared": "3.5.12",
- "estree-walker": "^2.0.2",
- "magic-string": "^0.30.11",
- "postcss": "^8.4.47",
- "source-map-js": "^1.2.0"
- }
- },
- "node_modules/@vue/compiler-ssr": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz",
- "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==",
- "peer": true,
- "dependencies": {
- "@vue/compiler-dom": "3.5.12",
- "@vue/shared": "3.5.12"
- }
- },
- "node_modules/@vue/language-core": {
- "version": "2.0.28",
- "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.0.28.tgz",
- "integrity": "sha512-0z4tyCCaqqPbdyz0T4yTFQeLpCo4TOM/ZHAC3geGLHeCiFAjVbROB9PiEtrXR1AoLObqUPFHSmKZeWtEMssSqw==",
- "dependencies": {
- "@volar/language-core": "~2.4.0-alpha.18",
- "@vue/compiler-dom": "^3.4.0",
- "@vue/shared": "^3.4.0",
- "computeds": "^0.0.1",
- "minimatch": "^9.0.3",
- "muggle-string": "^0.4.1",
- "path-browserify": "^1.0.1",
- "vue-template-compiler": "^2.7.14"
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
},
- "peerDependencies": {
- "typescript": "*"
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
},
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "engines": {
+ "node": ">= 8"
}
},
- "node_modules/@vue/language-server": {
- "version": "2.0.28",
- "resolved": "https://registry.npmmirror.com/@vue/language-server/-/language-server-2.0.28.tgz",
- "integrity": "sha512-V5c54qz3+Udbxhb7FqcwiLcSzdy1LZLUDA6awd8/CjGhx6gb5FhXq4DjvqS71WkR8F/u+J+6pUw56JlzFyj5fg==",
+ "node_modules/@nuxt/kit": {
+ "version": "3.16.1",
+ "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.16.1.tgz",
+ "integrity": "sha512-Perby8hJGUeCWad5oTVXb/Ibvp18ZCUC5PxHHu+acMDmVfnxSo48yqk7qNd09VkTF3LEzoEjNZpmW2ZWN0ry7A==",
+ "license": "MIT",
"dependencies": {
- "@volar/language-core": "~2.4.0-alpha.18",
- "@volar/language-server": "~2.4.0-alpha.18",
- "@vue/language-core": "2.0.28",
- "@vue/language-service": "2.0.28",
- "@vue/typescript-plugin": "2.0.28",
- "vscode-languageserver-protocol": "^3.17.5",
- "vscode-uri": "^3.0.8"
+ "c12": "^3.0.2",
+ "consola": "^3.4.2",
+ "defu": "^6.1.4",
+ "destr": "^2.0.3",
+ "errx": "^0.1.0",
+ "exsolve": "^1.0.4",
+ "globby": "^14.1.0",
+ "ignore": "^7.0.3",
+ "jiti": "^2.4.2",
+ "klona": "^2.0.6",
+ "knitwork": "^1.2.0",
+ "mlly": "^1.7.4",
+ "ohash": "^2.0.11",
+ "pathe": "^2.0.3",
+ "pkg-types": "^2.1.0",
+ "scule": "^1.3.0",
+ "semver": "^7.7.1",
+ "std-env": "^3.8.1",
+ "ufo": "^1.5.4",
+ "unctx": "^2.4.1",
+ "unimport": "^4.1.2",
+ "untyped": "^2.0.0"
},
- "bin": {
- "vue-language-server": "bin/vue-language-server.js"
+ "engines": {
+ "node": ">=18.12.0"
}
},
- "node_modules/@vue/language-service": {
- "version": "2.0.28",
- "resolved": "https://registry.npmmirror.com/@vue/language-service/-/language-service-2.0.28.tgz",
- "integrity": "sha512-DN0RhYZ6wcrzrYLnHBmRbo8XOFYqBqj60QKq5/7BS1zEQlSXoNJbT+8IqB/OdUFAHKLiV45tvfjCyr3CY0mK3g==",
- "dependencies": {
- "@volar/language-core": "~2.4.0-alpha.18",
- "@volar/language-service": "~2.4.0-alpha.18",
- "@volar/typescript": "~2.4.0-alpha.18",
- "@vue/compiler-dom": "^3.4.0",
- "@vue/language-core": "2.0.28",
- "@vue/shared": "^3.4.0",
- "@vue/typescript-plugin": "2.0.28",
- "computeds": "^0.0.1",
- "path-browserify": "^1.0.1",
- "volar-service-css": "0.0.59",
- "volar-service-emmet": "0.0.59",
- "volar-service-html": "0.0.59",
- "volar-service-json": "0.0.59",
- "volar-service-pug": "0.0.59",
- "volar-service-pug-beautify": "0.0.59",
- "volar-service-typescript": "0.0.59",
- "volar-service-typescript-twoslash-queries": "0.0.59",
- "vscode-html-languageservice": "^5.2.0",
- "vscode-languageserver-textdocument": "^1.0.11",
- "vscode-uri": "^3.0.8"
- }
- },
- "node_modules/@vue/reactivity": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.12.tgz",
- "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==",
- "peer": true,
- "dependencies": {
- "@vue/shared": "3.5.12"
- }
- },
- "node_modules/@vue/runtime-core": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.12.tgz",
- "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==",
- "peer": true,
- "dependencies": {
- "@vue/reactivity": "3.5.12",
- "@vue/shared": "3.5.12"
- }
- },
- "node_modules/@vue/runtime-dom": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz",
- "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==",
- "peer": true,
- "dependencies": {
- "@vue/reactivity": "3.5.12",
- "@vue/runtime-core": "3.5.12",
- "@vue/shared": "3.5.12",
- "csstype": "^3.1.3"
- }
- },
- "node_modules/@vue/server-renderer": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.12.tgz",
- "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==",
- "peer": true,
- "dependencies": {
- "@vue/compiler-ssr": "3.5.12",
- "@vue/shared": "3.5.12"
+ "node_modules/@sindresorhus/merge-streams": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
+ "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
},
- "peerDependencies": {
- "vue": "3.5.12"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@vue/shared": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.12.tgz",
- "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg=="
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "license": "MIT"
},
- "node_modules/@vue/typescript-plugin": {
- "version": "2.0.28",
- "resolved": "https://registry.npmmirror.com/@vue/typescript-plugin/-/typescript-plugin-2.0.28.tgz",
- "integrity": "sha512-/NQP5reqT7WtfI3+GBKeDF6yLeHQM8Wl0VQMXOz0rpi4cirvUg/E8XNrLnLgHot2VzXfm23DoTJCEBgfYGmOUQ==",
+ "node_modules/@types/node": {
+ "version": "18.19.81",
+ "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.81.tgz",
+ "integrity": "sha512-7KO9oZ2//ivtSsryp0LQUqq79zyGXzwq1WqfywpC9ucjY7YyltMMmxWgtRFRKCxwa7VPxVBVy4kHf5UC1E8Lug==",
+ "license": "MIT",
"dependencies": {
- "@volar/typescript": "~2.4.0-alpha.18",
- "@vue/language-core": "2.0.28",
- "@vue/shared": "^3.4.0"
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.12.tgz",
+ "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
}
},
"node_modules/acorn": {
- "version": "7.4.1",
- "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
- "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "version": "8.14.1",
+ "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
@@ -379,146 +145,192 @@
"node": ">=0.4.0"
}
},
- "node_modules/ag-charts-community": {
- "version": "10.3.1",
- "resolved": "https://registry.npmmirror.com/ag-charts-community/-/ag-charts-community-10.3.1.tgz",
- "integrity": "sha512-/FyXVoSkErYvWZWyAahRNtkaT0A6RH6d16JsgVPEzyu/hUWRpknXAAplz9jQvuIKPg7tEUfjGcl33C7v64v8Ew==",
- "peer": true,
+ "node_modules/agentkeepalive": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
+ "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
+ "license": "MIT",
"dependencies": {
- "ag-charts-locale": "10.3.1",
- "ag-charts-types": "10.3.1"
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
}
},
- "node_modules/ag-charts-locale": {
- "version": "10.3.1",
- "resolved": "https://registry.npmmirror.com/ag-charts-locale/-/ag-charts-locale-10.3.1.tgz",
- "integrity": "sha512-ZI2wNECqOXbr2C8RfSxu4LOEWktYZuSCuZIBYc0VoR3ETPE05/RaI9KdnXYPau36asoxzLHWfbeNNeyBg8sNMg==",
- "peer": true
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
},
- "node_modules/ag-charts-types": {
- "version": "10.3.1",
- "resolved": "https://registry.npmmirror.com/ag-charts-types/-/ag-charts-types-10.3.1.tgz",
- "integrity": "sha512-oZvu9vJLk6lmzaYi0TmVVmHFZJpVNFziU0bnllx4wR3muXCmnxz5LouKIZ8CYnNiC7VO5HmHNlFu+0DmEO5zxg==",
- "peer": true
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
},
- "node_modules/ag-charts-vue3": {
- "version": "10.3.1",
- "resolved": "https://registry.npmmirror.com/ag-charts-vue3/-/ag-charts-vue3-10.3.1.tgz",
- "integrity": "sha512-NaYy9zyDGwXKJmaP+1GM+YD89iOUcvjtUf8fLpx+hidu/aQMkHpBLhsvPG5YRIbcTptlcfPiAaiyOaYCIL83sg==",
+ "node_modules/c12": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/c12/-/c12-3.0.2.tgz",
+ "integrity": "sha512-6Tzk1/TNeI3WBPpK0j/Ss4+gPj3PUJYbWl/MWDJBThFvwNGNkXtd7Cz8BJtD4aRwoGHtzQD0SnxamgUiBH0/Nw==",
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^4.0.3",
+ "confbox": "^0.1.8",
+ "defu": "^6.1.4",
+ "dotenv": "^16.4.7",
+ "exsolve": "^1.0.0",
+ "giget": "^2.0.0",
+ "jiti": "^2.4.2",
+ "ohash": "^2.0.5",
+ "pathe": "^2.0.3",
+ "perfect-debounce": "^1.0.0",
+ "pkg-types": "^2.0.0",
+ "rc9": "^2.1.2"
+ },
"peerDependencies": {
- "ag-charts-community": "10.3.1",
- "vue": "^3.0.0"
+ "magicast": "^0.3.5"
+ },
+ "peerDependenciesMeta": {
+ "magicast": {
+ "optional": true
+ }
}
},
- "node_modules/balanced-match": {
+ "node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
- "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
- },
- "node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/call-bind": {
- "version": "1.0.7",
- "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz",
- "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
- "dependencies": {
- "es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "set-function-length": "^1.2.1"
+ "function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
},
"funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "url": "https://paulmillr.com/funding/"
}
},
- "node_modules/character-parser": {
- "version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/character-parser/-/character-parser-2.2.0.tgz",
- "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==",
+ "node_modules/citty": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz",
+ "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
+ "license": "MIT",
"dependencies": {
- "is-regex": "^1.0.3"
+ "consola": "^3.2.3"
}
},
- "node_modules/computeds": {
- "version": "0.0.1",
- "resolved": "https://registry.npmmirror.com/computeds/-/computeds-0.0.1.tgz",
- "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q=="
- },
- "node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "peer": true
- },
- "node_modules/dayjs": {
- "version": "1.11.12",
- "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz",
- "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg=="
- },
- "node_modules/de-indent": {
- "version": "1.0.2",
- "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
- "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
- },
- "node_modules/define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
"dependencies": {
- "es-define-property": "^1.0.0",
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/confbox": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz",
+ "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "license": "MIT"
+ },
+ "node_modules/consola": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz",
+ "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.18.0 || >=16.10.0"
+ }
+ },
+ "node_modules/deep-pick-omit": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz",
+ "integrity": "sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==",
+ "license": "MIT"
+ },
+ "node_modules/defu": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz",
+ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
+ "license": "MIT"
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/destr": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.3.tgz",
+ "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==",
+ "license": "MIT"
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
- "gopd": "^1.0.1"
+ "gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/emmet": {
- "version": "2.4.11",
- "resolved": "https://registry.npmmirror.com/emmet/-/emmet-2.4.11.tgz",
- "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==",
- "workspaces": [
- "./packages/scanner",
- "./packages/abbreviation",
- "./packages/css-abbreviation",
- "./"
- ],
- "dependencies": {
- "@emmetio/abbreviation": "^2.3.3",
- "@emmetio/css-abbreviation": "^2.1.8"
- }
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
+ "node_modules/errx": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmmirror.com/errx/-/errx-0.1.0.tgz",
+ "integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==",
+ "license": "MIT"
},
"node_modules/es-define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz",
- "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
- "dependencies": {
- "get-intrinsic": "^1.2.4"
- },
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
}
@@ -527,33 +339,170 @@
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/estree-walker": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/exsolve": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.4.tgz",
+ "integrity": "sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==",
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz",
+ "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data-encoder": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmmirror.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
+ "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
+ "license": "MIT"
+ },
+ "node_modules/formdata-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmmirror.com/formdata-node/-/formdata-node-4.4.1.tgz",
+ "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "1.0.0",
+ "web-streams-polyfill": "4.0.0-beta.3"
+ },
+ "engines": {
+ "node": ">= 12.20"
+ }
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
- "version": "1.2.4",
- "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
- "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
"dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
- "has-proto": "^1.0.1",
- "has-symbols": "^1.0.3",
- "hasown": "^2.0.0"
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -562,32 +511,73 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/gopd": {
+ "node_modules/get-proto": {
"version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
"dependencies": {
- "get-intrinsic": "^1.1.3"
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "engines": {
+ "node": ">= 0.4"
}
},
- "node_modules/has-property-descriptors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "node_modules/giget": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/giget/-/giget-2.0.0.tgz",
+ "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
+ "license": "MIT",
"dependencies": {
- "es-define-property": "^1.0.0"
+ "citty": "^0.1.6",
+ "consola": "^3.4.0",
+ "defu": "^6.1.4",
+ "node-fetch-native": "^1.6.6",
+ "nypm": "^0.6.0",
+ "pathe": "^2.0.3"
},
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "bin": {
+ "giget": "dist/cli.mjs"
}
},
- "node_modules/has-proto": {
- "version": "1.0.3",
- "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz",
- "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/globby": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmmirror.com/globby/-/globby-14.1.0.tgz",
+ "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/merge-streams": "^2.1.0",
+ "fast-glob": "^3.3.3",
+ "ignore": "^7.0.3",
+ "path-type": "^6.0.0",
+ "slash": "^5.1.0",
+ "unicorn-magic": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -596,9 +586,10 @@
}
},
"node_modules/has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -610,6 +601,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
@@ -624,6 +616,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -631,169 +624,483 @@
"node": ">= 0.4"
}
},
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "bin": {
- "he": "bin/he"
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.0.0"
}
},
- "node_modules/is-expression": {
- "version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/is-expression/-/is-expression-4.0.0.tgz",
- "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==",
- "dependencies": {
- "acorn": "^7.1.1",
- "object-assign": "^4.1.1"
- }
- },
- "node_modules/is-regex": {
- "version": "1.1.4",
- "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.1.4.tgz",
- "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
- "dependencies": {
- "call-bind": "^1.0.2",
- "has-tostringtag": "^1.0.0"
- },
+ "node_modules/ignore": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.3.tgz",
+ "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==",
+ "license": "MIT",
"engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
+ "node": ">= 4"
}
},
- "node_modules/jsonc-parser": {
- "version": "2.3.1",
- "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz",
- "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="
- },
- "node_modules/magic-string": {
- "version": "0.30.12",
- "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.12.tgz",
- "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==",
- "peer": true,
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0"
- }
- },
- "node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/muggle-string": {
- "version": "0.4.1",
- "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz",
- "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="
- },
- "node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "peer": true,
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/path-browserify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
- "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
},
- "node_modules/picocolors": {
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.4.2.tgz",
+ "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
+ "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
+ "license": "MIT"
+ },
+ "node_modules/klona": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz",
+ "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/knitwork": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/knitwork/-/knitwork-1.2.0.tgz",
+ "integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==",
+ "license": "MIT"
+ },
+ "node_modules/local-pkg": {
"version": "1.1.1",
- "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "peer": true
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.1.tgz",
+ "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
+ "license": "MIT",
+ "dependencies": {
+ "mlly": "^1.7.4",
+ "pkg-types": "^2.0.1",
+ "quansync": "^0.2.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
},
- "node_modules/postcss": {
- "version": "8.4.47",
- "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.47.tgz",
- "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/marked": {
+ "version": "15.0.7",
+ "resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.7.tgz",
+ "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mlly": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.7.4.tgz",
+ "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "pathe": "^2.0.1",
+ "pkg-types": "^1.3.0",
+ "ufo": "^1.5.4"
+ }
+ },
+ "node_modules/mlly/node_modules/pkg-types": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz",
+ "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.1.8",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.1"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
- "url": "https://github.com/sponsors/ai"
+ "url": "https://paypal.me/jimmywarting"
}
],
- "peer": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "license": "MIT",
"dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.1.0",
- "source-map-js": "^1.2.1"
+ "whatwg-url": "^5.0.0"
},
"engines": {
- "node": "^10 || ^12 || >=14"
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
}
},
- "node_modules/pug-error": {
- "version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/pug-error/-/pug-error-2.1.0.tgz",
- "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg=="
+ "node_modules/node-fetch-native": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.6.tgz",
+ "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==",
+ "license": "MIT"
},
- "node_modules/pug-lexer": {
- "version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/pug-lexer/-/pug-lexer-5.0.1.tgz",
- "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==",
+ "node_modules/nypm": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmmirror.com/nypm/-/nypm-0.6.0.tgz",
+ "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==",
+ "license": "MIT",
"dependencies": {
- "character-parser": "^2.2.0",
- "is-expression": "^4.0.0",
- "pug-error": "^2.0.0"
+ "citty": "^0.1.6",
+ "consola": "^3.4.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^2.0.0",
+ "tinyexec": "^0.3.2"
+ },
+ "bin": {
+ "nypm": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": "^14.16.0 || >=16.10.0"
}
},
- "node_modules/pug-parser": {
+ "node_modules/ohash": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
+ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
+ "license": "MIT"
+ },
+ "node_modules/openai": {
+ "version": "4.89.0",
+ "resolved": "https://registry.npmmirror.com/openai/-/openai-4.89.0.tgz",
+ "integrity": "sha512-XNI0q2l8/Os6jmojxaID5EhyQjxZgzR2gWcpEjYWK5hGKwE7AcifxEY7UNwFDDHJQXqeiosQ0CJwQN+rvnwdjA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/node": "^18.11.18",
+ "@types/node-fetch": "^2.6.4",
+ "abort-controller": "^3.0.0",
+ "agentkeepalive": "^4.2.1",
+ "form-data-encoder": "1.7.2",
+ "formdata-node": "^4.3.2",
+ "node-fetch": "^2.6.7"
+ },
+ "bin": {
+ "openai": "bin/cli"
+ },
+ "peerDependencies": {
+ "ws": "^8.18.0",
+ "zod": "^3.23.8"
+ },
+ "peerDependenciesMeta": {
+ "ws": {
+ "optional": true
+ },
+ "zod": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/path-type": {
"version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/pug-parser/-/pug-parser-6.0.0.tgz",
- "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==",
- "dependencies": {
- "pug-error": "^2.0.0",
- "token-stream": "1.0.0"
+ "resolved": "https://registry.npmmirror.com/path-type/-/path-type-6.0.0.tgz",
+ "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/request-light": {
- "version": "0.7.0",
- "resolved": "https://registry.npmmirror.com/request-light/-/request-light-0.7.0.tgz",
- "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "license": "MIT"
+ },
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "license": "MIT"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pinia-plugin-persistedstate": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.2.0.tgz",
+ "integrity": "sha512-3buhA7ac+ssbOIx3VRCC8oHkoFwhDM9oHRCjo7nj+O8WUqnW+jRqh7eYT5eS/DNa3H28zp3dYf/nd/Vc8zj8eQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@nuxt/kit": "^3.14.1592",
+ "deep-pick-omit": "^1.2.1",
+ "defu": "^6.1.4",
+ "destr": "^2.0.3"
+ },
+ "peerDependencies": {
+ "@pinia/nuxt": ">=0.9.0",
+ "pinia": ">=2.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@pinia/nuxt": {
+ "optional": true
+ },
+ "pinia": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pkg-types": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.1.0.tgz",
+ "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==",
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.2.1",
+ "exsolve": "^1.0.1",
+ "pathe": "^2.0.3"
+ }
+ },
+ "node_modules/pkg-types/node_modules/confbox": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.1.tgz",
+ "integrity": "sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==",
+ "license": "MIT"
+ },
+ "node_modules/quansync": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.10.tgz",
+ "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/antfu"
+ },
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/rc9": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/rc9/-/rc9-2.1.2.tgz",
+ "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
+ "license": "MIT",
+ "dependencies": {
+ "defu": "^6.1.4",
+ "destr": "^2.0.3"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/scule": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
+ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
+ "license": "MIT"
},
"node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "version": "7.7.1",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
@@ -801,297 +1108,261 @@
"node": ">=10"
}
},
- "node_modules/set-function-length": {
- "version": "1.2.2",
- "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "node_modules/slash": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/slash/-/slash-5.1.0.tgz",
+ "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/std-env": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.8.1.tgz",
+ "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==",
+ "license": "MIT"
+ },
+ "node_modules/strip-literal": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.0.0.tgz",
+ "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
+ "license": "MIT",
"dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2"
+ "js-tokens": "^9.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.12.tgz",
+ "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.3",
+ "picomatch": "^4.0.2"
},
"engines": {
- "node": ">= 0.4"
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.3.tgz",
+ "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "license": "MIT",
"engines": {
- "node": ">=0.10.0"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/token-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/token-stream/-/token-stream-1.0.0.tgz",
- "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg=="
- },
- "node_modules/typescript-auto-import-cache": {
- "version": "0.3.5",
- "resolved": "https://registry.npmmirror.com/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.5.tgz",
- "integrity": "sha512-fAIveQKsoYj55CozUiBoj4b/7WpN0i4o74wiGY5JVUEoD0XiqDk1tJqTEjgzL2/AizKQrXxyRosSebyDzBZKjw==",
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "license": "MIT",
"dependencies": {
- "semver": "^7.3.8"
- }
- },
- "node_modules/volar-service-css": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-css/-/volar-service-css-0.0.59.tgz",
- "integrity": "sha512-gLNjJnECbalPvQB7qeJjhkDN8sR5M3ItbVYjnyio61aHaWptIiXm/HfDahcQ2ApwmvWidkMWWegjGq5L0BENDA==",
- "dependencies": {
- "vscode-css-languageservice": "^6.3.0",
- "vscode-languageserver-textdocument": "^1.0.11",
- "vscode-uri": "^3.0.8"
+ "is-number": "^7.0.0"
},
- "peerDependencies": {
- "@volar/language-service": "~2.4.0-alpha.12"
- },
- "peerDependenciesMeta": {
- "@volar/language-service": {
- "optional": true
- }
- }
- },
- "node_modules/volar-service-emmet": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-emmet/-/volar-service-emmet-0.0.59.tgz",
- "integrity": "sha512-6EynHcuMwMBETpK29TbZvIMmvzdVG+Tkokk9VWfZeI+SwDptk2tgdhEqiXXvIkqYNgbuu73Itp66lpH76cAU+Q==",
- "dependencies": {
- "@emmetio/css-parser": "^0.4.0",
- "@emmetio/html-matcher": "^1.3.0",
- "@vscode/emmet-helper": "^2.9.3",
- "vscode-uri": "^3.0.8"
- },
- "peerDependencies": {
- "@volar/language-service": "~2.4.0-alpha.12"
- },
- "peerDependenciesMeta": {
- "@volar/language-service": {
- "optional": true
- }
- }
- },
- "node_modules/volar-service-html": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-html/-/volar-service-html-0.0.59.tgz",
- "integrity": "sha512-hEXOsYpILDlITZxnqRLV9OepVWD63GZBsyjMxszwdzlxvGZjzbGcBBinJGGJRwFIV8djdJwnt91bkdg1V5tj6Q==",
- "dependencies": {
- "vscode-html-languageservice": "^5.3.0",
- "vscode-languageserver-textdocument": "^1.0.11",
- "vscode-uri": "^3.0.8"
- },
- "peerDependencies": {
- "@volar/language-service": "~2.4.0-alpha.12"
- },
- "peerDependenciesMeta": {
- "@volar/language-service": {
- "optional": true
- }
- }
- },
- "node_modules/volar-service-json": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-json/-/volar-service-json-0.0.59.tgz",
- "integrity": "sha512-LfDOQhCvUpDBjA6CP9EogO0dn1yEFbInvV3Yk4OsEdyxwWUEYPLVjDacPlVUYcjCIKQN6NcTOWbVwpg4vYjw6A==",
- "dependencies": {
- "vscode-json-languageservice": "^5.4.0",
- "vscode-uri": "^3.0.8"
- },
- "peerDependencies": {
- "@volar/language-service": "~2.4.0-alpha.12"
- },
- "peerDependenciesMeta": {
- "@volar/language-service": {
- "optional": true
- }
- }
- },
- "node_modules/volar-service-pug": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-pug/-/volar-service-pug-0.0.59.tgz",
- "integrity": "sha512-kmch7yoqeGNlJuDzpw/YL2b89ilzBmWDd0lJbpG412/RXc3PJVA4usUK+SQHdVoF+qi5IcZL6IDxlvRiIrDgWg==",
- "dependencies": {
- "@volar/language-service": "~2.4.0-alpha.12",
- "muggle-string": "^0.4.1",
- "pug-lexer": "^5.0.1",
- "pug-parser": "^6.0.0",
- "volar-service-html": "0.0.59",
- "vscode-html-languageservice": "^5.3.0",
- "vscode-languageserver-textdocument": "^1.0.11"
- }
- },
- "node_modules/volar-service-pug-beautify": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-pug-beautify/-/volar-service-pug-beautify-0.0.59.tgz",
- "integrity": "sha512-SCLWHpBdgvWww3a9Vp8FX80ookozhnHx10gkKBTYW4wp7/rzEoVAPSyO7JKBwTdXmdKQv7YXfxLMVUGv0sYUKg==",
- "dependencies": {
- "@johnsoncodehk/pug-beautify": "^0.2.2"
- },
- "peerDependencies": {
- "@volar/language-service": "~2.4.0-alpha.12"
- },
- "peerDependenciesMeta": {
- "@volar/language-service": {
- "optional": true
- }
- }
- },
- "node_modules/volar-service-typescript": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-typescript/-/volar-service-typescript-0.0.59.tgz",
- "integrity": "sha512-VCOpfiu+lUo5lapWLB5L5vmQGtwzmNWn5MueV915eku7blpphmE+Z7hCNcL1NApn7AetXWhiblv8ZhmUx/dGIA==",
- "dependencies": {
- "path-browserify": "^1.0.1",
- "semver": "^7.6.2",
- "typescript-auto-import-cache": "^0.3.3",
- "vscode-languageserver-textdocument": "^1.0.11",
- "vscode-nls": "^5.2.0",
- "vscode-uri": "^3.0.8"
- },
- "peerDependencies": {
- "@volar/language-service": "~2.4.0-alpha.12"
- },
- "peerDependenciesMeta": {
- "@volar/language-service": {
- "optional": true
- }
- }
- },
- "node_modules/volar-service-typescript-twoslash-queries": {
- "version": "0.0.59",
- "resolved": "https://registry.npmmirror.com/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.59.tgz",
- "integrity": "sha512-skm8e6yhCIkqLwJB6S9MqT5lO9LNFuMD3dYxKpmOZs1CKbXmCZZTmLfEaD5VkJae1xdleEDZFFTHl2O5HLjOGQ==",
- "dependencies": {
- "vscode-uri": "^3.0.8"
- },
- "peerDependencies": {
- "@volar/language-service": "~2.4.0-alpha.12"
- },
- "peerDependenciesMeta": {
- "@volar/language-service": {
- "optional": true
- }
- }
- },
- "node_modules/vscode-css-languageservice": {
- "version": "6.3.1",
- "resolved": "https://registry.npmmirror.com/vscode-css-languageservice/-/vscode-css-languageservice-6.3.1.tgz",
- "integrity": "sha512-1BzTBuJfwMc3A0uX4JBdJgoxp74cjj4q2mDJdp49yD/GuAq4X0k5WtK6fNcMYr+FfJ9nqgR6lpfCSZDkARJ5qQ==",
- "dependencies": {
- "@vscode/l10n": "^0.0.18",
- "vscode-languageserver-textdocument": "^1.0.12",
- "vscode-languageserver-types": "3.17.5",
- "vscode-uri": "^3.0.8"
- }
- },
- "node_modules/vscode-html-languageservice": {
- "version": "5.3.1",
- "resolved": "https://registry.npmmirror.com/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz",
- "integrity": "sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA==",
- "dependencies": {
- "@vscode/l10n": "^0.0.18",
- "vscode-languageserver-textdocument": "^1.0.12",
- "vscode-languageserver-types": "^3.17.5",
- "vscode-uri": "^3.0.8"
- }
- },
- "node_modules/vscode-json-languageservice": {
- "version": "5.4.1",
- "resolved": "https://registry.npmmirror.com/vscode-json-languageservice/-/vscode-json-languageservice-5.4.1.tgz",
- "integrity": "sha512-5czFGNyVPxz3ZJYl8R3a3SuIj5gjhmGF4Wv05MRPvD4DEnHK6b8km4VbNMJNHBlTCh7A0aHzUbPVzo+0C18mCA==",
- "dependencies": {
- "@vscode/l10n": "^0.0.18",
- "jsonc-parser": "^3.3.1",
- "vscode-languageserver-textdocument": "^1.0.12",
- "vscode-languageserver-types": "^3.17.5",
- "vscode-uri": "^3.0.8"
- }
- },
- "node_modules/vscode-json-languageservice/node_modules/jsonc-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
- "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="
- },
- "node_modules/vscode-jsonrpc": {
- "version": "8.2.0",
- "resolved": "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
- "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
"engines": {
- "node": ">=14.0.0"
+ "node": ">=8.0"
}
},
- "node_modules/vscode-languageserver": {
- "version": "9.0.1",
- "resolved": "https://registry.npmmirror.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
- "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
+ "node_modules/ufo": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.5.4.tgz",
+ "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
+ "license": "MIT"
+ },
+ "node_modules/unctx": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmmirror.com/unctx/-/unctx-2.4.1.tgz",
+ "integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
+ "license": "MIT",
"dependencies": {
- "vscode-languageserver-protocol": "3.17.5"
+ "acorn": "^8.14.0",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.17",
+ "unplugin": "^2.1.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "license": "MIT"
+ },
+ "node_modules/unicorn-magic": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+ "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unimport": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmmirror.com/unimport/-/unimport-4.1.3.tgz",
+ "integrity": "sha512-H+IVJ7rAkE3b+oC8rSJ2FsPaVsweeMC8eKZc+C6Mz7+hxDF45AnrY/tVCNRBvzMwWNcJEV67WdAVcal27iMjOw==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.14.1",
+ "escape-string-regexp": "^5.0.0",
+ "estree-walker": "^3.0.3",
+ "local-pkg": "^1.1.1",
+ "magic-string": "^0.30.17",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.2",
+ "pkg-types": "^2.1.0",
+ "scule": "^1.3.0",
+ "strip-literal": "^3.0.0",
+ "tinyglobby": "^0.2.12",
+ "unplugin": "^2.2.2",
+ "unplugin-utils": "^0.2.4"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
+ "node_modules/unimport/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/unplugin": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.2.2.tgz",
+ "integrity": "sha512-Qp+iiD+qCRnUek+nDoYvtWX7tfnYyXsrOnJ452FRTgOyKmTM7TUJ3l+PLPJOOWPTUyKISKp4isC5JJPSXUjGgw==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.14.1",
+ "webpack-virtual-modules": "^0.6.2"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
+ "node_modules/unplugin-utils": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.2.4.tgz",
+ "integrity": "sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==",
+ "license": "MIT",
+ "dependencies": {
+ "pathe": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/unplugin-utils/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/untyped": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/untyped/-/untyped-2.0.0.tgz",
+ "integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==",
+ "license": "MIT",
+ "dependencies": {
+ "citty": "^0.1.6",
+ "defu": "^6.1.4",
+ "jiti": "^2.4.2",
+ "knitwork": "^1.2.0",
+ "scule": "^1.3.0"
},
"bin": {
- "installServerIntoExtension": "bin/installServerIntoExtension"
+ "untyped": "dist/cli.mjs"
}
},
- "node_modules/vscode-languageserver-protocol": {
- "version": "3.17.5",
- "resolved": "https://registry.npmmirror.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
- "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
- "dependencies": {
- "vscode-jsonrpc": "8.2.0",
- "vscode-languageserver-types": "3.17.5"
+ "node_modules/web-streams-polyfill": {
+ "version": "4.0.0-beta.3",
+ "resolved": "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
+ "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
}
},
- "node_modules/vscode-languageserver-textdocument": {
- "version": "1.0.12",
- "resolved": "https://registry.npmmirror.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
- "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
},
- "node_modules/vscode-languageserver-types": {
- "version": "3.17.5",
- "resolved": "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
- "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
+ "node_modules/webpack-virtual-modules": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
+ "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
+ "license": "MIT"
},
- "node_modules/vscode-nls": {
- "version": "5.2.0",
- "resolved": "https://registry.npmmirror.com/vscode-nls/-/vscode-nls-5.2.0.tgz",
- "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="
- },
- "node_modules/vscode-uri": {
- "version": "3.0.8",
- "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.0.8.tgz",
- "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="
- },
- "node_modules/vue": {
- "version": "3.5.12",
- "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.12.tgz",
- "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==",
- "peer": true,
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.12",
- "@vue/compiler-sfc": "3.5.12",
- "@vue/runtime-dom": "3.5.12",
- "@vue/server-renderer": "3.5.12",
- "@vue/shared": "3.5.12"
- },
- "peerDependencies": {
- "typescript": "*"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/vue-template-compiler": {
- "version": "2.7.16",
- "resolved": "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
- "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==",
- "dependencies": {
- "de-indent": "^1.0.2",
- "he": "^1.2.0"
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
}
}
}
diff --git a/package.json b/package.json
index 06c4446..f83ed9d 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,7 @@
{
"dependencies": {
- "@volar/language-server": "2.4.0-alpha.18",
- "@vue/language-server": "2.0.28",
- "ag-charts-vue3": "^10.3.1",
- "dayjs": "^1.11.12"
+ "marked": "^15.0.7",
+ "openai": "^4.89.0",
+ "pinia-plugin-persistedstate": "^4.2.0"
}
}
diff --git a/src/Utilities.py b/src/Utilities.py
new file mode 100644
index 0000000..106579a
--- /dev/null
+++ b/src/Utilities.py
@@ -0,0 +1,76 @@
+import re, ipaddress
+import subprocess
+
+
+def RegexMatch(regex, text) -> bool:
+ """
+ Regex Match
+ @param regex: Regex patter
+ @param text: Text to match
+ @return: Boolean indicate if the text match the regex pattern
+ """
+ pattern = re.compile(regex)
+ return pattern.search(text) is not None
+
+def GetRemoteEndpoint() -> str:
+ """
+ Using socket to determine default interface IP address. Thanks, @NOXICS
+ @return:
+ """
+ import socket
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
+ s.connect(("1.1.1.1", 80)) # Connecting to a public IP
+ wgd_remote_endpoint = s.getsockname()[0]
+ return str(wgd_remote_endpoint)
+
+
+def StringToBoolean(value: str):
+ """
+ Convert string boolean to boolean
+ @param value: Boolean value in string came from Configuration file
+ @return: Boolean value
+ """
+ return (value.strip().replace(" ", "").lower() in
+ ("yes", "true", "t", "1", 1))
+
+def ValidateIPAddressesWithRange(ips: str) -> bool:
+ s = ips.replace(" ", "").split(",")
+ for ip in s:
+ try:
+ ipaddress.ip_network(ip)
+ except ValueError as e:
+ return False
+ return True
+
+def ValidateIPAddresses(ips) -> bool:
+ s = ips.replace(" ", "").split(",")
+ for ip in s:
+ try:
+ ipaddress.ip_address(ip)
+ except ValueError as e:
+ return False
+ return True
+
+def ValidateDNSAddress(addresses) -> tuple[bool, str]:
+ s = addresses.replace(" ", "").split(",")
+ for address in s:
+ if not ValidateIPAddresses(address) and not RegexMatch(
+ r"(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]", address):
+ return False, f"{address} does not appear to be an valid DNS address"
+ return True, ""
+
+def GenerateWireguardPublicKey(privateKey: str) -> tuple[bool, str] | tuple[bool, None]:
+ try:
+ publicKey = subprocess.check_output(f"wg pubkey", input=privateKey.encode(), shell=True,
+ stderr=subprocess.STDOUT)
+ return True, publicKey.decode().strip('\n')
+ except subprocess.CalledProcessError:
+ return False, None
+
+def GenerateWireguardPrivateKey() -> tuple[bool, str] | tuple[bool, None]:
+ try:
+ publicKey = subprocess.check_output(f"wg genkey", shell=True,
+ stderr=subprocess.STDOUT)
+ return True, publicKey.decode().strip('\n')
+ except subprocess.CalledProcessError:
+ return False, None
\ No newline at end of file
diff --git a/src/dashboard.py b/src/dashboard.py
index 4ae3071..240d14d 100644
--- a/src/dashboard.py
+++ b/src/dashboard.py
@@ -1,212 +1,69 @@
-import itertools, random
-import shutil
-import sqlite3
-import configparser
-import hashlib
-import ipaddress
-import json
-import traceback
-import os
-import secrets
-import subprocess
-import time
-import re
-import urllib.error
-import uuid
+import random, shutil, sqlite3, configparser, hashlib, ipaddress, json, os, secrets, subprocess
+import time, re, urllib.error, uuid, bcrypt, psutil, pyotp, threading
+from uuid import uuid4
+from zipfile import ZipFile
from datetime import datetime, timedelta
from typing import Any
-import bcrypt
-import ifcfg
-import psutil
-import pyotp
-from flask import Flask, request, render_template, session, g
+from jinja2 import Template
+from flask import Flask, request, render_template, session, send_file
from json import JSONEncoder
from flask_cors import CORS
from icmplib import ping, traceroute
-import threading
-
from flask.json.provider import DefaultJSONProvider
+from itertools import islice
+from Utilities import (
+ RegexMatch, GetRemoteEndpoint, StringToBoolean,
+ ValidateIPAddressesWithRange, ValidateDNSAddress,
+ GenerateWireguardPublicKey, GenerateWireguardPrivateKey
+)
+from packaging import version
+from modules.Email import EmailSender
+from modules.Log import Log
+from modules.DashboardLogger import DashboardLogger
+from modules.PeerJobLogger import PeerJobLogger
+from modules.PeerJob import PeerJob
+from modules.SystemStatus import SystemStatus
+SystemStatus = SystemStatus()
+
+DASHBOARD_VERSION = 'v4.2.0'
-DASHBOARD_VERSION = 'v4.1.4'
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
DB_PATH = os.path.join(CONFIGURATION_PATH, 'db')
if not os.path.isdir(DB_PATH):
os.mkdir(DB_PATH)
DASHBOARD_CONF = os.path.join(CONFIGURATION_PATH, 'wg-dashboard.ini')
-WG_CONF_PATH = None
UPDATE = None
app = Flask("WGDashboard", template_folder=os.path.abspath("./static/app/dist"))
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
app.secret_key = secrets.token_urlsafe(32)
-class ModelEncoder(JSONEncoder):
- def default(self, o: Any) -> Any:
- if hasattr(o, 'toJson'):
- return o.toJson()
- else:
- return super(ModelEncoder, self).default(o)
-
-'''
-Classes
-'''
-
-def ResponseObject(status=True, message=None, data=None) -> Flask.response_class:
- response = Flask.make_response(app, {
- "status": status,
- "message": message,
- "data": data
- })
- response.content_type = "application/json"
- return response
-
-
class CustomJsonEncoder(DefaultJSONProvider):
def __init__(self, app):
super().__init__(app)
def default(self, o):
- if (isinstance(o, WireguardConfiguration)
- or isinstance(o, Peer)
- or isinstance(o, PeerJob)
- or isinstance(o, Log)
- or isinstance(o, DashboardAPIKey)
- or isinstance(o, PeerShareLink)):
+ if callable(getattr(o, "toJson", None)):
return o.toJson()
- return super().default(self, o)
-
-
+ return super().default(self)
app.json = CustomJsonEncoder(app)
-class Log:
- def __init__(self, LogID: str, JobID: str, LogDate: str, Status: str, Message: str):
- self.LogID = LogID
- self.JobID = JobID
- self.LogDate = LogDate
- self.Status = Status
- self.Message = Message
-
- def toJson(self):
- return {
- "LogID": self.LogID,
- "JobID": self.JobID,
- "LogDate": self.LogDate,
- "Status": self.Status,
- "Message": self.Message
- }
-
- def __dict__(self):
- return self.toJson()
-
-class DashboardLogger:
- def __init__(self):
- self.loggerdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_log.db'),
- check_same_thread=False)
- self.loggerdb.row_factory = sqlite3.Row
- self.__createLogDatabase()
- self.log(Message="WGDashboard started")
- def __createLogDatabase(self):
- with self.loggerdb:
- loggerdbCursor = self.loggerdb.cursor()
- existingTable = loggerdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
- existingTable = [t['name'] for t in existingTable]
- if "DashboardLog" not in existingTable:
- loggerdbCursor.execute(
- "CREATE TABLE DashboardLog (LogID VARCHAR NOT NULL, LogDate DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S','now', 'localtime')), URL VARCHAR, IP VARCHAR, Status VARCHAR, Message VARCHAR, PRIMARY KEY (LogID))")
- if self.loggerdb.in_transaction:
- self.loggerdb.commit()
-
- def log(self, URL: str = "", IP: str = "", Status: str = "true", Message: str = "") -> bool:
- try:
- with self.loggerdb:
- loggerdbCursor = self.loggerdb.cursor()
- loggerdbCursor.execute(
- "INSERT INTO DashboardLog (LogID, URL, IP, Status, Message) VALUES (?, ?, ?, ?, ?)", (str(uuid.uuid4()), URL, IP, Status, Message,))
- if self.loggerdb.in_transaction:
- self.loggerdb.commit()
- return True
- except Exception as e:
- print(f"[WGDashboard] Access Log Error: {str(e)}")
- return False
-
-class PeerJobLogger:
- def __init__(self):
- self.loggerdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_log.db'),
- check_same_thread=False)
- self.loggerdb.row_factory = sqlite3.Row
- self.logs:list(Log) = []
- self.__createLogDatabase()
-
- def __createLogDatabase(self):
- with self.loggerdb:
- loggerdbCursor = self.loggerdb.cursor()
-
- existingTable = loggerdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
- existingTable = [t['name'] for t in existingTable]
-
- if "JobLog" not in existingTable:
- loggerdbCursor.execute("CREATE TABLE JobLog (LogID VARCHAR NOT NULL, JobID NOT NULL, LogDate DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S','now', 'localtime')), Status VARCHAR NOT NULL, Message VARCHAR, PRIMARY KEY (LogID))")
- if self.loggerdb.in_transaction:
- self.loggerdb.commit()
- def log(self, JobID: str, Status: bool = True, Message: str = "") -> bool:
- try:
- with self.loggerdb:
- loggerdbCursor = self.loggerdb.cursor()
- loggerdbCursor.execute(f"INSERT INTO JobLog (LogID, JobID, Status, Message) VALUES (?, ?, ?, ?)",
- (str(uuid.uuid4()), JobID, Status, Message,))
- if self.loggerdb.in_transaction:
- self.loggerdb.commit()
- except Exception as e:
- print(f"[WGDashboard] Peer Job Log Error: {str(e)}")
- return False
- return True
-
- def getLogs(self, all: bool = False, configName = None) -> list[Log]:
- logs: list[Log] = []
- try:
- allJobs = AllPeerJobs.getAllJobs(configName)
- allJobsID = ", ".join([f"'{x.JobID}'" for x in allJobs])
- with self.loggerdb:
- loggerdbCursor = self.loggerdb.cursor()
- table = loggerdbCursor.execute(f"SELECT * FROM JobLog WHERE JobID IN ({allJobsID}) ORDER BY LogDate DESC").fetchall()
- self.logs.clear()
- for l in table:
- logs.append(
- Log(l["LogID"], l["JobID"], l["LogDate"], l["Status"], l["Message"]))
- except Exception as e:
- return logs
- return logs
-
-class PeerJob:
- def __init__(self, JobID: str, Configuration: str, Peer: str,
- Field: str, Operator: str, Value: str, CreationDate: datetime, ExpireDate: datetime, Action: str):
- self.Action = Action
- self.ExpireDate = ExpireDate
- self.CreationDate = CreationDate
- self.Value = Value
- self.Operator = Operator
- self.Field = Field
- self.Configuration = Configuration
- self.Peer = Peer
- self.JobID = JobID
-
- def toJson(self):
- return {
- "JobID": self.JobID,
- "Configuration": self.Configuration,
- "Peer": self.Peer,
- "Field": self.Field,
- "Operator": self.Operator,
- "Value": self.Value,
- "CreationDate": self.CreationDate,
- "ExpireDate": self.ExpireDate,
- "Action": self.Action
- }
-
- def __dict__(self):
- return self.toJson()
+'''
+Response Object
+'''
+def ResponseObject(status=True, message=None, data=None, status_code = 200) -> Flask.response_class:
+ response = Flask.make_response(app, {
+ "status": status,
+ "message": message,
+ "data": data
+ })
+ response.status_code = status_code
+ response.content_type = "application/json"
+ return response
+"""
+Peer Jobs
+"""
class PeerJobs:
-
def __init__(self):
self.Jobs: list[PeerJob] = []
self.jobdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_job.db'),
@@ -259,18 +116,19 @@ class PeerJobs:
def searchJob(self, Configuration: str, Peer: str):
return list(filter(lambda x: x.Configuration == Configuration and x.Peer == Peer, self.Jobs))
-
+
+ def searchJobById(self, JobID):
+ return list(filter(lambda x: x.JobID == JobID, self.Jobs))
+
def saveJob(self, Job: PeerJob) -> tuple[bool, list] | tuple[bool, str]:
try:
with self.jobdb:
jobdbCursor = self.jobdb.cursor()
-
- if (len(str(Job.CreationDate))) == 0:
+ if len(self.searchJobById(Job.JobID)) == 0:
jobdbCursor.execute('''
INSERT INTO PeerJobs VALUES (?, ?, ?, ?, ?, ?, strftime('%Y-%m-%d %H:%M:%S','now'), NULL, ?)
''', (Job.JobID, Job.Configuration, Job.Peer, Job.Field, Job.Operator, Job.Value, Job.Action,))
JobLogger.log(Job.JobID, Message=f"Job is created if {Job.Field} {Job.Operator} {Job.Value} then {Job.Action}")
-
else:
currentJob = jobdbCursor.execute('SELECT * FROM PeerJobs WHERE JobID = ?', (Job.JobID, )).fetchone()
if currentJob is not None:
@@ -279,16 +137,16 @@ class PeerJobs:
''', (Job.Field, Job.Operator, Job.Value, Job.Action, Job.JobID))
JobLogger.log(Job.JobID,
Message=f"Job is updated from if {currentJob['Field']} {currentJob['Operator']} {currentJob['value']} then {currentJob['Action']}; to if {Job.Field} {Job.Operator} {Job.Value} then {Job.Action}")
+
self.jobdb.commit()
self.__getJobs()
-
return True, list(
filter(lambda x: x.Configuration == Job.Configuration and x.Peer == Job.Peer and x.JobID == Job.JobID,
self.Jobs))
except Exception as e:
return False, str(e)
- def deleteJob(self, Job: PeerJob, deletedFrom = 'Job Runner') -> tuple[bool, list] | tuple[bool, str]:
+ def deleteJob(self, Job: PeerJob) -> tuple[bool, list] | tuple[bool, str]:
try:
if (len(str(Job.CreationDate))) == 0:
return False, "Job does not exist"
@@ -298,7 +156,7 @@ class PeerJobs:
UPDATE PeerJobs SET ExpireDate = strftime('%Y-%m-%d %H:%M:%S','now') WHERE JobID = ?
''', (Job.JobID,))
self.jobdb.commit()
- JobLogger.log(Job.JobID, Message=f"Job is removed by {deletedFrom} due to being deleted or finshed.")
+ JobLogger.log(Job.JobID, Message=f"Job is removed due to being deleted or finshed.")
self.__getJobs()
return True, list(
filter(lambda x: x.Configuration == Job.Configuration and x.Peer == Job.Peer and x.JobID == Job.JobID,
@@ -306,7 +164,7 @@ class PeerJobs:
except Exception as e:
return False, str(e)
- def updateJobConfigurationName(self, ConfigurationName: str, NewConfigurationName: str) -> tuple[bool, str]:
+ def updateJobConfigurationName(self, ConfigurationName: str, NewConfigurationName: str) -> tuple[bool, str] | tuple[bool, None]:
try:
with self.jobdb:
jobdbCursor = self.jobdb.cursor()
@@ -315,6 +173,7 @@ class PeerJobs:
''', (NewConfigurationName, ConfigurationName, ))
self.jobdb.commit()
self.__getJobs()
+ return True, None
except Exception as e:
return False, str(e)
@@ -351,7 +210,7 @@ class PeerJobs:
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
)
else:
- JobLogger.log(job.JobID,False,
+ JobLogger.log(job.JobID, False,
f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed."
)
else:
@@ -370,7 +229,10 @@ class PeerJobs:
return x > y
if operator == "lst":
return x < y
-
+
+"""
+Peer Share Link
+"""
class PeerShareLink:
def __init__(self, ShareID:str, Configuration: str, Peer: str, ExpireDate: datetime, ShareDate: datetime):
self.ShareID = ShareID
@@ -388,6 +250,9 @@ class PeerShareLink:
"ExpireDate": self.ExpireDate
}
+"""
+Peer Share Links
+"""
class PeerShareLinks:
def __init__(self):
self.Links: list[PeerShareLink] = []
@@ -410,7 +275,6 @@ class PeerShareLinks:
self.Links.append(PeerShareLink(*link))
def getLink(self, Configuration: str, Peer: str) -> list[PeerShareLink]:
- self.__getSharedLinks()
return list(filter(lambda x : x.Configuration == Configuration and x.Peer == Peer, self.Links))
def getLinkByID(self, ShareID: str) -> list[PeerShareLink]:
@@ -432,7 +296,10 @@ class PeerShareLinks:
sqlUpdate("UPDATE PeerShareLinks SET ExpireDate = ? WHERE ShareID = ?;", (ExpireDate, ShareID, ))
self.__getSharedLinks()
return True, ""
-
+
+"""
+WireGuard Configuration
+"""
class WireguardConfiguration:
class InvalidConfigurationFileException(Exception):
def __init__(self, m):
@@ -441,10 +308,10 @@ class WireguardConfiguration:
def __str__(self):
return self.message
- def __init__(self, name: str = None, data: dict = None, backup: dict = None, startup: bool = False):
+ def __init__(self, name: str = None, data: dict = None, backup: dict = None, startup: bool = False, wg: bool = True):
- self.__parser: configparser.ConfigParser = configparser.ConfigParser(strict=False)
+ self.__parser: configparser.ConfigParser = configparser.RawConfigParser(strict=False)
self.__parser.optionxform = str
self.__configFileModifiedTime = None
@@ -452,6 +319,7 @@ class WireguardConfiguration:
self.Name: str = ""
self.PrivateKey: str = ""
self.PublicKey: str = ""
+
self.ListenPort: str = ""
self.Address: str = ""
self.DNS: str = ""
@@ -463,35 +331,30 @@ class WireguardConfiguration:
self.PostDown: str = ""
self.SaveConfig: bool = True
self.Name = name
- self.__configPath = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf')
-
-
- backupPath = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup')
- if not os.path.exists(backupPath):
- os.mkdir(backupPath)
+ self.Protocol = "wg" if wg else "awg"
+ self.configPath = os.path.join(self.__getProtocolPath(), f'{self.Name}.conf') if wg else os.path.join(DashboardConfig.GetConfig("Server", "awg_conf_path")[1], f'{self.Name}.conf')
if name is not None:
if data is not None and "Backup" in data.keys():
db = self.__importDatabase(
os.path.join(
- DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
+ self.__getProtocolPath(),
'WGDashboard_Backup',
data["Backup"].replace(".conf", ".sql")))
else:
- self.__createDatabase()
+ self.createDatabase()
self.__parseConfigurationFile()
self.__initPeersList()
-
else:
self.Name = data["ConfigurationName"]
- self.__configPath = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf')
+ self.configPath = os.path.join(self.__getProtocolPath(), f'{self.Name}.conf')
for i in dir(self):
if str(i) in data.keys():
if isinstance(getattr(self, i), bool):
- setattr(self, i, _strToBool(data[i]))
+ setattr(self, i, StringToBoolean(data[i]))
else:
setattr(self, i, str(data[i]))
@@ -499,46 +362,112 @@ class WireguardConfiguration:
"PrivateKey": self.PrivateKey,
"Address": self.Address,
"ListenPort": self.ListenPort,
- "PreUp": self.PreUp,
- "PreDown": self.PreDown,
- "PostUp": self.PostUp,
- "PostDown": self.PostDown,
+ "PreUp": f"{self.PreUp}",
+ "PreDown": f"{self.PreDown}",
+ "PostUp": f"{self.PostUp}",
+ "PostDown": f"{self.PostDown}",
"SaveConfig": "true"
}
+ if self.Protocol == 'awg':
+ self.__parser["Interface"]["Jc"] = self.Jc
+ self.__parser["Interface"]["Jc"] = self.Jc
+ self.__parser["Interface"]["Jmin"] = self.Jmin
+ self.__parser["Interface"]["Jmax"] = self.Jmax
+ self.__parser["Interface"]["S1"] = self.S1
+ self.__parser["Interface"]["S2"] = self.S2
+ self.__parser["Interface"]["H1"] = self.H1
+ self.__parser["Interface"]["H2"] = self.H2
+ self.__parser["Interface"]["H3"] = self.H3
+ self.__parser["Interface"]["H4"] = self.H4
+
if "Backup" not in data.keys():
- self.__createDatabase()
- with open(self.__configPath, "w+") as configFile:
+ self.createDatabase()
+ with open(self.configPath, "w+") as configFile:
self.__parser.write(configFile)
+ print(f"[WGDashboard] Configuration file {self.configPath} created")
self.__initPeersList()
+
+ if not os.path.exists(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup')):
+ os.mkdir(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup'))
print(f"[WGDashboard] Initialized Configuration: {name}")
if self.getAutostartStatus() and not self.getStatus() and startup:
self.toggleConfiguration()
print(f"[WGDashboard] Autostart Configuration: {name}")
-
-
+
+ def __getProtocolPath(self):
+ return DashboardConfig.GetConfig("Server", "wg_conf_path")[1] if self.Protocol == "wg" \
+ else DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
+
def __initPeersList(self):
self.Peers: list[Peer] = []
self.getPeersList()
self.getRestrictedPeersList()
+
+ def getRawConfigurationFile(self):
+ return open(self.configPath, 'r').read()
+ def updateRawConfigurationFile(self, newRawConfiguration):
+ backupStatus, backup = self.backupConfigurationFile()
+ if not backupStatus:
+ return False, "Cannot create backup"
+
+ if self.Status:
+ self.toggleConfiguration()
+
+ with open(self.configPath, 'w') as f:
+ f.write(newRawConfiguration)
+
+ status, err = self.toggleConfiguration()
+ if not status:
+ restoreStatus = self.restoreBackup(backup['filename'])
+ print(f"Restore status: {restoreStatus}")
+ self.toggleConfiguration()
+ return False, err
+ return True, None
+
def __parseConfigurationFile(self):
- self.__parser.read_file(open(self.__configPath))
- sections = self.__parser.sections()
- if "Interface" not in sections:
- raise self.InvalidConfigurationFileException(
- "[Interface] section not found in " + os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'))
- interfaceConfig = dict(self.__parser.items("Interface", True))
- for i in dir(self):
- if str(i) in interfaceConfig.keys():
- if isinstance(getattr(self, i), bool):
- setattr(self, i, _strToBool(interfaceConfig[i]))
- else:
- setattr(self, i, interfaceConfig[i])
- if self.PrivateKey:
- self.PublicKey = self.__getPublicKey()
- self.Status = self.getStatus()
+ with open(self.configPath, 'r') as f:
+ original = [l.rstrip("\n") for l in f.readlines()]
+ try:
+ start = original.index("[Interface]")
+
+ # Clean
+ for i in range(start, len(original)):
+ if original[i] == "[Peer]":
+ break
+ split = re.split(r'\s*=\s*', original[i], 1)
+ if len(split) == 2:
+ key = split[0]
+ if key in dir(self):
+ if isinstance(getattr(self, key), bool):
+ setattr(self, key, False)
+ else:
+ setattr(self, key, "")
+
+ # Set
+ for i in range(start, len(original)):
+ if original[i] == "[Peer]":
+ break
+ split = re.split(r'\s*=\s*', original[i], 1)
+ if len(split) == 2:
+ key = split[0]
+ value = split[1]
+ if key in dir(self):
+ if isinstance(getattr(self, key), bool):
+ setattr(self, key, StringToBoolean(value))
+ else:
+ if len(getattr(self, key)) > 0:
+ setattr(self, key, f"{getattr(self, key)}, {value}")
+ else:
+ setattr(self, key, value)
+ except ValueError as e:
+ raise self.InvalidConfigurationFileException(
+ "[Interface] section not found in " + self.configPath)
+ if self.PrivateKey:
+ self.PublicKey = self.__getPublicKey()
+ self.Status = self.getStatus()
def __dropDatabase(self):
existingTables = sqlSelect(f"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '{self.Name}%'").fetchall()
@@ -547,7 +476,7 @@ class WireguardConfiguration:
existingTables = sqlSelect(f"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '{self.Name}%'").fetchall()
- def __createDatabase(self, dbName = None):
+ def createDatabase(self, dbName = None):
if dbName is None:
dbName = self.Name
@@ -567,7 +496,6 @@ class WireguardConfiguration:
)
""" % dbName
)
-
if f'{dbName}_restrict_access' not in existingTables:
sqlUpdate(
"""
@@ -618,7 +546,7 @@ class WireguardConfiguration:
def __importDatabase(self, sqlFilePath) -> bool:
self.__dropDatabase()
- self.__createDatabase()
+ self.createDatabase()
if not os.path.exists(sqlFilePath):
return False
with open(sqlFilePath, 'r') as f:
@@ -629,7 +557,7 @@ class WireguardConfiguration:
return True
def __getPublicKey(self) -> str:
- return _generatePublicKey(self.PrivateKey)[1]
+ return GenerateWireguardPublicKey(self.PrivateKey)[1]
def getStatus(self) -> bool:
self.Status = self.Name in psutil.net_if_addrs().keys()
@@ -639,22 +567,22 @@ class WireguardConfiguration:
s, d = DashboardConfig.GetConfig("WireGuardConfiguration", "autostart")
return self.Name in d
- def __getRestrictedPeers(self):
+ def getRestrictedPeers(self):
self.RestrictedPeers = []
restricted = sqlSelect("SELECT * FROM '%s_restrict_access'" % self.Name).fetchall()
for i in restricted:
self.RestrictedPeers.append(Peer(i, self))
def configurationFileChanged(self) :
- mt = os.path.getmtime(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'))
+ mt = os.path.getmtime(self.configPath)
changed = self.__configFileModifiedTime is None or self.__configFileModifiedTime != mt
self.__configFileModifiedTime = mt
return changed
- def __getPeers(self):
+ def getPeers(self):
if self.configurationFileChanged():
self.Peers = []
- with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'), 'r') as configFile:
+ with open(self.configPath, 'r') as configFile:
p = []
pCounter = -1
content = configFile.read().split('\n')
@@ -662,7 +590,7 @@ class WireguardConfiguration:
peerStarts = content.index("[Peer]")
content = content[peerStarts:]
for i in content:
- if not regex_match("#(.*)", i) and not regex_match(";(.*)", i):
+ if not RegexMatch("#(.*)", i) and not RegexMatch(";(.*)", i):
if i == "[Peer]":
pCounter += 1
p.append({})
@@ -673,7 +601,7 @@ class WireguardConfiguration:
if len(split) == 2:
p[pCounter][split[0]] = split[1]
- if regex_match("#Name# = (.*)", i):
+ if RegexMatch("#Name# = (.*)", i):
split = re.split(r'\s*=\s*', i, 1)
if len(split) == 2:
p[pCounter]["name"] = split[1]
@@ -728,7 +656,11 @@ class WireguardConfiguration:
for i in checkIfExist:
self.Peers.append(Peer(i, self))
- def addPeers(self, peers: list):
+ def addPeers(self, peers: list) -> tuple[bool, dict]:
+ result = {
+ "message": None,
+ "peers": []
+ }
try:
for i in peers:
newPeer = {
@@ -769,17 +701,21 @@ class WireguardConfiguration:
with open(uid, "w+") as f:
f.write(p['preshared_key'])
- subprocess.check_output(f"wg set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
+ subprocess.check_output(f"{self.Protocol} set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
shell=True, stderr=subprocess.STDOUT)
if presharedKeyExist:
os.remove(uid)
subprocess.check_output(
- f"wg-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
+ f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
self.getPeersList()
- return True
+ for p in peers:
+ p = self.searchPeer(p['id'])
+ if p[0]:
+ result['peers'].append(p[1])
+ return True, result
except Exception as e:
- print(str(e))
- return False
+ result['message'] = str(e)
+ return False, result
def searchPeer(self, publicKey):
for i in self.Peers:
@@ -806,7 +742,7 @@ class WireguardConfiguration:
with open(uid, "w+") as f:
f.write(p['preshared_key'])
- subprocess.check_output(f"wg set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
+ subprocess.check_output(f"{self.Protocol} set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
shell=True, stderr=subprocess.STDOUT)
if presharedKeyExist: os.remove(uid)
else:
@@ -814,7 +750,7 @@ class WireguardConfiguration:
if not self.__wgSave():
return ResponseObject(False, "Failed to save configuration through WireGuard")
- self.__getPeers()
+ self.getPeers()
return ResponseObject(True, "Allow access successfully")
def restrictPeers(self, listOfPublicKeys):
@@ -826,7 +762,7 @@ class WireguardConfiguration:
found, pf = self.searchPeer(p)
if found:
try:
- subprocess.check_output(f"wg set {self.Name} peer {pf.id} remove",
+ subprocess.check_output(f"{self.Protocol} set {self.Name} peer {pf.id} remove",
shell=True, stderr=subprocess.STDOUT)
sqlUpdate("INSERT INTO '%s_restrict_access' SELECT * FROM '%s' WHERE id = ?" %
(self.Name, self.Name,), (pf.id,))
@@ -840,7 +776,7 @@ class WireguardConfiguration:
if not self.__wgSave():
return ResponseObject(False, "Failed to save configuration through WireGuard")
- self.__getPeers()
+ self.getPeers()
if numOfRestrictedPeers == len(listOfPublicKeys):
return ResponseObject(True, f"Restricted {numOfRestrictedPeers} peer(s)")
@@ -857,7 +793,7 @@ class WireguardConfiguration:
found, pf = self.searchPeer(p)
if found:
try:
- subprocess.check_output(f"wg set {self.Name} peer {pf.id} remove",
+ subprocess.check_output(f"{self.Protocol} set {self.Name} peer {pf.id} remove",
shell=True, stderr=subprocess.STDOUT)
sqlUpdate("DELETE FROM '%s' WHERE id = ?" % self.Name, (pf.id,))
numOfDeletedPeers += 1
@@ -867,31 +803,16 @@ class WireguardConfiguration:
if not self.__wgSave():
return ResponseObject(False, "Failed to save configuration through WireGuard")
- self.__getPeers()
+ self.getPeers()
if numOfDeletedPeers == len(listOfPublicKeys):
return ResponseObject(True, f"Deleted {numOfDeletedPeers} peer(s)")
return ResponseObject(False,
f"Deleted {numOfDeletedPeers} peer(s) successfully. Failed to delete {numOfFailedToDeletePeers} peer(s)")
- def __savePeers(self):
- for i in self.Peers:
- d = i.toJson()
- sqlUpdate(
- '''
- UPDATE '%s' SET private_key = :private_key,
- DNS = :DNS, endpoint_allowed_ip = :endpoint_allowed_ip, name = :name,
- total_receive = :total_receive, total_sent = :total_sent, total_data = :total_data,
- endpoint = :endpoint, status = :status, latest_handshake = :latest_handshake,
- allowed_ip = :allowed_ip, cumu_receive = :cumu_receive, cumu_sent = :cumu_sent,
- cumu_data = :cumu_data, mtu = :mtu, keepalive = :keepalive,
- remote_endpoint = :remote_endpoint, preshared_key = :preshared_key WHERE id = :id
- ''' % self.Name, d
- )
-
def __wgSave(self) -> tuple[bool, str] | tuple[bool, None]:
try:
- subprocess.check_output(f"wg-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
+ subprocess.check_output(f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
return True, None
except subprocess.CalledProcessError as e:
return False, str(e)
@@ -900,7 +821,7 @@ class WireguardConfiguration:
if not self.getStatus():
self.toggleConfiguration()
try:
- latestHandshake = subprocess.check_output(f"wg show {self.Name} latest-handshakes",
+ latestHandshake = subprocess.check_output(f"{self.Protocol} show {self.Name} latest-handshakes",
shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
return "stopped"
@@ -926,7 +847,7 @@ class WireguardConfiguration:
if not self.getStatus():
self.toggleConfiguration()
try:
- data_usage = subprocess.check_output(f"wg show {self.Name} transfer",
+ data_usage = subprocess.check_output(f"{self.Protocol} show {self.Name} transfer",
shell=True, stderr=subprocess.STDOUT)
data_usage = data_usage.decode("UTF-8").split("\n")
data_usage = [p.split("\t") for p in data_usage]
@@ -967,7 +888,7 @@ class WireguardConfiguration:
if not self.getStatus():
self.toggleConfiguration()
try:
- data_usage = subprocess.check_output(f"wg show {self.Name} endpoints",
+ data_usage = subprocess.check_output(f"{self.Protocol} show {self.Name} endpoints",
shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
return "stopped"
@@ -982,25 +903,25 @@ class WireguardConfiguration:
self.getStatus()
if self.Status:
try:
- check = subprocess.check_output(f"wg-quick down {self.Name}",
+ check = subprocess.check_output(f"{self.Protocol}-quick down {self.Name}",
shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
return False, str(exc.output.strip().decode("utf-8"))
else:
try:
- check = subprocess.check_output(f"wg-quick up {self.Name}",
- shell=True, stderr=subprocess.STDOUT)
+ check = subprocess.check_output(f"{self.Protocol}-quick up {self.Name}", shell=True, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
return False, str(exc.output.strip().decode("utf-8"))
+ self.__parseConfigurationFile()
self.getStatus()
return True, None
def getPeersList(self):
- self.__getPeers()
+ self.getPeers()
return self.Peers
def getRestrictedPeersList(self) -> list:
- self.__getRestrictedPeers()
+ self.getRestrictedPeers()
return self.RestrictedPeers
def toJson(self):
@@ -1023,42 +944,48 @@ class WireguardConfiguration:
"Receive": sum(list(map(lambda x: x.cumu_receive + x.total_receive, self.Peers)))
},
"ConnectedPeers": len(list(filter(lambda x: x.status == "running", self.Peers))),
- "TotalPeers": len(self.Peers)
+ "TotalPeers": len(self.Peers),
+ "Protocol": self.Protocol
}
- def backupConfigurationFile(self):
- if not os.path.exists(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup')):
- os.mkdir(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup'))
+ def backupConfigurationFile(self) -> tuple[bool, dict[str, str]]:
+ if not os.path.exists(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup')):
+ os.mkdir(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup'))
time = datetime.now().strftime("%Y%m%d%H%M%S")
shutil.copy(
- self.__configPath,
- os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f'{self.Name}_{time}.conf')
+ self.configPath,
+ os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', f'{self.Name}_{time}.conf')
)
- with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f'{self.Name}_{time}.sql'), 'w+') as f:
+ with open(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', f'{self.Name}_{time}.sql'), 'w+') as f:
for l in self.__dumpDatabase():
f.write(l + "\n")
+ return True, {
+ "filename": f'{self.Name}_{time}.conf',
+ "backupDate": datetime.now().strftime("%Y%m%d%H%M%S")
+ }
+
def getBackups(self, databaseContent: bool = False) -> list[dict[str: str, str: str, str: str]]:
backups = []
- directory = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup')
+ directory = os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup')
files = [(file, os.path.getctime(os.path.join(directory, file)))
for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))]
files.sort(key=lambda x: x[1], reverse=True)
for f, ct in files:
- if _regexMatch(f"^({self.Name})_(.*)\\.(conf)$", f):
+ if RegexMatch(f"^({self.Name})_(.*)\\.(conf)$", f):
s = re.search(f"^({self.Name})_(.*)\\.(conf)$", f)
date = s.group(2)
d = {
"filename": f,
"backupDate": date,
- "content": open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
+ "content": open(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', f), 'r').read()
}
if f.replace(".conf", ".sql") in list(os.listdir(directory)):
d['database'] = True
if databaseContent:
- d['databaseContent'] = open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read()
+ d['databaseContent'] = open(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read()
backups.append(d)
return backups
@@ -1067,16 +994,15 @@ class WireguardConfiguration:
backups = list(map(lambda x : x['filename'], self.getBackups()))
if backupFileName not in backups:
return False
- self.backupConfigurationFile()
if self.Status:
self.toggleConfiguration()
- target = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', backupFileName)
- targetSQL = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', backupFileName.replace(".conf", ".sql"))
+ target = os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backupFileName)
+ targetSQL = os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backupFileName.replace(".conf", ".sql"))
if not os.path.exists(target):
return False
targetContent = open(target, 'r').read()
try:
- with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'), 'w') as f:
+ with open(self.configPath, 'w') as f:
f.write(targetContent)
except Exception as e:
return False
@@ -1091,25 +1017,44 @@ class WireguardConfiguration:
if backupFileName not in backups:
return False
try:
- os.remove(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', backupFileName))
+ os.remove(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backupFileName))
except Exception as e:
return False
return True
+ def downloadBackup(self, backupFileName: str) -> tuple[bool, str] | tuple[bool, None]:
+ backup = list(filter(lambda x : x['filename'] == backupFileName, self.getBackups()))
+ if len(backup) == 0:
+ return False, None
+ zip = f'{str(uuid.UUID(int=random.Random().getrandbits(128), version=4))}.zip'
+ with ZipFile(os.path.join('download', zip), 'w') as zipF:
+ zipF.write(
+ os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backup[0]['filename']),
+ os.path.basename(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backup[0]['filename']))
+ )
+ if backup[0]['database']:
+ zipF.write(
+ os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backup[0]['filename'].replace('.conf', '.sql')),
+ os.path.basename(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup', backup[0]['filename'].replace('.conf', '.sql')))
+ )
+
+ return True, zip
+
def updateConfigurationSettings(self, newData: dict) -> tuple[bool, str]:
if self.Status:
self.toggleConfiguration()
original = []
dataChanged = False
- with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'), 'r') as f:
+ with open(self.configPath, 'r') as f:
original = [l.rstrip("\n") for l in f.readlines()]
allowEdit = ["Address", "PreUp", "PostUp", "PreDown", "PostDown", "ListenPort"]
+ if self.Protocol == 'awg':
+ allowEdit += ["Jc", "Jmin", "Jmax", "S1", "S2", "H1", "H2", "H3", "H4"]
start = original.index("[Interface]")
try:
end = original.index("[Peer]")
except ValueError as e:
end = len(original)
-
new = ["[Interface]"]
peerFound = False
for line in range(start, end):
@@ -1121,29 +1066,25 @@ class WireguardConfiguration:
new.insert(1, f"{key} = {str(newData[key]).strip()}")
new.append("")
for line in range(end, len(original)):
- new.append(original[line])
+ new.append(original[line])
self.backupConfigurationFile()
- print(f"[WGDashboard] Edited Configuration -- {self.Name}.conf")
- print("\n".join(new))
- with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'), 'w') as f:
+ with open(self.configPath, 'w') as f:
f.write("\n".join(new))
-
+
status, msg = self.toggleConfiguration()
if not status:
return False, msg
-
for i in allowEdit:
if isinstance(getattr(self, i), bool):
setattr(self, i, _strToBool(newData[i]))
else:
setattr(self, i, str(newData[i]))
-
return True, ""
def deleteConfiguration(self):
if self.getStatus():
self.toggleConfiguration()
- os.remove(self.__configPath)
+ os.remove(self.configPath)
self.__dropDatabase()
return True
@@ -1153,21 +1094,371 @@ class WireguardConfiguration:
try:
if self.getStatus():
self.toggleConfiguration()
- self.__createDatabase(newConfigurationName)
+ self.createDatabase(newConfigurationName)
sqlUpdate(f'INSERT INTO "{newConfigurationName}" SELECT * FROM "{self.Name}"')
sqlUpdate(f'INSERT INTO "{newConfigurationName}_restrict_access" SELECT * FROM "{self.Name}_restrict_access"')
sqlUpdate(f'INSERT INTO "{newConfigurationName}_deleted" SELECT * FROM "{self.Name}_deleted"')
sqlUpdate(f'INSERT INTO "{newConfigurationName}_transfer" SELECT * FROM "{self.Name}_transfer"')
AllPeerJobs.updateJobConfigurationName(self.Name, newConfigurationName)
shutil.copy(
- self.__configPath,
- os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{newConfigurationName}.conf')
+ self.configPath,
+ os.path.join(self.__getProtocolPath(), f'{newConfigurationName}.conf')
)
self.deleteConfiguration()
except Exception as e:
return False, str(e)
return True, None
+
+ def getNumberOfAvailableIP(self):
+ if len(self.Address) < 0:
+ return False, None
+ existedAddress = set()
+ availableAddress = {}
+ for p in self.Peers + self.getRestrictedPeersList():
+ peerAllowedIP = p.allowed_ip.split(',')
+ for pip in peerAllowedIP:
+ ppip = pip.strip().split('/')
+ if len(ppip) == 2:
+ try:
+ check = ipaddress.ip_network(ppip[0])
+ existedAddress.add(check)
+ except Exception as e:
+ print(f"[WGDashboard] Error: {self.Name} peer {p.id} have invalid ip")
+ configurationAddresses = self.Address.split(',')
+ for ca in configurationAddresses:
+ ca = ca.strip()
+ caSplit = ca.split('/')
+ try:
+ if len(caSplit) == 2:
+ network = ipaddress.ip_network(ca, False)
+ existedAddress.add(ipaddress.ip_network(caSplit[0]))
+ availableAddress[ca] = network.num_addresses
+ for p in existedAddress:
+ if p.version == network.version and p.subnet_of(network):
+ availableAddress[ca] -= 1
+ except Exception as e:
+ print(e)
+ print(f"[WGDashboard] Error: Failed to parse IP address {ca} from {self.Name}")
+ return True, availableAddress
+
+ def getAvailableIP(self, threshold = 255):
+ if len(self.Address) < 0:
+ return False, None
+ existedAddress = set()
+ availableAddress = {}
+ for p in self.Peers + self.getRestrictedPeersList():
+ peerAllowedIP = p.allowed_ip.split(',')
+ for pip in peerAllowedIP:
+ ppip = pip.strip().split('/')
+ if len(ppip) == 2:
+ try:
+ check = ipaddress.ip_network(ppip[0])
+ existedAddress.add(check.compressed)
+ except Exception as e:
+ print(f"[WGDashboard] Error: {self.Name} peer {p.id} have invalid ip")
+ configurationAddresses = self.Address.split(',')
+ for ca in configurationAddresses:
+ ca = ca.strip()
+ caSplit = ca.split('/')
+ try:
+ if len(caSplit) == 2:
+ network = ipaddress.ip_network(ca, False)
+ existedAddress.add(ipaddress.ip_network(caSplit[0]).compressed)
+ if threshold == -1:
+ availableAddress[ca] = filter(lambda ip : ip not in existedAddress,
+ map(lambda iph : ipaddress.ip_network(iph).compressed, network.hosts()))
+ else:
+ availableAddress[ca] = list(islice(filter(lambda ip : ip not in existedAddress,
+ map(lambda iph : ipaddress.ip_network(iph).compressed, network.hosts())), threshold))
+ except Exception as e:
+ print(e)
+ print(f"[WGDashboard] Error: Failed to parse IP address {ca} from {self.Name}")
+ print("Generated IP")
+ return True, availableAddress
+
+ def getRealtimeTrafficUsage(self):
+ stats = psutil.net_io_counters(pernic=True, nowrap=True)
+ if self.Name in stats.keys():
+ stat = stats[self.Name]
+ recv1 = stat.bytes_recv
+ sent1 = stat.bytes_sent
+ time.sleep(1)
+ stats = psutil.net_io_counters(pernic=True, nowrap=True)
+ if self.Name in stats.keys():
+ stat = stats[self.Name]
+ recv2 = stat.bytes_recv
+ sent2 = stat.bytes_sent
+ net_in = round((recv2 - recv1) / 1024 / 1024, 3)
+ net_out = round((sent2 - sent1) / 1024 / 1024, 3)
+ return {
+ "sent": net_out,
+ "recv": net_in
+ }
+ else:
+ return { "sent": 0, "recv": 0 }
+ else:
+ return { "sent": 0, "recv": 0 }
+
+"""
+AmneziaWG Configuration
+"""
+class AmneziaWireguardConfiguration(WireguardConfiguration):
+ def __init__(self, name: str = None, data: dict = None, backup: dict = None, startup: bool = False):
+ self.Jc = 0
+ self.Jmin = 0
+ self.Jmax = 0
+ self.S1 = 0
+ self.S2 = 0
+ self.H1 = 1
+ self.H2 = 2
+ self.H3 = 3
+ self.H4 = 4
+ super().__init__(name, data, backup, startup, wg=False)
+
+ def toJson(self):
+ self.Status = self.getStatus()
+ return {
+ "Status": self.Status,
+ "Name": self.Name,
+ "PrivateKey": self.PrivateKey,
+ "PublicKey": self.PublicKey,
+ "Address": self.Address,
+ "ListenPort": self.ListenPort,
+ "PreUp": self.PreUp,
+ "PreDown": self.PreDown,
+ "PostUp": self.PostUp,
+ "PostDown": self.PostDown,
+ "SaveConfig": self.SaveConfig,
+ "DataUsage": {
+ "Total": sum(list(map(lambda x: x.cumu_data + x.total_data, self.Peers))),
+ "Sent": sum(list(map(lambda x: x.cumu_sent + x.total_sent, self.Peers))),
+ "Receive": sum(list(map(lambda x: x.cumu_receive + x.total_receive, self.Peers)))
+ },
+ "ConnectedPeers": len(list(filter(lambda x: x.status == "running", self.Peers))),
+ "TotalPeers": len(self.Peers),
+ "Protocol": self.Protocol,
+ "Jc": self.Jc,
+ "Jmin": self.Jmin,
+ "Jmax": self.Jmax,
+ "S1": self.S1,
+ "S2": self.S2,
+ "H1": self.H1,
+ "H2": self.H2,
+ "H3": self.H3,
+ "H4": self.H4
+ }
+
+ def createDatabase(self, dbName = None):
+ if dbName is None:
+ dbName = self.Name
+
+ existingTables = sqlSelect("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
+ existingTables = [t['name'] for t in existingTables]
+ if dbName not in existingTables:
+ sqlUpdate(
+ """
+ CREATE TABLE '%s'(
+ id VARCHAR NOT NULL, private_key VARCHAR NULL, DNS VARCHAR NULL, advanced_security VARCHAR NULL,
+ endpoint_allowed_ip VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL,
+ total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
+ status VARCHAR NULL, latest_handshake VARCHAR NULL, allowed_ip VARCHAR NULL,
+ cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, mtu INT NULL,
+ keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
+ PRIMARY KEY (id)
+ )
+ """ % dbName
+ )
+
+ if f'{dbName}_restrict_access' not in existingTables:
+ sqlUpdate(
+ """
+ CREATE TABLE '%s_restrict_access' (
+ id VARCHAR NOT NULL, private_key VARCHAR NULL, DNS VARCHAR NULL, advanced_security VARCHAR NULL,
+ endpoint_allowed_ip VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL,
+ total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
+ status VARCHAR NULL, latest_handshake VARCHAR NULL, allowed_ip VARCHAR NULL,
+ cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, mtu INT NULL,
+ keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
+ PRIMARY KEY (id)
+ )
+ """ % dbName
+ )
+ if f'{dbName}_transfer' not in existingTables:
+ sqlUpdate(
+ """
+ CREATE TABLE '%s_transfer' (
+ id VARCHAR NOT NULL, total_receive FLOAT NULL,
+ total_sent FLOAT NULL, total_data FLOAT NULL,
+ cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, time DATETIME
+ )
+ """ % dbName
+ )
+ if f'{dbName}_deleted' not in existingTables:
+ sqlUpdate(
+ """
+ CREATE TABLE '%s_deleted' (
+ id VARCHAR NOT NULL, private_key VARCHAR NULL, DNS VARCHAR NULL, advanced_security VARCHAR NULL,
+ endpoint_allowed_ip VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL,
+ total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
+ status VARCHAR NULL, latest_handshake VARCHAR NULL, allowed_ip VARCHAR NULL,
+ cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, mtu INT NULL,
+ keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
+ PRIMARY KEY (id)
+ )
+ """ % dbName
+ )
+
+ def getPeers(self):
+ if self.configurationFileChanged():
+ self.Peers = []
+ with open(self.configPath, 'r') as configFile:
+ p = []
+ pCounter = -1
+ content = configFile.read().split('\n')
+ try:
+ peerStarts = content.index("[Peer]")
+ content = content[peerStarts:]
+ for i in content:
+ if not RegexMatch("#(.*)", i) and not RegexMatch(";(.*)", i):
+ if i == "[Peer]":
+ pCounter += 1
+ p.append({})
+ p[pCounter]["name"] = ""
+ else:
+ if len(i) > 0:
+ split = re.split(r'\s*=\s*', i, 1)
+ if len(split) == 2:
+ p[pCounter][split[0]] = split[1]
+
+ if RegexMatch("#Name# = (.*)", i):
+ split = re.split(r'\s*=\s*', i, 1)
+ if len(split) == 2:
+ p[pCounter]["name"] = split[1]
+
+ for i in p:
+ if "PublicKey" in i.keys():
+ checkIfExist = sqlSelect("SELECT * FROM '%s' WHERE id = ?" % self.Name,
+ ((i['PublicKey']),)).fetchone()
+ if checkIfExist is None:
+ newPeer = {
+ "id": i['PublicKey'],
+ "advanced_security": i.get('AdvancedSecurity', 'off'),
+ "private_key": "",
+ "DNS": DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1],
+ "endpoint_allowed_ip": DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[
+ 1],
+ "name": i.get("name"),
+ "total_receive": 0,
+ "total_sent": 0,
+ "total_data": 0,
+ "endpoint": "N/A",
+ "status": "stopped",
+ "latest_handshake": "N/A",
+ "allowed_ip": i.get("AllowedIPs", "N/A"),
+ "cumu_receive": 0,
+ "cumu_sent": 0,
+ "cumu_data": 0,
+ "traffic": [],
+ "mtu": DashboardConfig.GetConfig("Peers", "peer_mtu")[1],
+ "keepalive": DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1],
+ "remote_endpoint": DashboardConfig.GetConfig("Peers", "remote_endpoint")[1],
+ "preshared_key": i["PresharedKey"] if "PresharedKey" in i.keys() else ""
+ }
+ sqlUpdate(
+ """
+ INSERT INTO '%s'
+ VALUES (:id, :private_key, :DNS, :advanced_security, :endpoint_allowed_ip, :name, :total_receive, :total_sent,
+ :total_data, :endpoint, :status, :latest_handshake, :allowed_ip, :cumu_receive, :cumu_sent,
+ :cumu_data, :mtu, :keepalive, :remote_endpoint, :preshared_key);
+ """ % self.Name
+ , newPeer)
+ self.Peers.append(AmneziaWGPeer(newPeer, self))
+ else:
+ sqlUpdate("UPDATE '%s' SET allowed_ip = ? WHERE id = ?" % self.Name,
+ (i.get("AllowedIPs", "N/A"), i['PublicKey'],))
+ self.Peers.append(AmneziaWGPeer(checkIfExist, self))
+ except Exception as e:
+ if __name__ == '__main__':
+ print(f"[WGDashboard] {self.Name} Error: {str(e)}")
+ else:
+ self.Peers.clear()
+ checkIfExist = sqlSelect("SELECT * FROM '%s'" % self.Name).fetchall()
+ for i in checkIfExist:
+ self.Peers.append(AmneziaWGPeer(i, self))
+
+ def addPeers(self, peers: list) -> tuple[bool, dict]:
+ result = {
+ "message": None,
+ "peers": []
+ }
+ try:
+ for i in peers:
+ newPeer = {
+ "id": i['id'],
+ "private_key": i['private_key'],
+ "DNS": i['DNS'],
+ "endpoint_allowed_ip": i['endpoint_allowed_ip'],
+ "name": i['name'],
+ "total_receive": 0,
+ "total_sent": 0,
+ "total_data": 0,
+ "endpoint": "N/A",
+ "status": "stopped",
+ "latest_handshake": "N/A",
+ "allowed_ip": i.get("allowed_ip", "N/A"),
+ "cumu_receive": 0,
+ "cumu_sent": 0,
+ "cumu_data": 0,
+ "traffic": [],
+ "mtu": i['mtu'],
+ "keepalive": i['keepalive'],
+ "remote_endpoint": DashboardConfig.GetConfig("Peers", "remote_endpoint")[1],
+ "preshared_key": i["preshared_key"],
+ "advanced_security": i['advanced_security']
+ }
+ sqlUpdate(
+ """
+ INSERT INTO '%s'
+ VALUES (:id, :private_key, :DNS, :advanced_security, :endpoint_allowed_ip, :name, :total_receive, :total_sent,
+ :total_data, :endpoint, :status, :latest_handshake, :allowed_ip, :cumu_receive, :cumu_sent,
+ :cumu_data, :mtu, :keepalive, :remote_endpoint, :preshared_key);
+ """ % self.Name
+ , newPeer)
+ for p in peers:
+ presharedKeyExist = len(p['preshared_key']) > 0
+ rd = random.Random()
+ uid = str(uuid.UUID(int=rd.getrandbits(128), version=4))
+ if presharedKeyExist:
+ with open(uid, "w+") as f:
+ f.write(p['preshared_key'])
+
+ subprocess.check_output(
+ f"{self.Protocol} set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''} advanced-security {p['advanced_security']}",
+ shell=True, stderr=subprocess.STDOUT)
+ if presharedKeyExist:
+ os.remove(uid)
+ subprocess.check_output(
+ f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
+ self.getPeersList()
+ for p in peers:
+ p = self.searchPeer(p['id'])
+ if p[0]:
+ result['peers'].append(p[1])
+ return True, result
+ except Exception as e:
+ result['message'] = str(e)
+ return False, result
+
+ def getRestrictedPeers(self):
+ self.RestrictedPeers = []
+ restricted = sqlSelect("SELECT * FROM '%s_restrict_access'" % self.Name).fetchall()
+ for i in restricted:
+ self.RestrictedPeers.append(AmneziaWGPeer(i, self))
+
+"""
+Peer
+"""
class Peer:
def __init__(self, tableData, configuration: WireguardConfiguration):
self.configuration = configuration
@@ -1217,16 +1508,16 @@ class Peer:
if allowed_ip in existingAllowedIps:
return ResponseObject(False, "Allowed IP already taken by another peer")
- if not _checkIPWithRange(endpoint_allowed_ip):
+ if not ValidateIPAddressesWithRange(endpoint_allowed_ip):
return ResponseObject(False, f"Endpoint Allowed IPs format is incorrect")
- if len(dns_addresses) > 0 and not _checkDNS(dns_addresses):
+ if len(dns_addresses) > 0 and not ValidateDNSAddress(dns_addresses):
return ResponseObject(False, f"DNS format is incorrect")
if mtu < 0 or mtu > 1460:
return ResponseObject(False, "MTU format is not correct")
if keepalive < 0:
return ResponseObject(False, "Persistent Keepalive format is not correct")
if len(private_key) > 0:
- pubKey = _generatePublicKey(private_key)
+ pubKey = GenerateWireguardPublicKey(private_key)
if not pubKey[0] or pubKey[1] != self.id:
return ResponseObject(False, "Private key does not match with the public key")
try:
@@ -1239,15 +1530,14 @@ class Peer:
f.write(preshared_key)
newAllowedIPs = allowed_ip.replace(" ", "")
updateAllowedIp = subprocess.check_output(
- f"wg set {self.configuration.Name} peer {self.id} allowed-ips {newAllowedIPs}{f' preshared-key {uid}' if pskExist else ''}",
+ f"{self.configuration.Protocol} set {self.configuration.Name} peer {self.id} allowed-ips {newAllowedIPs} {f'preshared-key {uid}' if pskExist else 'preshared-key /dev/null'}",
shell=True, stderr=subprocess.STDOUT)
- if pskExist: os.remove(uid)
-
+ if pskExist: os.remove(uid)
if len(updateAllowedIp.decode().strip("\n")) != 0:
return ResponseObject(False,
"Update peer failed when updating Allowed IPs")
- saveConfig = subprocess.check_output(f"wg-quick save {self.configuration.Name}",
+ saveConfig = subprocess.check_output(f"{self.configuration.Protocol}-quick save {self.configuration.Name}",
shell=True, stderr=subprocess.STDOUT)
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
return ResponseObject(False,
@@ -1267,12 +1557,17 @@ class Peer:
if len(filename) == 0:
filename = "UntitledPeer"
filename = "".join(filename.split(' '))
- filename = f"{filename}_{self.configuration.Name}"
+ filename = f"{filename}"
illegal_filename = [".", ",", "/", "?", "<", ">", "\\", ":", "*", '|' '\"', "com1", "com2", "com3",
"com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4",
"lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "con", "nul", "prn"]
for i in illegal_filename:
filename = filename.replace(i, "")
+
+ finalFilename = ""
+ for i in filename:
+ if re.match("^[a-zA-Z0-9_=+.-]$", i):
+ finalFilename += i
peerConfiguration = f'''[Interface]
PrivateKey = {self.private_key}
@@ -1281,6 +1576,7 @@ MTU = {str(self.mtu)}
'''
if len(self.DNS) > 0:
peerConfiguration += f"DNS = {self.DNS}\n"
+
peerConfiguration += f'''
[Peer]
PublicKey = {self.configuration.PublicKey}
@@ -1291,7 +1587,7 @@ PersistentKeepalive = {str(self.keepalive)}
if len(self.preshared_key) > 0:
peerConfiguration += f"PresharedKey = {self.preshared_key}\n"
return {
- "fileName": filename,
+ "fileName": finalFilename,
"file": peerConfiguration
}
@@ -1305,20 +1601,144 @@ PersistentKeepalive = {str(self.keepalive)}
try:
if type == "total":
sqlUpdate("UPDATE '%s' SET total_data = 0, cumu_data = 0, total_receive = 0, cumu_receive = 0, total_sent = 0, cumu_sent = 0 WHERE id = ?" % self.configuration.Name, (self.id, ))
+ self.total_data = 0
+ self.total_receive = 0
+ self.total_sent = 0
+ self.cumu_data = 0
+ self.cumu_sent = 0
+ self.cumu_receive = 0
elif type == "receive":
sqlUpdate("UPDATE '%s' SET total_receive = 0, cumu_receive = 0 WHERE id = ?" % self.configuration.Name, (self.id, ))
+ self.cumu_receive = 0
+ self.total_receive = 0
elif type == "sent":
sqlUpdate("UPDATE '%s' SET total_sent = 0, cumu_sent = 0 WHERE id = ?" % self.configuration.Name, (self.id, ))
+ self.cumu_sent = 0
+ self.total_sent = 0
else:
return False
except Exception as e:
+ print(e)
return False
+
return True
-# Regex Match
-def regex_match(regex, text):
- pattern = re.compile(regex)
- return pattern.search(text) is not None
+
+class AmneziaWGPeer(Peer):
+ def __init__(self, tableData, configuration: AmneziaWireguardConfiguration):
+ self.advanced_security = tableData["advanced_security"]
+ super().__init__(tableData, configuration)
+ def downloadPeer(self) -> dict[str, str]:
+ filename = self.name
+ if len(filename) == 0:
+ filename = "UntitledPeer"
+ filename = "".join(filename.split(' '))
+ filename = f"{filename}_{self.configuration.Name}"
+ illegal_filename = [".", ",", "/", "?", "<", ">", "\\", ":", "*", '|' '\"', "com1", "com2", "com3",
+ "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4",
+ "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "con", "nul", "prn"]
+ for i in illegal_filename:
+ filename = filename.replace(i, "")
+
+ finalFilename = ""
+ for i in filename:
+ if re.match("^[a-zA-Z0-9_=+.-]$", i):
+ finalFilename += i
+
+ peerConfiguration = f'''[Interface]
+PrivateKey = {self.private_key}
+Address = {self.allowed_ip}
+MTU = {str(self.mtu)}
+Jc = {self.configuration.Jc}
+Jmin = {self.configuration.Jmin}
+Jmax = {self.configuration.Jmax}
+S1 = {self.configuration.S1}
+S2 = {self.configuration.S2}
+H1 = {self.configuration.H1}
+H2 = {self.configuration.H2}
+H3 = {self.configuration.H3}
+H4 = {self.configuration.H4}
+'''
+ if len(self.DNS) > 0:
+ peerConfiguration += f"DNS = {self.DNS}\n"
+ peerConfiguration += f'''
+[Peer]
+PublicKey = {self.configuration.PublicKey}
+AllowedIPs = {self.endpoint_allowed_ip}
+Endpoint = {DashboardConfig.GetConfig("Peers", "remote_endpoint")[1]}:{self.configuration.ListenPort}
+PersistentKeepalive = {str(self.keepalive)}
+'''
+ if len(self.preshared_key) > 0:
+ peerConfiguration += f"PresharedKey = {self.preshared_key}\n"
+ return {
+ "fileName": finalFilename,
+ "file": peerConfiguration
+ }
+
+ def updatePeer(self, name: str, private_key: str,
+ preshared_key: str,
+ dns_addresses: str, allowed_ip: str, endpoint_allowed_ip: str, mtu: int,
+ keepalive: int, advanced_security: str) -> ResponseObject:
+ if not self.configuration.getStatus():
+ self.configuration.toggleConfiguration()
+
+ existingAllowedIps = [item for row in list(
+ map(lambda x: [q.strip() for q in x.split(',')],
+ map(lambda y: y.allowed_ip,
+ list(filter(lambda k: k.id != self.id, self.configuration.getPeersList()))))) for item in row]
+
+ if allowed_ip in existingAllowedIps:
+ return ResponseObject(False, "Allowed IP already taken by another peer")
+ if not ValidateIPAddressesWithRange(endpoint_allowed_ip):
+ return ResponseObject(False, f"Endpoint Allowed IPs format is incorrect")
+ if len(dns_addresses) > 0 and not ValidateDNSAddress(dns_addresses):
+ return ResponseObject(False, f"DNS format is incorrect")
+ if mtu < 0 or mtu > 1460:
+ return ResponseObject(False, "MTU format is not correct")
+ if keepalive < 0:
+ return ResponseObject(False, "Persistent Keepalive format is not correct")
+ if advanced_security != "on" and advanced_security != "off":
+ return ResponseObject(False, "Advanced Security can only be on or off")
+ if len(private_key) > 0:
+ pubKey = GenerateWireguardPublicKey(private_key)
+ if not pubKey[0] or pubKey[1] != self.id:
+ return ResponseObject(False, "Private key does not match with the public key")
+ try:
+ rd = random.Random()
+ uid = str(uuid.UUID(int=rd.getrandbits(128), version=4))
+ pskExist = len(preshared_key) > 0
+
+ if pskExist:
+ with open(uid, "w+") as f:
+ f.write(preshared_key)
+ newAllowedIPs = allowed_ip.replace(" ", "")
+ updateAllowedIp = subprocess.check_output(
+ f"{self.configuration.Protocol} set {self.configuration.Name} peer {self.id} allowed-ips {newAllowedIPs} {f'preshared-key {uid}' if pskExist else 'preshared-key /dev/null'} advanced-security {advanced_security}",
+ shell=True, stderr=subprocess.STDOUT)
+
+ if pskExist: os.remove(uid)
+
+ if len(updateAllowedIp.decode().strip("\n")) != 0:
+ return ResponseObject(False,
+ "Update peer failed when updating Allowed IPs")
+ saveConfig = subprocess.check_output(f"{self.configuration.Protocol}-quick save {self.configuration.Name}",
+ shell=True, stderr=subprocess.STDOUT)
+ if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
+ return ResponseObject(False,
+ "Update peer failed when saving the configuration")
+ sqlUpdate(
+ '''UPDATE '%s' SET name = ?, private_key = ?, DNS = ?, endpoint_allowed_ip = ?, mtu = ?,
+ keepalive = ?, preshared_key = ?, advanced_security = ? WHERE id = ?''' % self.configuration.Name,
+ (name, private_key, dns_addresses, endpoint_allowed_ip, mtu,
+ keepalive, preshared_key, advanced_security, self.id,)
+ )
+ return ResponseObject()
+ except subprocess.CalledProcessError as exc:
+ return ResponseObject(False, exc.output.decode("UTF-8").strip())
+
+"""
+Dashboard API Key
+"""
class DashboardAPIKey:
def __init__(self, Key: str, CreatedAt: str, ExpiredAt: str):
self.Key = Key
@@ -1328,6 +1748,9 @@ class DashboardAPIKey:
def toJson(self):
return self.__dict__
+"""
+Dashboard Configuration
+"""
class DashboardConfig:
def __init__(self):
@@ -1335,7 +1758,7 @@ class DashboardConfig:
open(DASHBOARD_CONF, "x")
self.__config = configparser.ConfigParser(strict=False)
self.__config.read_file(open(DASHBOARD_CONF, "r+"))
- self.hiddenAttribute = ["totp_key"]
+ self.hiddenAttribute = ["totp_key", "auth_req"]
self.__default = {
"Account": {
"username": "admin",
@@ -1346,12 +1769,14 @@ class DashboardConfig:
},
"Server": {
"wg_conf_path": "/etc/wireguard",
+ "awg_conf_path": "/etc/amnezia/amneziawg",
"app_prefix": "",
"app_ip": "0.0.0.0",
"app_port": "10086",
"auth_req": "true",
"version": DASHBOARD_VERSION,
"dashboard_refresh_interval": "60000",
+ "dashboard_peer_list_display": "grid",
"dashboard_sort": "status",
"dashboard_theme": "dark",
"dashboard_api_key": "false",
@@ -1361,7 +1786,7 @@ class DashboardConfig:
"peer_global_DNS": "1.1.1.1",
"peer_endpoint_allowed_ip": "0.0.0.0/0",
"peer_display_mode": "grid",
- "remote_endpoint": ifcfg.default_interface()['inet'] if ifcfg.default_interface() else '',
+ "remote_endpoint": GetRemoteEndpoint(),
"peer_MTU": "1420",
"peer_keep_alive": "21"
},
@@ -1371,6 +1796,15 @@ class DashboardConfig:
"Database":{
"type": "sqlite"
},
+ "Email":{
+ "server": "",
+ "port": "",
+ "encryption": "",
+ "username": "",
+ "email_password": "",
+ "send_from": "",
+ "email_template": ""
+ },
"WireGuardConfiguration": {
"autostart": ""
}
@@ -1386,7 +1820,6 @@ class DashboardConfig:
self.APIAccessed = False
self.SetConfig("Server", "version", DASHBOARD_VERSION)
-
def __createAPIKeyTable(self):
existingTable = sqlSelect("SELECT name FROM sqlite_master WHERE type='table' AND name = 'DashboardAPIKeys'").fetchall()
if len(existingTable) == 0:
@@ -1410,22 +1843,25 @@ class DashboardConfig:
sqlUpdate("UPDATE DashboardAPIKeys SET ExpiredAt = datetime('now', 'localtime') WHERE Key = ?", (key, ))
self.DashboardAPIKeys = self.__getAPIKeys()
- def __configValidation(self, key, value: Any) -> [bool, str]:
- if type(value) is str and len(value) == 0:
+ def __configValidation(self, section : str, key: str, value: Any) -> [bool, str]:
+ if (type(value) is str and len(value) == 0
+ and section not in ['Email', 'WireGuardConfiguration'] and
+ (section == 'Peer' and key == 'peer_global_dns')):
return False, "Field cannot be empty!"
- if key == "peer_global_dns":
- return _checkDNS(value)
- if key == "peer_endpoint_allowed_ip":
+ if section == "Peers" and key == "peer_global_dns" and len(value) > 0:
+ return ValidateDNSAddress(value)
+ if section == "Peers" and key == "peer_endpoint_allowed_ip":
value = value.split(",")
for i in value:
+ i = i.strip()
try:
ipaddress.ip_network(i, strict=False)
except Exception as e:
return False, str(e)
- if key == "wg_conf_path":
+ if section == "Server" and key == "wg_conf_path":
if not os.path.exists(value):
return False, f"{value} is not a valid path"
- if key == "password":
+ if section == "Account" and key == "password":
if self.GetConfig("Account", "password")[0]:
if not self.__checkPassword(
value["currentPassword"], self.GetConfig("Account", "password")[1].encode("utf-8")):
@@ -1445,7 +1881,7 @@ class DashboardConfig:
return False, None
if not init:
- valid, msg = self.__configValidation(key, value)
+ valid, msg = self.__configValidation(section, key, value)
if not valid:
return False, msg
@@ -1454,15 +1890,22 @@ class DashboardConfig:
value = self.generatePassword(value["newPassword"]).decode("utf-8")
else:
value = self.generatePassword(value).decode("utf-8")
+
+ if section == "Email" and key == "email_template":
+ value = value.encode('unicode_escape').decode('utf-8')
if section == "Server" and key == "wg_conf_path":
if not os.path.exists(value):
return False, "Path does not exist"
if section not in self.__config:
- self.__config[section] = {}
-
- if key not in self.__config[section].keys() or value != self.__config[section][key]:
+ if init:
+ self.__config[section] = {}
+ else:
+ return False, "Section does not exist"
+
+ if ((key not in self.__config[section].keys() and init) or
+ (key in self.__config[section].keys() and value != self.__config[section][key] and not init)):
if type(value) is bool:
if value:
self.__config[section][key] = "true"
@@ -1475,6 +1918,8 @@ class DashboardConfig:
else:
self.__config[section][key] = value
return self.SaveConfig(), ""
+ else:
+ return False, f"{key} does not exist under {section}"
return True, ""
def SaveConfig(self) -> bool:
@@ -1492,14 +1937,18 @@ class DashboardConfig:
if key not in self.__config[section]:
return False, None
+ if section == "Email" and key == "email_template":
+ return True, self.__config[section][key].encode('utf-8').decode('unicode_escape')
+
+ if section == "WireGuardConfiguration" and key == "autostart":
+ return True, list(filter(lambda x: len(x) > 0, self.__config[section][key].split("||")))
+
if self.__config[section][key] in ["1", "yes", "true", "on"]:
return True, True
if self.__config[section][key] in ["0", "no", "false", "off"]:
return True, False
- if section == "WireGuardConfiguration" and key == "autostart":
- return True, list(filter(lambda x: len(x) > 0, self.__config[section][key].split("||")))
return True, self.__config[section][key]
@@ -1513,157 +1962,40 @@ class DashboardConfig:
the_dict[section][key] = self.GetConfig(section, key)[1]
return the_dict
-'''
-Private Functions
-'''
-
-def _strToBool(value: str) -> bool:
- return value.lower() in ("yes", "true", "t", "1", 1)
-
-def _regexMatch(regex, text):
- pattern = re.compile(regex)
- return pattern.search(text) is not None
-
-def _getConfigurationList(startup: bool = False):
- confs = os.listdir(DashboardConfig.GetConfig("Server", "wg_conf_path")[1])
- confs.sort()
- for i in confs:
- if _regexMatch("^(.{1,}).(conf)$", i):
- i = i.replace('.conf', '')
- try:
- if i in WireguardConfigurations.keys():
- if WireguardConfigurations[i].configurationFileChanged():
- WireguardConfigurations[i] = WireguardConfiguration(i)
- else:
- WireguardConfigurations[i] = WireguardConfiguration(i, startup=startup)
- except WireguardConfiguration.InvalidConfigurationFileException as e:
- print(f"{i} have an invalid configuration file.")
-
-def _checkIPWithRange(ip):
- ip_patterns = (
- r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|\/)){4}([0-9]{1,2})(,|$)",
- r"[0-9a-fA-F]{0,4}(:([0-9a-fA-F]{0,4})){1,7}\/([0-9]{1,3})(,|$)"
- )
-
- for match_pattern in ip_patterns:
- match_result = regex_match(match_pattern, ip)
- if match_result:
- result = match_result
- break
- else:
- result = None
-
- return result
-
-def _checkIP(ip):
- ip_patterns = (
- r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}",
- r"[0-9a-fA-F]{0,4}(:([0-9a-fA-F]{0,4})){1,7}$"
- )
- for match_pattern in ip_patterns:
- match_result = regex_match(match_pattern, ip)
- if match_result:
- result = match_result
- break
- else:
- result = None
-
- return result
-
-def _checkDNS(dns):
- dns = dns.replace(' ', '').split(',')
- for i in dns:
- if not _checkIP(i) and not regex_match(r"(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]", i):
- return False, f"{i} does not appear to be an valid DNS address"
- return True, ""
-
-def _generatePublicKey(privateKey) -> tuple[bool, str] | tuple[bool, None]:
- try:
- publicKey = subprocess.check_output(f"wg pubkey", input=privateKey.encode(), shell=True,
- stderr=subprocess.STDOUT)
- return True, publicKey.decode().strip('\n')
- except subprocess.CalledProcessError:
- return False, None
-
-def _generatePrivateKey() -> [bool, str]:
- try:
- publicKey = subprocess.check_output(f"wg genkey", shell=True,
- stderr=subprocess.STDOUT)
- return True, publicKey.decode().strip('\n')
- except subprocess.CalledProcessError:
- return False, None
-
-def _getWireguardConfigurationAvailableIP(configName: str, all: bool = False) -> tuple[bool, list[str]] | tuple[bool, None]:
- if configName not in WireguardConfigurations.keys():
- return False, None
- configuration = WireguardConfigurations[configName]
- if len(configuration.Address) > 0:
- address = configuration.Address.split(',')
- existedAddress = []
- availableAddress = []
- for p in configuration.Peers:
- if len(p.allowed_ip) > 0:
- add = p.allowed_ip.split(',')
- for i in add:
- a, c = i.split('/')
- try:
- existedAddress.append(ipaddress.ip_address(a.replace(" ", "")))
- except ValueError as error:
- print(f"[WGDashboard] Error: {configName} peer {p.id} have invalid ip")
-
- for p in configuration.getRestrictedPeersList():
- if len(p.allowed_ip) > 0:
- add = p.allowed_ip.split(',')
- for i in add:
- a, c = i.split('/')
- existedAddress.append(ipaddress.ip_address(a.replace(" ", "")))
-
- for i in address:
- addressSplit, cidr = i.split('/')
- existedAddress.append(ipaddress.ip_address(addressSplit.replace(" ", "")))
- for i in address:
- network = ipaddress.ip_network(i.replace(" ", ""), False)
- count = 0
- for h in network.hosts():
- if h not in existedAddress:
- availableAddress.append(ipaddress.ip_network(h).compressed)
- count += 1
- if not all:
- if network.version == 6 and count > 255:
- break
- return True, availableAddress
-
- return False, None
+"""
+Database Connection Functions
+"""
sqldb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard.db'), check_same_thread=False)
sqldb.row_factory = sqlite3.Row
-sqldb.isolation_level = None
-cursor = sqldb.cursor()
def sqlSelect(statement: str, paramters: tuple = ()) -> sqlite3.Cursor:
+ result = []
try:
cursor = sqldb.cursor()
- return cursor.execute(statement, paramters)
+ result = cursor.execute(statement, paramters)
except Exception as error:
print("[WGDashboard] SQLite Error:" + str(error) + " | Statement: " + statement)
- return []
-
-
+ return result
def sqlUpdate(statement: str, paramters: tuple = ()) -> sqlite3.Cursor:
+ sqldb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard.db'))
+ sqldb.row_factory = sqlite3.Row
+ cursor = sqldb.cursor()
with sqldb:
cursor = sqldb.cursor()
try:
statement = statement.rstrip(';')
s = f'BEGIN TRANSACTION;{statement};END TRANSACTION;'
cursor.execute(statement, paramters)
- sqldb.commit()
+ # sqldb.commit()
except Exception as error:
print("[WGDashboard] SQLite Error:" + str(error) + " | Statement: " + statement)
- return []
+ sqldb.close()
DashboardConfig = DashboardConfig()
+EmailSender = EmailSender(DashboardConfig)
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
cors = CORS(app, resources={rf"{APP_PREFIX}/api/*": {
"origins": "*",
@@ -1678,7 +2010,7 @@ API Routes
@app.before_request
def auth_req():
if request.method.lower() == 'options':
- return ResponseObject(True)
+ return ResponseObject(True)
DashboardConfig.APIAccessed = False
if "api" in request.path:
@@ -1709,15 +2041,16 @@ def auth_req():
DashboardConfig.APIAccessed = True
else:
DashboardConfig.APIAccessed = False
- if ('/static/' not in request.path and "username" not in session
+ whiteList = [
+ '/static/', 'validateAuthentication', 'authenticate', 'getDashboardConfiguration',
+ 'getDashboardTheme', 'getDashboardVersion', 'sharePeer/get', 'isTotpEnabled', 'locale',
+ '/fileDownload'
+ ]
+
+ if ("username" not in session
and (f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}/" != request.path
- and f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}" != request.path)
- and "validateAuthentication" not in request.path and "authenticate" not in request.path
- and "getDashboardConfiguration" not in request.path and "getDashboardTheme" not in request.path
- and "getDashboardVersion" not in request.path
- and "sharePeer/get" not in request.path
- and "isTotpEnabled" not in request.path
- and "locale" not in request.path
+ and f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}" != request.path)
+ and len(list(filter(lambda x : x not in request.path, whiteList))) == len(whiteList)
):
response = Flask.make_response(app, {
"status": False,
@@ -1729,19 +2062,27 @@ def auth_req():
return response
@app.route(f'{APP_PREFIX}/api/handshake', methods=["GET", "OPTIONS"])
-def API_ValidateAPIKey():
+def API_Handshake():
return ResponseObject(True)
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
def API_ValidateAuthentication():
- token = request.cookies.get("authToken") + ""
- if token == "" or "username" not in session or session["username"] != token:
- return ResponseObject(False, "Invalid authentication.")
+ token = request.cookies.get("authToken")
+ if DashboardConfig.GetConfig("Server", "auth_req")[1]:
+ if token is None or token == "" or "username" not in session or session["username"] != token:
+ return ResponseObject(False, "Invalid authentication.")
return ResponseObject(True)
+@app.get(f'{APP_PREFIX}/api/requireAuthentication')
+def API_RequireAuthentication():
+ return ResponseObject(data=DashboardConfig.GetConfig("Server", "auth_req")[1])
+
@app.post(f'{APP_PREFIX}/api/authenticate')
def API_AuthenticateLogin():
data = request.get_json()
+ if not DashboardConfig.GetConfig("Server", "auth_req")[1]:
+ return ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
+
if DashboardConfig.APIAccessed:
authToken = hashlib.sha256(f"{request.headers.get('wg-dashboard-apikey')}{datetime.now()}".encode()).hexdigest()
session['username'] = authToken
@@ -1782,18 +2123,21 @@ def API_SignOut():
@app.route(f'{APP_PREFIX}/api/getWireguardConfigurations', methods=["GET"])
def API_getWireguardConfigurations():
- _getConfigurationList()
+ InitWireguardConfigurationsList()
return ResponseObject(data=[wc for wc in WireguardConfigurations.values()])
@app.route(f'{APP_PREFIX}/api/addWireguardConfiguration', methods=["POST"])
def API_addWireguardConfiguration():
data = request.get_json()
requiredKeys = [
- "ConfigurationName", "Address", "ListenPort", "PrivateKey"
+ "ConfigurationName", "Address", "ListenPort", "PrivateKey", "Protocol"
]
for i in requiredKeys:
if i not in data.keys():
return ResponseObject(False, "Please provide all required parameters.")
+
+ if data.get("Protocol") not in ProtocolsEnabled():
+ return ResponseObject(False, "Please provide a valid protocol: wg / awg.")
# Check duplicate names, ports, address
for i in WireguardConfigurations.values():
@@ -1813,22 +2157,27 @@ def API_addWireguardConfiguration():
"Address")
if "Backup" in data.keys():
- if not os.path.exists(os.path.join(
- DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
- 'WGDashboard_Backup',
- data["Backup"])) or not os.path.exists(os.path.join(
- DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
- 'WGDashboard_Backup',
- data["Backup"].replace('.conf', '.sql'))):
- return ResponseObject(False, "Backup file does not exist")
+ path = {
+ "wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
+ "awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
+ }
+
+ if (os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"])) and
+ os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
+ protocol = "wg"
+ elif (os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"])) and
+ os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
+ protocol = "awg"
+ else:
+ return ResponseObject(False, "Backup does not exist")
shutil.copy(
- os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', data["Backup"]),
- os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{data["ConfigurationName"]}.conf')
+ os.path.join(path[protocol], 'WGDashboard_Backup', data["Backup"]),
+ os.path.join(path[protocol], f'{data["ConfigurationName"]}.conf')
)
- WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data, name=data['ConfigurationName'])
+ WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data, name=data['ConfigurationName']) if protocol == 'wg' else AmneziaWireguardConfiguration(data=data, name=data['ConfigurationName'])
else:
- WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data)
+ WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data) if data.get('Protocol') == 'wg' else AmneziaWireguardConfiguration(data=data)
return ResponseObject()
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration/')
@@ -1836,7 +2185,7 @@ def API_toggleWireguardConfiguration():
configurationName = request.args.get('configurationName')
if configurationName is None or len(
configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
- return ResponseObject(False, "Please provide a valid configuration name")
+ return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
toggleStatus, msg = WireguardConfigurations[configurationName].toggleConfiguration()
return ResponseObject(toggleStatus, msg, WireguardConfigurations[configurationName].Status)
@@ -1849,44 +2198,76 @@ def API_updateWireguardConfiguration():
return ResponseObject(False, "Please provide these following field: " + ", ".join(requiredKeys))
name = data.get("Name")
if name not in WireguardConfigurations.keys():
- return ResponseObject(False, "Configuration does not exist")
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
status, msg = WireguardConfigurations[name].updateConfigurationSettings(data)
return ResponseObject(status, message=msg, data=WireguardConfigurations[name])
+@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRawFile')
+def API_GetWireguardConfigurationRawFile():
+ configurationName = request.args.get('configurationName')
+ if configurationName is None or len(
+ configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
+ return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
+
+ return ResponseObject(data={
+ "path": WireguardConfigurations[configurationName].configPath,
+ "content": WireguardConfigurations[configurationName].getRawConfigurationFile()
+ })
+
+@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationRawFile')
+def API_UpdateWireguardConfigurationRawFile():
+ data = request.get_json()
+ configurationName = data.get('configurationName')
+ rawConfiguration = data.get('rawConfiguration')
+ if configurationName is None or len(
+ configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
+ return ResponseObject(False, "Please provide a valid configuration name")
+ if rawConfiguration is None or len(rawConfiguration) == 0:
+ return ResponseObject(False, "Please provide content")
+
+ status, err = WireguardConfigurations[configurationName].updateRawConfigurationFile(rawConfiguration)
+
+ return ResponseObject(status=status, message=err)
+
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfiguration')
def API_deleteWireguardConfiguration():
data = request.get_json()
- if "Name" not in data.keys() or data.get("Name") is None or data.get("Name") not in WireguardConfigurations.keys():
- return ResponseObject(False, "Please provide the configuration name you want to delete")
-
- status = WireguardConfigurations[data.get("Name")].deleteConfiguration()
-
+ if "ConfigurationName" not in data.keys() or data.get("ConfigurationName") is None or data.get("ConfigurationName") not in WireguardConfigurations.keys():
+ return ResponseObject(False, "Please provide the configuration name you want to delete", status_code=404)
+ status = WireguardConfigurations[data.get("ConfigurationName")].deleteConfiguration()
if status:
- WireguardConfigurations.pop(data.get("Name"))
+ WireguardConfigurations.pop(data.get("ConfigurationName"))
return ResponseObject(status)
@app.post(f'{APP_PREFIX}/api/renameWireguardConfiguration')
def API_renameWireguardConfiguration():
data = request.get_json()
- keys = ["Name", "NewConfigurationName"]
+ keys = ["ConfigurationName", "NewConfigurationName"]
for k in keys:
if (k not in data.keys() or data.get(k) is None or len(data.get(k)) == 0 or
- (k == "Name" and data.get(k) not in WireguardConfigurations.keys())):
- return ResponseObject(False, "Please provide the configuration name you want to rename")
+ (k == "ConfigurationName" and data.get(k) not in WireguardConfigurations.keys())):
+ return ResponseObject(False, "Please provide the configuration name you want to rename", status_code=404)
- status, message = WireguardConfigurations[data.get("Name")].renameConfiguration(data.get("NewConfigurationName"))
+ status, message = WireguardConfigurations[data.get("ConfigurationName")].renameConfiguration(data.get("NewConfigurationName"))
if status:
- WireguardConfigurations.pop(data.get("Name"))
+ WireguardConfigurations.pop(data.get("ConfigurationName"))
WireguardConfigurations[data.get("NewConfigurationName")] = WireguardConfiguration(data.get("NewConfigurationName"))
return ResponseObject(status, message)
+@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRealtimeTraffic')
+def API_getWireguardConfigurationRealtimeTraffic():
+ configurationName = request.args.get('configurationName')
+ if configurationName is None or configurationName not in WireguardConfigurations.keys():
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
+ return ResponseObject(data=WireguardConfigurations[configurationName].getRealtimeTrafficUsage())
+
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
def API_getWireguardConfigurationBackup():
configurationName = request.args.get('configurationName')
if configurationName is None or configurationName not in WireguardConfigurations.keys():
- return ResponseObject(False, "Configuration does not exist")
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
return ResponseObject(data=WireguardConfigurations[configurationName].getBackups())
@app.get(f'{APP_PREFIX}/api/getAllWireguardConfigurationBackup')
@@ -1900,71 +2281,85 @@ def API_getAllWireguardConfigurationBackup():
b = WireguardConfigurations[i].getBackups(True)
if len(b) > 0:
data['ExistingConfigurations'][i] = WireguardConfigurations[i].getBackups(True)
-
- directory = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup')
- files = [(file, os.path.getctime(os.path.join(directory, file)))
- for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))]
- files.sort(key=lambda x: x[1], reverse=True)
-
- for f, ct in files:
- if _regexMatch(r"^(.*)_(.*)\.(conf)$", f):
- s = re.search(r"^(.*)_(.*)\.(conf)$", f)
- name = s.group(1)
- if name not in existingConfiguration:
- if name not in data['NonExistingConfigurations'].keys():
- data['NonExistingConfigurations'][name] = []
-
- date = s.group(2)
- d = {
- "filename": f,
- "backupDate": date,
- "content": open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
- }
- if f.replace(".conf", ".sql") in list(os.listdir(directory)):
- d['database'] = True
- d['databaseContent'] = open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read()
- data['NonExistingConfigurations'][name].append(d)
+
+ for protocol in ProtocolsEnabled():
+ directory = os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup')
+ files = [(file, os.path.getctime(os.path.join(directory, file)))
+ for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))]
+ files.sort(key=lambda x: x[1], reverse=True)
+
+ for f, ct in files:
+ if RegexMatch(r"^(.*)_(.*)\.(conf)$", f):
+ s = re.search(r"^(.*)_(.*)\.(conf)$", f)
+ name = s.group(1)
+ if name not in existingConfiguration:
+ if name not in data['NonExistingConfigurations'].keys():
+ data['NonExistingConfigurations'][name] = []
+
+ date = s.group(2)
+ d = {
+ "protocol": protocol,
+ "filename": f,
+ "backupDate": date,
+ "content": open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
+ }
+ if f.replace(".conf", ".sql") in list(os.listdir(directory)):
+ d['database'] = True
+ d['databaseContent'] = open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read()
+ data['NonExistingConfigurations'][name].append(d)
return ResponseObject(data=data)
@app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup')
def API_createWireguardConfigurationBackup():
configurationName = request.args.get('configurationName')
if configurationName is None or configurationName not in WireguardConfigurations.keys():
- return ResponseObject(False, "Configuration does not exist")
- return ResponseObject(status=WireguardConfigurations[configurationName].backupConfigurationFile(),
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
+ return ResponseObject(status=WireguardConfigurations[configurationName].backupConfigurationFile()[0],
data=WireguardConfigurations[configurationName].getBackups())
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfigurationBackup')
def API_deleteWireguardConfigurationBackup():
data = request.get_json()
- if ("configurationName" not in data.keys() or
- "backupFileName" not in data.keys() or
- len(data['configurationName']) == 0 or
- len(data['backupFileName']) == 0):
+ if ("ConfigurationName" not in data.keys() or
+ "BackupFileName" not in data.keys() or
+ len(data['ConfigurationName']) == 0 or
+ len(data['BackupFileName']) == 0):
return ResponseObject(False,
- "Please provide configurationName and backupFileName in body")
- configurationName = data['configurationName']
- backupFileName = data['backupFileName']
+ "Please provide configurationName and backupFileName in body", status_code=400)
+ configurationName = data['ConfigurationName']
+ backupFileName = data['BackupFileName']
if configurationName not in WireguardConfigurations.keys():
- return ResponseObject(False, "Configuration does not exist")
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
- return ResponseObject(WireguardConfigurations[configurationName].deleteBackup(backupFileName))
+ status = WireguardConfigurations[configurationName].deleteBackup(backupFileName)
+ return ResponseObject(status=status, message=(None if status else 'Backup file does not exist'),
+ status_code = (200 if status else 404))
+
+@app.get(f'{APP_PREFIX}/api/downloadWireguardConfigurationBackup')
+def API_downloadWireguardConfigurationBackup():
+ configurationName = request.args.get('configurationName')
+ backupFileName = request.args.get('backupFileName')
+ if configurationName is None or configurationName not in WireguardConfigurations.keys():
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
+ status, zip = WireguardConfigurations[configurationName].downloadBackup(backupFileName)
+ return ResponseObject(status, data=zip, status_code=(200 if status else 404))
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
def API_restoreWireguardConfigurationBackup():
data = request.get_json()
- if ("configurationName" not in data.keys() or
- "backupFileName" not in data.keys() or
- len(data['configurationName']) == 0 or
- len(data['backupFileName']) == 0):
+ if ("ConfigurationName" not in data.keys() or
+ "BackupFileName" not in data.keys() or
+ len(data['ConfigurationName']) == 0 or
+ len(data['BackupFileName']) == 0):
return ResponseObject(False,
- "Please provide configurationName and backupFileName in body")
- configurationName = data['configurationName']
- backupFileName = data['backupFileName']
+ "Please provide ConfigurationName and BackupFileName in body", status_code=400)
+ configurationName = data['ConfigurationName']
+ backupFileName = data['BackupFileName']
if configurationName not in WireguardConfigurations.keys():
- return ResponseObject(False, "Configuration does not exist")
-
- return ResponseObject(WireguardConfigurations[configurationName].restoreBackup(backupFileName))
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
+
+ status = WireguardConfigurations[configurationName].restoreBackup(backupFileName)
+ return ResponseObject(status=status, message=(None if status else 'Restore backup failed'))
@app.get(f'{APP_PREFIX}/api/getDashboardConfiguration')
def API_getDashboardConfiguration():
@@ -1978,13 +2373,12 @@ def API_updateDashboardConfigurationItem():
valid, msg = DashboardConfig.SetConfig(
data["section"], data["key"], data['value'])
if not valid:
- return ResponseObject(False, msg)
-
+ return ResponseObject(False, msg, status_code=404)
if data['section'] == "Server":
if data['key'] == 'wg_conf_path':
WireguardConfigurations.clear()
- _getConfigurationList()
-
+ WireguardConfigurations.clear()
+ InitWireguardConfigurationsList()
return ResponseObject(True, data=DashboardConfig.GetConfig(data["section"], data["key"])[1])
@app.get(f'{APP_PREFIX}/api/getDashboardAPIKeys')
@@ -1998,7 +2392,7 @@ def API_newDashboardAPIKey():
data = request.get_json()
if DashboardConfig.GetConfig('Server', 'dashboard_api_key'):
try:
- if data['neverExpire']:
+ if data['NeverExpire']:
expiredAt = None
else:
expiredAt = datetime.strptime(data['ExpiredAt'], '%Y-%m-%d %H:%M:%S')
@@ -2015,6 +2409,8 @@ def API_deleteDashboardAPIKey():
if len(data['Key']) > 0 and len(list(filter(lambda x : x.Key == data['Key'], DashboardConfig.DashboardAPIKeys))) > 0:
DashboardConfig.deleteAPIKey(data['Key'])
return ResponseObject(True, data=DashboardConfig.DashboardAPIKeys)
+ else:
+ return ResponseObject(False, "API Key does not exist", status_code=404)
return ResponseObject(False, "Dashboard API Keys function is disbaled")
@app.post(f'{APP_PREFIX}/api/updatePeerSettings/')
@@ -2033,8 +2429,13 @@ def API_updatePeerSettings(configName):
wireguardConfig = WireguardConfigurations[configName]
foundPeer, peer = wireguardConfig.searchPeer(id)
if foundPeer:
+ if wireguardConfig.Protocol == 'wg':
+ return peer.updatePeer(name, private_key, preshared_key, dns_addresses,
+ allowed_ip, endpoint_allowed_ip, mtu, keepalive)
+
return peer.updatePeer(name, private_key, preshared_key, dns_addresses,
- allowed_ip, endpoint_allowed_ip, mtu, keepalive)
+ allowed_ip, endpoint_allowed_ip, mtu, keepalive, data.get('advanced_security', 'off'))
+
return ResponseObject(False, "Peer does not exist")
@app.post(f'{APP_PREFIX}/api/resetPeerData/')
@@ -2048,7 +2449,13 @@ def API_resetPeerData(configName):
foundPeer, peer = wgc.searchPeer(id)
if not foundPeer:
return ResponseObject(False, "Configuration/Peer does not exist")
- return ResponseObject(status=peer.resetDataUsage(type))
+
+ resetStatus = peer.resetDataUsage(type)
+ if resetStatus:
+ wgc.restrictPeers([id])
+ wgc.allowAccessPeers([id])
+
+ return ResponseObject(status=resetStatus)
@app.post(f'{APP_PREFIX}/api/deletePeers/')
def API_deletePeers(configName: str) -> ResponseObject:
@@ -2071,7 +2478,7 @@ def API_restrictPeers(configName: str) -> ResponseObject:
return ResponseObject(False, "Please specify one or more peers")
configuration = WireguardConfigurations.get(configName)
return configuration.restrictPeers(peers)
- return ResponseObject(False, "Configuration does not exist")
+ return ResponseObject(False, "Configuration does not exist", status_code=404)
@app.post(f'{APP_PREFIX}/api/sharePeer/create')
def API_sharePeer_create():
@@ -2083,7 +2490,10 @@ def API_sharePeer_create():
return ResponseObject(False, "Please specify configuration and peers")
activeLink = AllPeerShareLinks.getLink(Configuration, Peer)
if len(activeLink) > 0:
- return ResponseObject(False, "This peer is already sharing, please stop sharing first.")
+ return ResponseObject(True,
+ "This peer is already sharing. Please view data for shared link.",
+ data=activeLink[0]
+ )
status, message = AllPeerShareLinks.addLink(Configuration, Peer, ExpireDate)
if not status:
return ResponseObject(status, message)
@@ -2145,73 +2555,115 @@ def API_addPeers(configName):
bulkAdd: bool = data.get("bulkAdd", False)
bulkAddAmount: int = data.get('bulkAddAmount', 0)
preshared_key_bulkAdd: bool = data.get('preshared_key_bulkAdd', False)
-
-
+
public_key: str = data.get('public_key', "")
- allowed_ips: list[str] = data.get('allowed_ips', "")
+ allowed_ips: list[str] = data.get('allowed_ips', [])
+ allowed_ips_validation: bool = data.get('allowed_ips_validation', True)
endpoint_allowed_ip: str = data.get('endpoint_allowed_ip', DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[1])
dns_addresses: str = data.get('DNS', DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1])
mtu: int = data.get('mtu', int(DashboardConfig.GetConfig("Peers", "peer_MTU")[1]))
keep_alive: int = data.get('keepalive', int(DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1]))
- preshared_key: str = data.get('preshared_key', "")
-
+ preshared_key: str = data.get('preshared_key', "")
if type(mtu) is not int or mtu < 0 or mtu > 1460:
mtu = int(DashboardConfig.GetConfig("Peers", "peer_MTU")[1])
if type(keep_alive) is not int or keep_alive < 0:
keep_alive = int(DashboardConfig.GetConfig("Peers", "peer_keep_alive")[1])
- if len(dns_addresses) == 0:
- dns_addresses = DashboardConfig.GetConfig("Peers", "peer_global_DNS")[1]
- if len(endpoint_allowed_ip) == 0:
- endpoint_allowed_ip = DashboardConfig.GetConfig("Peers", "peer_endpoint_allowed_ip")[1]
config = WireguardConfigurations.get(configName)
- if not bulkAdd and (len(public_key) == 0 or len(allowed_ips) == 0):
- return ResponseObject(False, "Please provide at least public_key and allowed_ips")
if not config.getStatus():
config.toggleConfiguration()
- availableIps = _getWireguardConfigurationAvailableIP(configName)
+ ipStatus, availableIps = config.getAvailableIP(-1)
+ ipCountStatus, numberOfAvailableIPs = config.getNumberOfAvailableIP()
+ defaultIPSubnet = list(availableIps.keys())[0]
if bulkAdd:
if type(preshared_key_bulkAdd) is not bool:
preshared_key_bulkAdd = False
-
if type(bulkAddAmount) is not int or bulkAddAmount < 1:
return ResponseObject(False, "Please specify amount of peers you want to add")
- if not availableIps[0]:
+ if not ipStatus:
return ResponseObject(False, "No more available IP can assign")
- if bulkAddAmount > len(availableIps[1]):
+ if len(availableIps.keys()) == 0:
+ return ResponseObject(False, "This configuration does not have any IP address available")
+ if bulkAddAmount > sum(list(numberOfAvailableIPs.values())):
return ResponseObject(False,
- f"The maximum number of peers can add is {len(availableIps[1])}")
+ f"The maximum number of peers can add is {sum(list(numberOfAvailableIPs.values()))}")
keyPairs = []
- for i in range(bulkAddAmount):
- newPrivateKey = _generatePrivateKey()[1]
- keyPairs.append({
- "private_key": newPrivateKey,
- "id": _generatePublicKey(newPrivateKey)[1],
- "preshared_key": (_generatePrivateKey()[1] if preshared_key_bulkAdd else ""),
- "allowed_ip": availableIps[1][i],
- "name": f"BulkPeer #{(i + 1)}_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
- "DNS": dns_addresses,
- "endpoint_allowed_ip": endpoint_allowed_ip,
- "mtu": mtu,
- "keepalive": keep_alive
- })
- if len(keyPairs) == 0:
+ addedCount = 0
+ for subnet in availableIps.keys():
+ for ip in availableIps[subnet]:
+ newPrivateKey = GenerateWireguardPrivateKey()[1]
+ addedCount += 1
+ keyPairs.append({
+ "private_key": newPrivateKey,
+ "id": GenerateWireguardPublicKey(newPrivateKey)[1],
+ "preshared_key": (GenerateWireguardPrivateKey()[1] if preshared_key_bulkAdd else ""),
+ "allowed_ip": ip,
+ "name": f"BulkPeer_{(addedCount + 1)}_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
+ "DNS": dns_addresses,
+ "endpoint_allowed_ip": endpoint_allowed_ip,
+ "mtu": mtu,
+ "keepalive": keep_alive,
+ "advanced_security": data.get("advanced_security", "off")
+ })
+ if addedCount == bulkAddAmount:
+ break
+ if addedCount == bulkAddAmount:
+ break
+ if len(keyPairs) == 0 or (bulkAdd and len(keyPairs) != bulkAddAmount):
return ResponseObject(False, "Generating key pairs by bulk failed")
- config.addPeers(keyPairs)
- return ResponseObject()
+ status, result = config.addPeers(keyPairs)
+ return ResponseObject(status=status, message=result['message'], data=result['peers'])
else:
if config.searchPeer(public_key)[0] is True:
return ResponseObject(False, f"This peer already exist")
name = data.get("name", "")
private_key = data.get("private_key", "")
-
- for i in allowed_ips:
- if i not in availableIps[1]:
- return ResponseObject(False, f"This IP is not available: {i}")
-
- status = config.addPeers([
+
+ if len(public_key) == 0:
+ if len(private_key) == 0:
+ private_key = GenerateWireguardPrivateKey()[1]
+ public_key = GenerateWireguardPublicKey(private_key)[1]
+ else:
+ public_key = GenerateWireguardPublicKey(private_key)[1]
+ else:
+ if len(private_key) > 0:
+ genPub = GenerateWireguardPublicKey(private_key)[1]
+ # Check if provided pubkey match provided private key
+ if public_key != genPub:
+ return ResponseObject(False, "Provided Public Key does not match provided Private Key")
+
+ # if len(public_key) == 0 and len(private_key) == 0:
+ # private_key = GenerateWireguardPrivateKey()[1]
+ # public_key = GenerateWireguardPublicKey(private_key)[1]
+ # elif len(public_key) == 0 and len(private_key) > 0:
+ # public_key = GenerateWireguardPublicKey(private_key)[1]
+
+
+ if len(allowed_ips) == 0:
+ if ipStatus:
+ for subnet in availableIps.keys():
+ for ip in availableIps[subnet]:
+ allowed_ips = [ip]
+ break
+ break
+ else:
+ return ResponseObject(False, "No more available IP can assign")
+
+ if allowed_ips_validation:
+ for i in allowed_ips:
+ found = False
+ for subnet in availableIps.keys():
+ network = ipaddress.ip_network(subnet, False)
+ ap = ipaddress.ip_network(i)
+ if network.version == ap.version and ap.subnet_of(network):
+ found = True
+
+ if not found:
+ return ResponseObject(False, f"This IP is not available: {i}")
+
+ status, result = config.addPeers([
{
"name": name,
"id": public_key,
@@ -2221,12 +2673,13 @@ def API_addPeers(configName):
"endpoint_allowed_ip": endpoint_allowed_ip,
"DNS": dns_addresses,
"mtu": mtu,
- "keepalive": keep_alive
+ "keepalive": keep_alive,
+ "advanced_security": data.get("advanced_security", "off")
}]
)
- return ResponseObject(status)
+ return ResponseObject(status=status, message=result['message'], data=result['peers'])
except Exception as e:
- print(e)
+ print(e, str(e.__traceback__))
return ResponseObject(False, "Add peers failed. Please see data for specific issue")
return ResponseObject(False, "Configuration does not exist")
@@ -2251,7 +2704,7 @@ def API_downloadAllPeers(configName):
untitledPeer = 0
for i in configuration.Peers:
file = i.downloadPeer()
- if file["fileName"] == "UntitledPeer_" + configName:
+ if file["fileName"] == "UntitledPeer":
file["fileName"] = str(untitledPeer) + "_" + file["fileName"]
untitledPeer += 1
peerData.append(file)
@@ -2259,7 +2712,16 @@ def API_downloadAllPeers(configName):
@app.get(f"{APP_PREFIX}/api/getAvailableIPs/")
def API_getAvailableIPs(configName):
- status, ips = _getWireguardConfigurationAvailableIP(configName)
+ if configName not in WireguardConfigurations.keys():
+ return ResponseObject(False, "Configuration does not exist")
+ status, ips = WireguardConfigurations.get(configName).getAvailableIP()
+ return ResponseObject(status=status, data=ips)
+
+@app.get(f"{APP_PREFIX}/api/getNumberOfAvailableIPs/")
+def API_getNumberOfAvailableIPs(configName):
+ if configName not in WireguardConfigurations.keys():
+ return ResponseObject(False, "Configuration does not exist")
+ status, ips = WireguardConfigurations.get(configName).getNumberOfAvailableIP()
return ResponseObject(status=status, data=ips)
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationInfo')
@@ -2281,19 +2743,22 @@ def API_getDashboardTheme():
def API_getDashboardVersion():
return ResponseObject(data=DashboardConfig.GetConfig("Server", "version")[1])
-@app.post(f'{APP_PREFIX}/api/savePeerScheduleJob/')
+@app.post(f'{APP_PREFIX}/api/savePeerScheduleJob')
def API_savePeerScheduleJob():
data = request.json
if "Job" not in data.keys():
return ResponseObject(False, "Please specify job")
job: dict = data['Job']
- if "Peer" not in job.keys() or "Configuration" not in job.keys() or job['Configuration'] not in WireguardConfigurations.keys():
+ if "Peer" not in job.keys() or "Configuration" not in job.keys():
return ResponseObject(False, "Please specify peer and configuration")
configuration = WireguardConfigurations.get(job['Configuration'])
+ if configuration is None:
+ return ResponseObject(False, "Configuration does not exist")
f, fp = configuration.searchPeer(job['Peer'])
if not f:
return ResponseObject(False, "Peer does not exist")
-
+
+
s, p = AllPeerJobs.saveJob(PeerJob(
job['JobID'], job['Configuration'], job['Peer'], job['Field'], job['Operator'], job['Value'],
job['CreationDate'], job['ExpireDate'], job['Action']))
@@ -2301,22 +2766,24 @@ def API_savePeerScheduleJob():
return ResponseObject(s, data=p)
return ResponseObject(s, message=p)
-@app.post(f'{APP_PREFIX}/api/deletePeerScheduleJob/')
+@app.post(f'{APP_PREFIX}/api/deletePeerScheduleJob')
def API_deletePeerScheduleJob():
data = request.json
if "Job" not in data.keys():
return ResponseObject(False, "Please specify job")
job: dict = data['Job']
- if "Peer" not in job.keys() or "Configuration" not in job.keys() or job['Configuration'] not in WireguardConfigurations.keys():
+ if "Peer" not in job.keys() or "Configuration" not in job.keys():
return ResponseObject(False, "Please specify peer and configuration")
configuration = WireguardConfigurations.get(job['Configuration'])
+ if configuration is None:
+ return ResponseObject(False, "Configuration does not exist")
f, fp = configuration.searchPeer(job['Peer'])
if not f:
return ResponseObject(False, "Peer does not exist")
s, p = AllPeerJobs.deleteJob(PeerJob(
job['JobID'], job['Configuration'], job['Peer'], job['Field'], job['Operator'], job['Value'],
- job['CreationDate'], job['ExpireDate'], job['Action']), 'API Call')
+ job['CreationDate'], job['ExpireDate'], job['Action']))
if s:
return ResponseObject(s, data=p)
return ResponseObject(s, message=p)
@@ -2331,6 +2798,20 @@ def API_getPeerScheduleJobLogs(configName):
requestAll = True
return ResponseObject(data=JobLogger.getLogs(requestAll, configName))
+'''
+File Download
+'''
+@app.get(f'{APP_PREFIX}/fileDownload')
+def API_download():
+ file = request.args.get('file')
+ if file is None or len(file) == 0:
+ return ResponseObject(False, "Please specify a file")
+ if os.path.exists(os.path.join('download', file)):
+ return send_file(os.path.join('download', file), as_attachment=True)
+ else:
+ return ResponseObject(False, "File does not exist")
+
+
'''
Tools
'''
@@ -2374,7 +2855,6 @@ def API_ping_execute():
try:
if ip is not None and len(ip) > 0 and count is not None and count.isnumeric():
result = ping(ip, count=int(count), source=None)
-
data = {
"address": result.address,
"is_alive": result.is_alive,
@@ -2386,8 +2866,6 @@ def API_ping_execute():
"package_loss": result.packet_loss,
"geo": None
}
-
-
try:
r = requests.get(f"http://ip-api.com/json/{result.address}?field=city")
data['geo'] = r.json()
@@ -2435,9 +2913,9 @@ def API_traceroute_execute():
data=json.dumps([x['ip'] for x in result]))
d = r.json()
for i in range(len(result)):
- result[i]['geo'] = d[i]
+ result[i]['geo'] = d[i]
except Exception as e:
- print(e)
+ return ResponseObject(data=result, message="Failed to request IP address geolocation")
return ResponseObject(data=result)
except Exception as exp:
return ResponseObject(False, exp)
@@ -2453,13 +2931,12 @@ def API_getDashboardUpdate():
tagName = data.get('tag_name')
htmlUrl = data.get('html_url')
if tagName is not None and htmlUrl is not None:
- if tagName != DASHBOARD_VERSION:
+ if version.parse(tagName) > version.parse(DASHBOARD_VERSION):
return ResponseObject(message=f"{tagName} is now available for update!", data=htmlUrl)
else:
return ResponseObject(message="You're on the latest version")
return ResponseObject(False)
-
- except urllib.error.HTTPError and urllib.error.URLError as e:
+ except Exception as e:
return ResponseObject(False, f"Request to GitHub API failed.")
'''
@@ -2491,7 +2968,6 @@ def API_Welcome_VerifyTotpLink():
DashboardConfig.SetConfig("Account", "enable_totp", "true")
return ResponseObject(totp == data['totp'])
-
@app.post(f'{APP_PREFIX}/api/Welcome_Finish')
def API_Welcome_Finish():
data = request.get_json()
@@ -2520,9 +2996,8 @@ class Locale:
self.localePath = './static/locale/'
self.activeLanguages = {}
with open(os.path.join(f"{self.localePath}active_languages.json"), "r") as f:
- self.activeLanguages = json.loads(''.join(f.readlines()))
+ self.activeLanguages = sorted(json.loads(''.join(f.readlines())), key=lambda x : x['lang_name'])
-
def getLanguage(self) -> dict | None:
currentLanguage = DashboardConfig.GetConfig("Server", "dashboard_language")[1]
if currentLanguage == "en":
@@ -2539,7 +3014,6 @@ class Locale:
else:
DashboardConfig.SetConfig("Server", "dashboard_language", lang_id)
-
Locale = Locale()
@app.get(f'{APP_PREFIX}/api/locale')
@@ -2557,13 +3031,76 @@ def API_Locale_Update():
return ResponseObject(False, "Please specify a lang_id")
Locale.updateLanguage(data['lang_id'])
return ResponseObject(data=Locale.getLanguage())
+
+@app.get(f'{APP_PREFIX}/api/email/ready')
+def API_Email_Ready():
+ return ResponseObject(EmailSender.ready())
+
+@app.post(f'{APP_PREFIX}/api/email/send')
+def API_Email_Send():
+ data = request.get_json()
+ if "Receiver" not in data.keys():
+ return ResponseObject(False, "Please at least specify receiver")
+ body = data.get('Body', '')
+ download = None
+ if ("ConfigurationName" in data.keys()
+ and "Peer" in data.keys()):
+ if data.get('ConfigurationName') in WireguardConfigurations.keys():
+ configuration = WireguardConfigurations.get(data.get('ConfigurationName'))
+ attachmentName = ""
+ if configuration is not None:
+ fp, p = configuration.searchPeer(data.get('Peer'))
+ if fp:
+ template = Template(body)
+ download = p.downloadPeer()
+ body = template.render(peer=p.toJson(), configurationFile=download)
+ if data.get('IncludeAttachment', False):
+ u = str(uuid4())
+ attachmentName = f'{u}.conf'
+ with open(os.path.join('./attachments', attachmentName,), 'w+') as f:
+ f.write(download['file'])
+
+ s, m = EmailSender.send(data.get('Receiver'), data.get('Subject', ''), body,
+ data.get('IncludeAttachment', False), (attachmentName if download else ''))
+ return ResponseObject(s, m)
+
+@app.post(f'{APP_PREFIX}/api/email/previewBody')
+def API_Email_PreviewBody():
+ data = request.get_json()
+ body = data.get('Body', '')
+ if len(body) == 0:
+ return ResponseObject(False, "Nothing to preview")
+ if ("ConfigurationName" not in data.keys()
+ or "Peer" not in data.keys() or data.get('ConfigurationName') not in WireguardConfigurations.keys()):
+ return ResponseObject(False, "Please specify configuration and peer")
+
+ configuration = WireguardConfigurations.get(data.get('ConfigurationName'))
+ fp, p = configuration.searchPeer(data.get('Peer'))
+ if not fp:
+ return ResponseObject(False, "Peer does not exist")
+
+ try:
+ template = Template(body)
+ download = p.downloadPeer()
+ body = template.render(peer=p.toJson(), configurationFile=download)
+ return ResponseObject(data=body)
+ except Exception as e:
+ return ResponseObject(False, message=str(e))
+
+@app.get(f'{APP_PREFIX}/api/systemStatus')
+def API_SystemStatus():
+ return ResponseObject(data=SystemStatus)
+
+@app.get(f'{APP_PREFIX}/api/protocolsEnabled')
+def API_ProtocolsEnabled():
+ return ResponseObject(data=ProtocolsEnabled())
@app.get(f'{APP_PREFIX}/')
def index():
return render_template('index.html')
-def backGroundThread():
+def peerInformationBackgroundThread():
global WireguardConfigurations
print(f"[WGDashboard] Background Thread #1 Started", flush=True)
time.sleep(10)
@@ -2594,27 +3131,64 @@ def gunicornConfig():
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
return app_ip, app_port
+def ProtocolsEnabled() -> list[str]:
+ from shutil import which
+ protocols = []
+ if which('awg') is not None and which('awg-quick') is not None:
+ protocols.append("awg")
+ if which('wg') is not None and which('wg-quick') is not None:
+ protocols.append("wg")
+ return protocols
+
+def InitWireguardConfigurationsList(startup: bool = False):
+ if os.path.exists(DashboardConfig.GetConfig("Server", "wg_conf_path")[1]):
+ confs = os.listdir(DashboardConfig.GetConfig("Server", "wg_conf_path")[1])
+ confs.sort()
+ for i in confs:
+ if RegexMatch("^(.{1,}).(conf)$", i):
+ i = i.replace('.conf', '')
+ try:
+ if i in WireguardConfigurations.keys():
+ if WireguardConfigurations[i].configurationFileChanged():
+ WireguardConfigurations[i] = WireguardConfiguration(i)
+ else:
+ WireguardConfigurations[i] = WireguardConfiguration(i, startup=startup)
+ except WireguardConfiguration.InvalidConfigurationFileException as e:
+ print(f"{i} have an invalid configuration file.")
+
+ if "awg" in ProtocolsEnabled():
+ confs = os.listdir(DashboardConfig.GetConfig("Server", "awg_conf_path")[1])
+ confs.sort()
+ for i in confs:
+ if RegexMatch("^(.{1,}).(conf)$", i):
+ i = i.replace('.conf', '')
+ try:
+ if i in WireguardConfigurations.keys():
+ if WireguardConfigurations[i].configurationFileChanged():
+ WireguardConfigurations[i] = AmneziaWireguardConfiguration(i)
+ else:
+ WireguardConfigurations[i] = AmneziaWireguardConfiguration(i, startup=startup)
+ except WireguardConfigurations.InvalidConfigurationFileException as e:
+ print(f"{i} have an invalid configuration file.")
+
AllPeerShareLinks: PeerShareLinks = PeerShareLinks()
AllPeerJobs: PeerJobs = PeerJobs()
-JobLogger: PeerJobLogger = PeerJobLogger()
-DashboardLogger: DashboardLogger = DashboardLogger()
+JobLogger: PeerJobLogger = PeerJobLogger(CONFIGURATION_PATH, AllPeerJobs)
+DashboardLogger: DashboardLogger = DashboardLogger(CONFIGURATION_PATH)
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
WireguardConfigurations: dict[str, WireguardConfiguration] = {}
-_getConfigurationList(startup=True)
+AmneziaWireguardConfigurations: dict[str, AmneziaWireguardConfiguration] = {}
+InitWireguardConfigurationsList(startup=True)
def startThreads():
- bgThread = threading.Thread(target=backGroundThread)
- bgThread.daemon = True
+ bgThread = threading.Thread(target=peerInformationBackgroundThread, daemon=True)
bgThread.start()
-
- scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread)
- scheduleJobThread.daemon = True
+ scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread, daemon=True)
scheduleJobThread.start()
-
if __name__ == "__main__":
startThreads()
- app.run(host=app_ip, debug=False, port=app_port)
+ app.run(host=app_ip, debug=False, port=app_port)
\ No newline at end of file
diff --git a/src/gunicorn.conf.py b/src/gunicorn.conf.py
index c3d08e8..fd0b332 100644
--- a/src/gunicorn.conf.py
+++ b/src/gunicorn.conf.py
@@ -1,15 +1,13 @@
-import dashboard
+import os.path
+import dashboard, configparser
from datetime import datetime
-
global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger
app_host, app_port = dashboard.gunicornConfig()
date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S')
-
def post_worker_init(worker):
dashboard.startThreads()
-
worker_class = 'gthread'
workers = 1
threads = 1
@@ -21,6 +19,8 @@ accesslog = f"./log/access_{date}.log"
log_level = "debug"
capture_output = True
errorlog = f"./log/error_{date}.log"
-print(f"[WGDashboard] WGDashboard w/ Gunicorn will be running on {bind}", flush=True)
-print(f"[WGDashboard] Access log file is at {accesslog}", flush=True)
-print(f"[WGDashboard] Error log file is at {errorlog}", flush=True)
+pythonpath = "., ./modules"
+
+print(f"[Gunicorn] WGDashboard w/ Gunicorn will be running on {bind}", flush=True)
+print(f"[Gunicorn] Access log file is at {accesslog}", flush=True)
+print(f"[Gunicorn] Error log file is at {errorlog}", flush=True)
diff --git a/src/modules/DashboardLogger.py b/src/modules/DashboardLogger.py
new file mode 100644
index 0000000..31f3b01
--- /dev/null
+++ b/src/modules/DashboardLogger.py
@@ -0,0 +1,35 @@
+"""
+Dashboard Logger Class
+"""
+import sqlite3, os, uuid
+
+class DashboardLogger:
+ def __init__(self, CONFIGURATION_PATH):
+ self.loggerdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_log.db'),
+ isolation_level=None,
+ check_same_thread=False)
+ self.loggerdb.row_factory = sqlite3.Row
+ self.__createLogDatabase()
+ self.log(Message="WGDashboard started")
+ def __createLogDatabase(self):
+ with self.loggerdb:
+ loggerdbCursor = self.loggerdb.cursor()
+ existingTable = loggerdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
+ existingTable = [t['name'] for t in existingTable]
+ if "DashboardLog" not in existingTable:
+ loggerdbCursor.execute(
+ "CREATE TABLE DashboardLog (LogID VARCHAR NOT NULL, LogDate DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S','now', 'localtime')), URL VARCHAR, IP VARCHAR, Status VARCHAR, Message VARCHAR, PRIMARY KEY (LogID))")
+ if self.loggerdb.in_transaction:
+ self.loggerdb.commit()
+
+ def log(self, URL: str = "", IP: str = "", Status: str = "true", Message: str = "") -> bool:
+ try:
+ loggerdbCursor = self.loggerdb.cursor()
+ loggerdbCursor.execute(
+ "INSERT INTO DashboardLog (LogID, URL, IP, Status, Message) VALUES (?, ?, ?, ?, ?);", (str(uuid.uuid4()), URL, IP, Status, Message,))
+ loggerdbCursor.close()
+ self.loggerdb.commit()
+ return True
+ except Exception as e:
+ print(f"[WGDashboard] Access Log Error: {str(e)}")
+ return False
\ No newline at end of file
diff --git a/src/modules/Email.py b/src/modules/Email.py
new file mode 100644
index 0000000..20dd7b5
--- /dev/null
+++ b/src/modules/Email.py
@@ -0,0 +1,70 @@
+import os.path
+import smtplib
+from email import encoders
+from email.header import Header
+from email.mime.base import MIMEBase
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
+from email.utils import formataddr
+
+class EmailSender:
+ def __init__(self, DashboardConfig):
+ self.smtp = None
+ self.DashboardConfig = DashboardConfig
+ if not os.path.exists('../attachments'):
+ os.mkdir('../attachments')
+
+ def Server(self):
+ return self.DashboardConfig.GetConfig("Email", "server")[1]
+
+ def Port(self):
+ return self.DashboardConfig.GetConfig("Email", "port")[1]
+
+ def Encryption(self):
+ return self.DashboardConfig.GetConfig("Email", "encryption")[1]
+
+ def Username(self):
+ return self.DashboardConfig.GetConfig("Email", "username")[1]
+
+ def Password(self):
+ return self.DashboardConfig.GetConfig("Email", "email_password")[1]
+
+ def SendFrom(self):
+ return self.DashboardConfig.GetConfig("Email", "send_from")[1]
+
+ def ready(self):
+ print(self.Server())
+ return len(self.Server()) > 0 and len(self.Port()) > 0 and len(self.Encryption()) > 0 and len(self.Username()) > 0 and len(self.Password()) > 0
+
+ def send(self, receiver, subject, body, includeAttachment = False, attachmentName = ""):
+ if self.ready():
+ try:
+ self.smtp = smtplib.SMTP(self.Server(), port=int(self.Port()))
+ self.smtp.ehlo()
+ if self.Encryption() == "STARTTLS":
+ self.smtp.starttls()
+ self.smtp.login(self.Username(), self.Password())
+ message = MIMEMultipart()
+ message['Subject'] = subject
+ message['From'] = formataddr((Header(self.SendFrom()).encode(), self.Username()))
+ message["To"] = receiver
+ message.attach(MIMEText(body, "plain"))
+
+ if includeAttachment and len(attachmentName) > 0:
+ attachmentPath = os.path.join('./attachments', attachmentName)
+ if os.path.exists(attachmentPath):
+ attachment = MIMEBase("application", "octet-stream")
+ with open(os.path.join('./attachments', attachmentName), 'rb') as f:
+ attachment.set_payload(f.read())
+ encoders.encode_base64(attachment)
+ attachment.add_header("Content-Disposition", f"attachment; filename= {attachmentName}",)
+ message.attach(attachment)
+ else:
+ self.smtp.close()
+ return False, "Attachment does not exist"
+ self.smtp.sendmail(self.Username(), receiver, message.as_string())
+ self.smtp.close()
+ return True, None
+ except Exception as e:
+ return False, f"Send failed | Reason: {e}"
+ return False, "SMTP not configured"
\ No newline at end of file
diff --git a/src/modules/Log.py b/src/modules/Log.py
new file mode 100644
index 0000000..70f7fbb
--- /dev/null
+++ b/src/modules/Log.py
@@ -0,0 +1,22 @@
+"""
+Log Class
+"""
+class Log:
+ def __init__(self, LogID: str, JobID: str, LogDate: str, Status: str, Message: str):
+ self.LogID = LogID
+ self.JobID = JobID
+ self.LogDate = LogDate
+ self.Status = Status
+ self.Message = Message
+
+ def toJson(self):
+ return {
+ "LogID": self.LogID,
+ "JobID": self.JobID,
+ "LogDate": self.LogDate,
+ "Status": self.Status,
+ "Message": self.Message
+ }
+
+ def __dict__(self):
+ return self.toJson()
\ No newline at end of file
diff --git a/src/modules/PeerJob.py b/src/modules/PeerJob.py
new file mode 100644
index 0000000..83d75c3
--- /dev/null
+++ b/src/modules/PeerJob.py
@@ -0,0 +1,32 @@
+"""
+Peer Job
+"""
+from datetime import datetime
+class PeerJob:
+ def __init__(self, JobID: str, Configuration: str, Peer: str,
+ Field: str, Operator: str, Value: str, CreationDate: datetime, ExpireDate: datetime, Action: str):
+ self.Action = Action
+ self.ExpireDate = ExpireDate
+ self.CreationDate = CreationDate
+ self.Value = Value
+ self.Operator = Operator
+ self.Field = Field
+ self.Configuration = Configuration
+ self.Peer = Peer
+ self.JobID = JobID
+
+ def toJson(self):
+ return {
+ "JobID": self.JobID,
+ "Configuration": self.Configuration,
+ "Peer": self.Peer,
+ "Field": self.Field,
+ "Operator": self.Operator,
+ "Value": self.Value,
+ "CreationDate": self.CreationDate,
+ "ExpireDate": self.ExpireDate,
+ "Action": self.Action
+ }
+
+ def __dict__(self):
+ return self.toJson()
\ No newline at end of file
diff --git a/src/modules/PeerJobLogger.py b/src/modules/PeerJobLogger.py
new file mode 100644
index 0000000..3e89791
--- /dev/null
+++ b/src/modules/PeerJobLogger.py
@@ -0,0 +1,53 @@
+"""
+Peer Job Logger
+"""
+import sqlite3, os, uuid
+from .Log import Log
+
+class PeerJobLogger:
+ def __init__(self, CONFIGURATION_PATH, AllPeerJobs):
+ self.loggerdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_log.db'),
+ check_same_thread=False)
+ self.loggerdb.row_factory = sqlite3.Row
+ self.logs: list[Log] = []
+ self.__createLogDatabase()
+ self.AllPeerJobs = AllPeerJobs
+ def __createLogDatabase(self):
+ with self.loggerdb:
+ loggerdbCursor = self.loggerdb.cursor()
+
+ existingTable = loggerdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
+ existingTable = [t['name'] for t in existingTable]
+
+ if "JobLog" not in existingTable:
+ loggerdbCursor.execute("CREATE TABLE JobLog (LogID VARCHAR NOT NULL, JobID NOT NULL, LogDate DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S','now', 'localtime')), Status VARCHAR NOT NULL, Message VARCHAR, PRIMARY KEY (LogID))")
+ if self.loggerdb.in_transaction:
+ self.loggerdb.commit()
+ def log(self, JobID: str, Status: bool = True, Message: str = "") -> bool:
+ try:
+ with self.loggerdb:
+ loggerdbCursor = self.loggerdb.cursor()
+ loggerdbCursor.execute(f"INSERT INTO JobLog (LogID, JobID, Status, Message) VALUES (?, ?, ?, ?)",
+ (str(uuid.uuid4()), JobID, Status, Message,))
+ if self.loggerdb.in_transaction:
+ self.loggerdb.commit()
+ except Exception as e:
+ print(f"[WGDashboard] Peer Job Log Error: {str(e)}")
+ return False
+ return True
+
+ def getLogs(self, all: bool = False, configName = None) -> list[Log]:
+ logs: list[Log] = []
+ try:
+ allJobs = self.AllPeerJobs.getAllJobs(configName)
+ allJobsID = ", ".join([f"'{x.JobID}'" for x in allJobs])
+ with self.loggerdb:
+ loggerdbCursor = self.loggerdb.cursor()
+ table = loggerdbCursor.execute(f"SELECT * FROM JobLog WHERE JobID IN ({allJobsID}) ORDER BY LogDate DESC").fetchall()
+ self.logs.clear()
+ for l in table:
+ logs.append(
+ Log(l["LogID"], l["JobID"], l["LogDate"], l["Status"], l["Message"]))
+ except Exception as e:
+ return logs
+ return logs
diff --git a/src/modules/SystemStatus.py b/src/modules/SystemStatus.py
new file mode 100644
index 0000000..26d2390
--- /dev/null
+++ b/src/modules/SystemStatus.py
@@ -0,0 +1,135 @@
+import psutil
+class SystemStatus:
+ def __init__(self):
+ self.CPU = CPU()
+ self.MemoryVirtual = Memory('virtual')
+ self.MemorySwap = Memory('swap')
+ self.Disks = Disks()
+ self.NetworkInterfaces = NetworkInterfaces()
+ self.Processes = Processes()
+ def toJson(self):
+ return {
+ "CPU": self.CPU,
+ "Memory": {
+ "VirtualMemory": self.MemoryVirtual,
+ "SwapMemory": self.MemorySwap
+ },
+ "Disks": self.Disks,
+ "NetworkInterfaces": self.NetworkInterfaces,
+ "Processes": self.Processes
+ }
+
+
+class CPU:
+ def __init__(self):
+ self.cpu_percent: float = 0
+ self.cpu_percent_per_cpu: list[float] = []
+ def getData(self):
+ try:
+ self.cpu_percent_per_cpu = psutil.cpu_percent(interval=0.5, percpu=True)
+ self.cpu_percent = psutil.cpu_percent(interval=0.5)
+ except Exception as e:
+ pass
+ def toJson(self):
+ self.getData()
+ return self.__dict__
+
+class Memory:
+ def __init__(self, memoryType: str):
+ self.__memoryType__ = memoryType
+ self.total = 0
+ self.available = 0
+ self.percent = 0
+ def getData(self):
+ try:
+ if self.__memoryType__ == "virtual":
+ memory = psutil.virtual_memory()
+ else:
+ memory = psutil.swap_memory()
+ self.total = memory.total
+ self.available = memory.available
+ self.percent = memory.percent
+ except Exception as e:
+ pass
+ def toJson(self):
+ self.getData()
+ return self.__dict__
+
+class Disks:
+ def __init__(self):
+ self.disks : list[Disk] = []
+ def getData(self):
+ try:
+ self.disks = list(map(lambda x : Disk(x.mountpoint), psutil.disk_partitions()))
+ except Exception as e:
+ pass
+ def toJson(self):
+ self.getData()
+ return self.disks
+
+class Disk:
+ def __init__(self, mountPoint: str):
+ self.total = 0
+ self.used = 0
+ self.free = 0
+ self.percent = 0
+ self.mountPoint = mountPoint
+ def getData(self):
+ try:
+ disk = psutil.disk_usage(self.mountPoint)
+ self.total = disk.total
+ self.free = disk.free
+ self.used = disk.used
+ self.percent = disk.percent
+ except Exception as e:
+ pass
+ def toJson(self):
+ self.getData()
+ return self.__dict__
+
+class NetworkInterfaces:
+ def __init__(self):
+ self.interfaces = {}
+ def getData(self):
+ try:
+ network = psutil.net_io_counters(pernic=True, nowrap=True)
+ for i in network.keys():
+ self.interfaces[i] = network[i]._asdict()
+ except Exception as e:
+ pass
+ def toJson(self):
+ self.getData()
+ return self.interfaces
+
+class Process:
+ def __init__(self, name, command, pid, percent):
+ self.name = name
+ self.command = command
+ self.pid = pid
+ self.percent = percent
+ def toJson(self):
+ return self.__dict__
+
+class Processes:
+ def __init__(self):
+ self.CPU_Top_10_Processes: list[Process] = []
+ self.Memory_Top_10_Processes: list[Process] = []
+ def getData(self):
+ while True:
+ try:
+ processes = list(psutil.process_iter())
+ self.CPU_Top_10_Processes = sorted(
+ list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.cpu_percent()), processes)),
+ key=lambda x : x.percent, reverse=True)[:20]
+ self.Memory_Top_10_Processes = sorted(
+ list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.memory_percent()), processes)),
+ key=lambda x : x.percent, reverse=True)[:20]
+ break
+ except Exception as e:
+ break
+ def toJson(self):
+ self.getData()
+ return {
+ "cpu_top_10": self.CPU_Top_10_Processes,
+ "memory_top_10": self.Memory_Top_10_Processes
+ }
\ No newline at end of file
diff --git a/src/requirements.txt b/src/requirements.txt
index a7014b5..793219e 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -6,4 +6,5 @@ Flask
flask-cors
icmplib
gunicorn
-requests
\ No newline at end of file
+requests
+tcconfig
\ No newline at end of file
diff --git a/src/static/app/dist/assets/configuration-w_AfPSyO.js b/src/static/app/dist/assets/configuration-w_AfPSyO.js
new file mode 100644
index 0000000..daba8f1
--- /dev/null
+++ b/src/static/app/dist/assets/configuration-w_AfPSyO.js
@@ -0,0 +1 @@
+import{_ as r,c as i,d as o,w as e,k as l,a as t,j as _,i as a,l as d,S as u}from"./index-CUmHRwBw.js";const m={name:"configuration"},p={class:"mt-md-5 mt-3 text-body"};function f(k,x,h,w,$,v){const n=l("RouterView");return t(),i("div",p,[o(n,null,{default:e(({Component:s,route:c})=>[o(_,{name:"fade2",mode:"out-in"},{default:e(()=>[(t(),a(u,null,{default:e(()=>[(t(),a(d(s),{key:c.path}))]),_:2},1024))]),_:2},1024)]),_:1})])}const B=r(m,[["render",f]]);export{B as default};
diff --git a/src/static/app/dist/assets/configurationBackupRestore-BeBqyCqt.css b/src/static/app/dist/assets/configurationBackupRestore-BeBqyCqt.css
deleted file mode 100644
index fd039a1..0000000
--- a/src/static/app/dist/assets/configurationBackupRestore-BeBqyCqt.css
+++ /dev/null
@@ -1 +0,0 @@
-.confirmationContainer[data-v-a575be12]{background-color:#00000087;z-index:9999;backdrop-filter:blur(1px);-webkit-backdrop-filter:blur(1px)}.list1-enter-active[data-v-a575be12]{transition-delay:var(--6919ade8)!important}.card[data-v-1f718118],.title[data-v-1f718118]{width:100%}@media screen and (min-width: 700px){.card[data-v-1f718118],.title[data-v-1f718118]{width:700px}}.animate__fadeInUp[data-v-1f718118]{animation-timing-function:cubic-bezier(.42,0,.22,1)}.list1-move[data-v-1f718118],.list1-enter-active[data-v-1f718118],.list1-leave-active[data-v-1f718118]{transition:all .5s cubic-bezier(.42,0,.22,1)}.list1-enter-from[data-v-1f718118],.list1-leave-to[data-v-1f718118]{opacity:0;transform:translateY(30px)}.list1-leave-active[data-v-1f718118]{width:100%;position:absolute}
diff --git a/src/static/app/dist/assets/configurationList-B0Mt7yx0.css b/src/static/app/dist/assets/configurationList-B0Mt7yx0.css
new file mode 100644
index 0000000..00e795c
--- /dev/null
+++ b/src/static/app/dist/assets/configurationList-B0Mt7yx0.css
@@ -0,0 +1 @@
+.fade-enter-active[data-v-6451623a]{transition-delay:var(--d75b06ae)!important}.progress-bar[data-v-c7f6d1a1]{width:0;transition:all 1s cubic-bezier(.42,0,.22,1)}.filter a[data-v-ea61b607]{text-decoration:none}
diff --git a/src/static/app/dist/assets/configurationList-CWcGXYWr.css b/src/static/app/dist/assets/configurationList-CWcGXYWr.css
deleted file mode 100644
index 18565db..0000000
--- a/src/static/app/dist/assets/configurationList-CWcGXYWr.css
+++ /dev/null
@@ -1 +0,0 @@
-.fade-enter-active[data-v-a85a04a5]{transition-delay:var(--1d5189b2)!important}.configurationListTitle{.btn[data-v-16b5ab33]{border-radius:50%!important}}
diff --git a/src/static/app/dist/assets/configurationList-DZKIqimE.js b/src/static/app/dist/assets/configurationList-DZKIqimE.js
new file mode 100644
index 0000000..cb750ff
--- /dev/null
+++ b/src/static/app/dist/assets/configurationList-DZKIqimE.js
@@ -0,0 +1 @@
+import{_ as D,D as M,g as I,a as i,c as n,b as t,d as r,w as S,n as m,t as u,f as v,i as _,e as y,m as U,v as V,p as B,k as p,r as O,q as K,j as G,s as L,o as R,x as W,F as w,h as k,W as q,G as $,T as F,y as z}from"./index-CUmHRwBw.js";import{L as x}from"./localeText-DvaqSAco.js";import{_ as j}from"./protocolBadge-DbaEYoS7.js";import{C as J}from"./storageMount.vue_vue_type_style_index_0_scoped_a382214a_lang-C_2M2VRO.js";const N={name:"configurationCard",components:{ProtocolBadge:j,LocaleText:x},props:{c:{Name:String,Status:Boolean,PublicKey:String,PrivateKey:String},delay:String,display:String},data(){return{configurationToggling:!1}},setup(){return{dashboardConfigurationStore:M()}},methods:{toggle(){this.configurationToggling=!0,I("/api/toggleWireguardConfiguration/",{configurationName:this.c.Name},s=>{s.status?this.dashboardConfigurationStore.newMessage("Server",`${this.c.Name} ${s.data?"is on":"is off"}`):this.dashboardConfigurationStore.newMessage("Server",s.message,"danger"),this.c.Status=s.data,this.configurationToggling=!1})}}},T=()=>{B(s=>({d75b06ae:s.delay}))},P=N.setup;N.setup=P?(s,e)=>(T(),P(s,e)):T;const E={class:"card conf_card rounded-3 shadow text-decoration-none"},H={class:"mb-0"},Y={class:"card-title mb-0 d-flex align-items-center gap-2"},A={class:"card-footer d-flex gap-2 flex-column"},Q={class:"row"},X={class:"d-flex gap-2 align-items-center"},Z={class:"text-muted"},tt={class:"mb-0 d-block d-lg-inline-block"},et={style:{"line-break":"anywhere"}},st={class:"form-check form-switch ms-auto"},ot=["for"],it={key:4,class:"spinner-border spinner-border-sm ms-2","aria-hidden":"true"},nt=["disabled","id"];function at(s,e,o,f,a,g){const c=p("ProtocolBadge"),l=p("RouterLink"),d=p("LocaleText");return i(),n("div",{class:m(["col-12",{"col-lg-6 col-xl-4":this.display==="Grid"}])},[t("div",E,[r(l,{to:"/configuration/"+o.c.Name+"/peers",class:"card-body d-flex align-items-center gap-3 flex-wrap text-decoration-none"},{default:S(()=>[t("h6",H,[t("span",{class:m(["dot",{active:o.c.Status}])},null,2)]),t("h6",Y,[t("samp",null,u(o.c.Name),1),t("small",null,[r(c,{protocol:o.c.Protocol,mini:!0},null,8,["protocol"])])]),e[2]||(e[2]=t("h6",{class:"mb-0 ms-auto"},[t("i",{class:"bi bi-chevron-right"})],-1))]),_:1},8,["to"]),t("div",A,[t("div",Q,[t("small",{class:m(["col-6",{"col-md-3":this.display==="List"}])},[e[3]||(e[3]=t("i",{class:"bi bi-arrow-down-up me-2"},null,-1)),v(u(o.c.DataUsage.Total>0?o.c.DataUsage.Total.toFixed(4):0)+" GB ",1)],2),t("small",{class:m(["text-primary-emphasis col-6",{"col-md-3":this.display==="List"}])},[e[4]||(e[4]=t("i",{class:"bi bi-arrow-down me-2"},null,-1)),v(u(o.c.DataUsage.Receive>0?o.c.DataUsage.Receive.toFixed(4):0)+" GB ",1)],2),t("small",{class:m(["text-success-emphasis col-6",{"col-md-3":this.display==="List"}])},[e[5]||(e[5]=t("i",{class:"bi bi-arrow-up me-2"},null,-1)),v(u(o.c.DataUsage.Sent>0?o.c.DataUsage.Sent.toFixed(4):0)+" GB ",1)],2),t("small",{class:m(["col-6",{"col-md-3 text-md-end ":this.display==="List"}])},[t("span",{class:m(["dot me-2",{active:o.c.ConnectedPeers>0}])},null,2),v(" "+u(o.c.ConnectedPeers)+" / "+u(o.c.TotalPeers)+" ",1),r(d,{t:"Peers"})],2)]),t("div",{class:m(["d-flex gap-2",[this.display==="Grid"?"flex-column":"gap-lg-3 flex-column flex-lg-row"]])},[t("div",X,[t("small",Z,[t("strong",null,[r(d,{t:"Public Key"})])]),t("small",tt,[t("samp",et,u(o.c.PublicKey),1)])]),t("div",st,[t("label",{class:"form-check-label",style:{cursor:"pointer"},for:"switch"+o.c.PrivateKey},[!o.c.Status&&this.configurationToggling?(i(),_(d,{key:0,t:"Turning Off..."})):o.c.Status&&this.configurationToggling?(i(),_(d,{key:1,t:"Turning On..."})):o.c.Status&&!this.configurationToggling?(i(),_(d,{key:2,t:"On"})):!o.c.Status&&!this.configurationToggling?(i(),_(d,{key:3,t:"Off"})):y("",!0),this.configurationToggling?(i(),n("span",it)):y("",!0)],8,ot),U(t("input",{class:"form-check-input",style:{cursor:"pointer"},disabled:this.configurationToggling,type:"checkbox",role:"switch",id:"switch"+o.c.PrivateKey,onChange:e[0]||(e[0]=C=>this.toggle()),"onUpdate:modelValue":e[1]||(e[1]=C=>o.c.Status=C)},null,40,nt),[[V,o.c.Status]])])],2)])])],2)}const rt=D(N,[["render",at],["__scopeId","data-v-6451623a"]]),lt={class:"text-muted me-2"},ct={class:"fw-bold"},dt={__name:"storageMount",props:{mount:Object,align:Boolean,square:Boolean},setup(s){B(a=>({"38705f32":f.value}));const e=s,o=O(!1),f=K(()=>e.square?"40px":"25px");return(a,g)=>(i(),n("div",{class:"flex-grow-1 square rounded-3 border position-relative",onMouseenter:g[0]||(g[0]=c=>o.value=!0),onMouseleave:g[1]||(g[1]=c=>o.value=!1),style:L({"background-color":`rgb(25 135 84 / ${s.mount.percent}%)`})},[r(G,{name:"zoomReversed"},{default:S(()=>[o.value?(i(),n("div",{key:0,style:{"white-space":"nowrap"},class:m(["floatingLabel z-3 border position-absolute d-block p-1 px-2 bg-body text-body rounded-3 border shadow d-flex",[s.align?"end-0":"start-0"]])},[t("small",lt,[t("samp",null,u(s.mount.mountPoint),1)]),t("small",ct,u(s.mount.percent)+"% ",1)],2)):y("",!0)]),_:1})],36))}},ut=D(dt,[["__scopeId","data-v-a382214a"]]),mt={class:"row text-body g-3 mb-5"},gt={class:"col-md-6 col-sm-12 col-xl-3"},ht={class:"d-flex align-items-center"},ft={class:"text-muted"},_t={class:"ms-auto"},pt={key:0},yt={key:1,class:"spinner-border spinner-border-sm"},bt={class:"progress",role:"progressbar",style:{height:"6px"}},St={class:"d-flex mt-2 gap-1"},vt={class:"col-md-6 col-sm-12 col-xl-3"},xt={class:"d-flex align-items-center"},wt={class:"text-muted"},kt={class:"ms-auto"},Ct={key:0},Lt={key:1,class:"spinner-border spinner-border-sm"},Dt={class:"progress",role:"progressbar",style:{height:"6px"}},$t={class:"d-flex mt-2 gap-1"},Nt={class:"col-md-6 col-sm-12 col-xl-3"},Tt={class:"d-flex align-items-center"},Pt={class:"text-muted"},Mt={class:"ms-auto"},It={key:0},Ut={key:1,class:"spinner-border spinner-border-sm"},Bt={class:"progress",role:"progressbar",style:{height:"6px"}},Kt={class:"col-md-6 col-sm-12 col-xl-3"},Gt={class:"d-flex align-items-center"},Vt={class:"text-muted"},Ot={class:"ms-auto"},Rt={key:0},Wt={key:1,class:"spinner-border spinner-border-sm"},qt={__name:"systemStatusWidget",setup(s){const e=M();let o=null;R(()=>{f(),o=setInterval(()=>{f()},5e3)}),W(()=>{clearInterval(o)});const f=()=>{I("/api/systemStatus",{},g=>{e.SystemStatus=g.data})},a=K(()=>e.SystemStatus);return(g,c)=>(i(),n("div",mt,[t("div",gt,[t("div",ht,[t("h6",ft,[c[0]||(c[0]=t("i",{class:"bi bi-cpu-fill me-2"},null,-1)),r(x,{t:"CPU"})]),t("h6",_t,[a.value?(i(),n("span",pt,u(a.value.CPU.cpu_percent)+"% ",1)):(i(),n("span",yt))])]),t("div",bt,[t("div",{class:"progress-bar",style:L({width:`${a.value?.CPU.cpu_percent}%`})},null,4)]),t("div",St,[(i(!0),n(w,null,k(a.value?.CPU.cpu_percent_per_cpu,(l,d)=>(i(),_(J,{key:d,align:d+1>Math.round(a.value?.CPU.cpu_percent_per_cpu.length/2),core_number:d,percentage:l},null,8,["align","core_number","percentage"]))),128))])]),t("div",vt,[t("div",xt,[t("h6",wt,[c[1]||(c[1]=t("i",{class:"bi bi-device-ssd-fill me-2"},null,-1)),r(x,{t:"Storage"})]),t("h6",kt,[a.value?(i(),n("span",Ct,u(a.value?.Disks.find(l=>l.mountPoint==="/").percent)+"% ",1)):(i(),n("span",Lt))])]),t("div",Dt,[t("div",{class:"progress-bar bg-success",style:L({width:`${a.value?.Disks.find(l=>l.mountPoint==="/").percent}%`})},null,4)]),t("div",$t,[a.value?(i(!0),n(w,{key:0},k(a.value?.Disks,(l,d)=>(i(),_(ut,{key:l.mountPoint,align:d+1>Math.round(a.value?.Disks.length/2),mount:l},null,8,["align","mount"]))),128)):y("",!0)])]),t("div",Nt,[t("div",Tt,[t("h6",Pt,[c[2]||(c[2]=t("i",{class:"bi bi-memory me-2"},null,-1)),r(x,{t:"Memory"})]),t("h6",Mt,[a.value?(i(),n("span",It,u(a.value?.Memory.VirtualMemory.percent)+"% ",1)):(i(),n("span",Ut))])]),t("div",Bt,[t("div",{class:"progress-bar bg-info",style:L({width:`${a.value?.Memory.VirtualMemory.percent}%`})},null,4)])]),t("div",Kt,[t("div",Gt,[t("h6",Vt,[c[3]||(c[3]=t("i",{class:"bi bi-memory me-2"},null,-1)),r(x,{t:"Swap Memory"})]),t("h6",Ot,[a.value?(i(),n("span",Rt,u(a.value?.Memory.SwapMemory.percent)+"% ",1)):(i(),n("span",Wt))])]),c[4]||(c[4]=t("div",{class:"progress",role:"progressbar",style:{height:"6px"}},[t("div",{class:"progress-bar bg-warning",style:{width:"$ data?.Memory.SwapMemory.percent}%"}})],-1))])]))}},Ft=D(qt,[["__scopeId","data-v-c7f6d1a1"]]),zt={name:"configurationList",components:{SystemStatus:Ft,LocaleText:x,ConfigurationCard:rt},async setup(){return{wireguardConfigurationsStore:q()}},data(){return{configurationLoaded:!1,sort:{Name:$("Name"),Status:$("Status"),"DataUsage.Total":$("Total Usage")},currentSort:{key:"Name",order:"asc"},currentDisplay:"List",searchKey:""}},async mounted(){window.localStorage.getItem("ConfigurationListSort")?this.currentSort=JSON.parse(window.localStorage.getItem("ConfigurationListSort")):window.localStorage.setItem("ConfigurationListSort",JSON.stringify(this.currentSort)),window.localStorage.getItem("ConfigurationListDisplay")?this.currentDisplay=window.localStorage.getItem("ConfigurationListDisplay"):window.localStorage.setItem("ConfigurationListDisplay",this.currentDisplay),await this.wireguardConfigurationsStore.getConfigurations(),this.configurationLoaded=!0,this.wireguardConfigurationsStore.ConfigurationListInterval=setInterval(()=>{this.wireguardConfigurationsStore.getConfigurations()},1e4)},beforeUnmount(){clearInterval(this.wireguardConfigurationsStore.ConfigurationListInterval)},computed:{configurations(){return[...this.wireguardConfigurationsStore.Configurations].filter(s=>s.Name.toLowerCase().includes(this.searchKey)||s.PublicKey.includes(this.searchKey)||!this.searchKey).sort((s,e)=>this.currentSort.order==="desc"?this.dotNotation(s,this.currentSort.key)this.dotNotation(e,this.currentSort.key)?-1:0:this.dotNotation(s,this.currentSort.key)>this.dotNotation(e,this.currentSort.key)?1:this.dotNotation(s,this.currentSort.key)f&&f[a],s);return typeof o=="string"?o.toLowerCase():o},updateSort(s){this.currentSort.key===s?this.currentSort.order==="asc"?this.currentSort.order="desc":this.currentSort.order="asc":this.currentSort.key=s,window.localStorage.setItem("ConfigurationListSort",JSON.stringify(this.currentSort))},updateDisplay(s){this.currentDisplay!==s&&(this.currentDisplay=s,window.localStorage.setItem("ConfigurationListDisplay",this.currentDisplay))}}},jt={class:"mt-md-5 mt-3"},Jt={class:"container-fluid"},Et={class:"d-flex mb-4 configurationListTitle align-items-md-center gap-2 flex-column flex-md-row"},Ht={class:"text-body d-flex mb-0"},Yt={key:0,class:"text-body filter mb-3 d-flex gap-2 flex-column flex-md-row"},At={class:"d-flex align-items-center gap-3 align-items-center mb-3 mb-md-0"},Qt={class:"text-muted"},Xt={class:"d-flex ms-auto ms-lg-0"},Zt=["onClick"],te={class:"align-items-center gap-3 align-items-center mb-3 mb-md-0 d-none d-lg-flex"},ee={class:"text-muted"},se={class:"d-flex ms-auto ms-lg-0"},oe=["onClick"],ie={class:"d-flex align-items-center ms-md-auto"},ne={class:"row g-3 mb-2"},ae={class:"text-muted col-12",key:"noConfiguration"};function re(s,e,o,f,a,g){const c=p("SystemStatus"),l=p("LocaleText"),d=p("RouterLink"),C=p("ConfigurationCard");return i(),n("div",jt,[t("div",Jt,[r(c),t("div",Et,[t("h2",Ht,[r(l,{t:"WireGuard Configurations"})]),r(d,{to:"/new_configuration",class:"ms-md-auto py-2 text-decoration-none btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle"},{default:S(()=>[e[1]||(e[1]=t("i",{class:"bi bi-plus-circle me-2"},null,-1)),r(l,{t:"Configuration"})]),_:1}),r(d,{to:"/restore_configuration",class:"py-2 text-decoration-none btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle"},{default:S(()=>[e[2]||(e[2]=t("i",{class:"bi bi-clock-history me-2"},null,-1)),r(l,{t:"Restore"})]),_:1})]),r(G,{name:"fade"},{default:S(()=>[this.configurationLoaded?(i(),n("div",Yt,[t("div",At,[t("small",Qt,[r(l,{t:"Sort By"})]),t("div",Xt,[(i(!0),n(w,null,k(this.sort,(h,b)=>(i(),n("a",{role:"button",onClick:le=>g.updateSort(b),class:m([{"bg-primary-subtle text-primary-emphasis":this.currentSort.key===b},"px-2 py-1 rounded-3"])},[t("small",null,[this.currentSort.key===b?(i(),n("i",{key:0,class:m(["bi me-2",[this.currentSort.order==="asc"?"bi-sort-up":"bi-sort-down"]])},null,2)):y("",!0),v(u(h),1)])],10,Zt))),256))])]),t("div",te,[t("small",ee,[r(l,{t:"Display as"})]),t("div",se,[(i(),n(w,null,k([{name:"List",key:"list"},{name:"Grid",key:"grid"}],h=>t("a",{role:"button",onClick:b=>g.updateDisplay(h.name),class:m([{"bg-primary-subtle text-primary-emphasis":this.currentDisplay===h.name},"px-2 py-1 rounded-3"])},[t("small",null,[t("i",{class:m(["bi me-2","bi-"+h.key])},null,2),e[3]||(e[3]=v()),r(l,{t:h.name},null,8,["t"])])],10,oe)),64))])]),t("div",ie,[e[4]||(e[4]=t("label",{for:"configurationSearch",class:"text-muted"},[t("i",{class:"bi bi-search me-2"})],-1)),U(t("input",{class:"form-control form-control-sm rounded-3","onUpdate:modelValue":e[0]||(e[0]=h=>this.searchKey=h),id:"configurationSearch"},null,512),[[z,this.searchKey]])])])):y("",!0)]),_:1}),t("div",ne,[r(F,{name:"fade"},{default:S(()=>[this.configurationLoaded&&this.wireguardConfigurationsStore.Configurations.length===0?(i(),n("p",ae,[r(l,{t:"You don't have any WireGuard configurations yet. Please check the configuration folder or change it in Settings. By default the folder is /etc/wireguard."})])):this.configurationLoaded?(i(!0),n(w,{key:1},k(g.configurations,(h,b)=>(i(),_(C,{display:this.currentDisplay,delay:b*.03+"s",key:h.Name,c:h},null,8,["display","delay","c"]))),128)):y("",!0)]),_:1})])])])}const ge=D(zt,[["render",re],["__scopeId","data-v-ea61b607"]]);export{ge as default};
diff --git a/src/static/app/dist/assets/dayjs.min-C3XPfvH9.js b/src/static/app/dist/assets/dayjs.min-C3XPfvH9.js
new file mode 100644
index 0000000..f83e555
--- /dev/null
+++ b/src/static/app/dist/assets/dayjs.min-C3XPfvH9.js
@@ -0,0 +1 @@
+import{a0 as Q,a1 as K}from"./index-CUmHRwBw.js";var E={exports:{}};(function(V,X){(function(W,k){V.exports=k()})(Q,function(){var W=1e3,k=6e4,N=36e5,A="millisecond",S="second",w="minute",O="hour",M="day",T="week",m="month",U="quarter",v="year",_="date",J="Invalid Date",q=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,B=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,G={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(s){var n=["th","st","nd","rd"],t=s%100;return"["+s+(n[(t-20)%10]||n[t]||n[0])+"]"}},I=function(s,n,t){var r=String(s);return!r||r.length>=n?s:""+Array(n+1-r.length).join(t)+s},P={s:I,z:function(s){var n=-s.utcOffset(),t=Math.abs(n),r=Math.floor(t/60),e=t%60;return(n<=0?"+":"-")+I(r,2,"0")+":"+I(e,2,"0")},m:function s(n,t){if(n.date()1)return s(u[0])}else{var o=n.name;D[o]=n,e=o}return!r&&e&&(x=e),e||!r&&x},f=function(s,n){if(F(s))return s.clone();var t=typeof n=="object"?n:{};return t.date=s,t.args=arguments,new C(t)},a=P;a.l=j,a.i=F,a.w=function(s,n){return f(s,{locale:n.$L,utc:n.$u,x:n.$x,$offset:n.$offset})};var C=function(){function s(t){this.$L=j(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[Z]=!0}var n=s.prototype;return n.parse=function(t){this.$d=function(r){var e=r.date,i=r.utc;if(e===null)return new Date(NaN);if(a.u(e))return new Date;if(e instanceof Date)return new Date(e);if(typeof e=="string"&&!/Z$/i.test(e)){var u=e.match(q);if(u){var o=u[2]-1||0,c=(u[7]||"0").substring(0,3);return i?new Date(Date.UTC(u[1],o,u[3]||1,u[4]||0,u[5]||0,u[6]||0,c)):new Date(u[1],o,u[3]||1,u[4]||0,u[5]||0,u[6]||0,c)}}return new Date(e)}(t),this.init()},n.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},n.$utils=function(){return a},n.isValid=function(){return this.$d.toString()!==J},n.isSame=function(t,r){var e=f(t);return this.startOf(r)<=e&&e<=this.endOf(r)},n.isAfter=function(t,r){return f(t).title{opacity:.5!important}.code-editor .dropdown:hover>.title{opacity:1}.code-editor .dropdown>.title{transition:opacity .2s ease;opacity:.5;display:flex;align-items:center;-webkit-user-select:none;user-select:none}.code-editor .dropdown>.title>div{white-space:nowrap;font-size:12px;line-height:16px}.code-editor .dropdown>.title>svg{transition:.2s;margin-left:3px;margin-top:1px}[theme=github-dark] pre code.hljs{display:block;overflow-x:auto;padding:1em}[theme=github-dark] code.hljs{padding:3px 5px}[theme=github-dark] .hljs{color:#c9d1d9;background:#0d1117}[theme=github-dark] .hljs-doctag,[theme=github-dark] .hljs-keyword,[theme=github-dark] .hljs-meta .hljs-keyword,[theme=github-dark] .hljs-template-tag,[theme=github-dark] .hljs-template-variable,[theme=github-dark] .hljs-type,[theme=github-dark] .hljs-variable.language_{color:#ff7b72}[theme=github-dark] .hljs-title,[theme=github-dark] .hljs-title.class_,[theme=github-dark] .hljs-title.class_.inherited__,[theme=github-dark] .hljs-title.function_{color:#d2a8ff}[theme=github-dark] .hljs-attr,[theme=github-dark] .hljs-attribute,[theme=github-dark] .hljs-literal,[theme=github-dark] .hljs-meta,[theme=github-dark] .hljs-number,[theme=github-dark] .hljs-operator,[theme=github-dark] .hljs-selector-attr,[theme=github-dark] .hljs-selector-class,[theme=github-dark] .hljs-selector-id,[theme=github-dark] .hljs-variable{color:#79c0ff}[theme=github-dark] .hljs-meta .hljs-string,[theme=github-dark] .hljs-regexp,[theme=github-dark] .hljs-string{color:#a5d6ff}[theme=github-dark] .hljs-built_in,[theme=github-dark] .hljs-symbol{color:#ffa657}[theme=github-dark] .hljs-code,[theme=github-dark] .hljs-comment,[theme=github-dark] .hljs-formula{color:#8b949e}[theme=github-dark] .hljs-name,[theme=github-dark] .hljs-quote,[theme=github-dark] .hljs-selector-pseudo,[theme=github-dark] .hljs-selector-tag{color:#7ee787}[theme=github-dark] .hljs-subst{color:#c9d1d9}[theme=github-dark] .hljs-section{color:#1f6feb;font-weight:700}[theme=github-dark] .hljs-bullet{color:#f2cc60}[theme=github-dark] .hljs-emphasis{color:#c9d1d9;font-style:italic}[theme=github-dark] .hljs-strong{color:#c9d1d9;font-weight:700}[theme=github-dark] .hljs-addition{color:#aff5b4;background-color:#033a16}[theme=github-dark] .hljs-deletion{color:#ffdcd7;background-color:#67060c}[theme=github] pre code.hljs{display:block;overflow-x:auto;padding:1em}[theme=github] code.hljs{padding:3px 5px}[theme=github] .hljs{color:#24292e;background:#efefef}[theme=github] .hljs-doctag,[theme=github] .hljs-keyword,[theme=github] .hljs-meta .hljs-keyword,[theme=github] .hljs-template-tag,[theme=github] .hljs-template-variable,[theme=github] .hljs-type,[theme=github] .hljs-variable.language_{color:#d73a49}[theme=github] .hljs-title,[theme=github] .hljs-title.class_,[theme=github] .hljs-title.class_.inherited__,[theme=github] .hljs-title.function_{color:#6f42c1}[theme=github] .hljs-attr,[theme=github] .hljs-attribute,[theme=github] .hljs-literal,[theme=github] .hljs-meta,[theme=github] .hljs-number,[theme=github] .hljs-operator,[theme=github] .hljs-selector-attr,[theme=github] .hljs-selector-class,[theme=github] .hljs-selector-id,[theme=github] .hljs-variable{color:#005cc5}[theme=github] .hljs-meta .hljs-string,[theme=github] .hljs-regexp,[theme=github] .hljs-string{color:#032f62}[theme=github] .hljs-built_in,[theme=github] .hljs-symbol{color:#e36209}[theme=github] .hljs-code,[theme=github] .hljs-comment,[theme=github] .hljs-formula{color:#6a737d}[theme=github] .hljs-name,[theme=github] .hljs-quote,[theme=github] .hljs-selector-pseudo,[theme=github] .hljs-selector-tag{color:#22863a}[theme=github] .hljs-subst{color:#24292e}[theme=github] .hljs-section{color:#005cc5;font-weight:700}[theme=github] .hljs-bullet{color:#735c0f}[theme=github] .hljs-emphasis{color:#24292e;font-style:italic}[theme=github] .hljs-strong{color:#24292e;font-weight:700}[theme=github] .hljs-addition{color:#22863a;background-color:#f0fff4}[theme=github] .hljs-deletion{color:#b31d28;background-color:#ffeef0}.code-editor{position:relative}.code-editor>div{width:100%;height:100%}.code-editor .header{box-sizing:border-box;position:relative;z-index:1;height:34px}.code-editor .header>.dropdown{position:absolute;top:12px;left:18px}.code-editor .header>.copy-code{position:absolute;top:10px;right:12px}.code-editor .code-area{position:relative;z-index:0;text-align:left;overflow:hidden}.code-editor .code-area>textarea,.code-editor .code-area>pre>code,.code-editor .line-nums>div{font-family:Consolas,Monaco,monospace;line-height:1.5}.code-editor .code-area>textarea:hover,.code-editor .code-area>textarea:focus-visible{outline:none}.code-editor .code-area>textarea{position:absolute;z-index:1;top:0;left:0;overflow-y:hidden;box-sizing:border-box;caret-color:#7f7f7f;color:transparent;white-space:pre;word-wrap:normal;border:0;width:100%;height:100%;background:none;resize:none;&[readOnly]~pre{filter:brightness(.7)}}.code-editor .code-area>pre{box-sizing:border-box;position:relative;z-index:0;overflow:hidden;font-size:0;margin:0}.code-editor .code-area>pre>code{background:none;display:block;position:relative;overflow-x:visible!important;border-radius:0;box-sizing:border-box;margin:0}.code-editor.wrap .code-area>textarea,.code-editor.wrap .code-area>pre>code{white-space:pre-wrap;word-wrap:break-word}.code-editor.hide-header.scroll .code-area{height:100%}.code-editor.scroll .code-area{height:calc(100% - 34px)}.code-editor.scroll .code-area>textarea{overflow:auto}.code-editor.scroll .code-area>pre{width:100%;height:100%;overflow:hidden}.code-editor .list{-webkit-user-select:none;user-select:none;height:100%;font-family:sans-serif}.code-editor .list>.lang-list{border-radius:5px;box-sizing:border-box;overflow:auto;font-size:13px;padding:0;margin:0;list-style:none;text-align:left}.code-editor .list>.lang-list>li{font-size:13px;transition:background .16s ease,color .16s ease;box-sizing:border-box;padding:0 12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:30px}.code-editor .list>.lang-list>li:first-child{padding-top:5px}.code-editor .list>.lang-list>li:last-child{padding-bottom:5px}.code-editor .list>.lang-list>li:hover{background:#a0a0a066}.code-editor .line-nums{min-width:36px;text-align:right;box-sizing:border-box;position:absolute;left:0;padding-right:8px;padding-left:8px;opacity:.3}.code-editor .line-nums:after{content:"";position:absolute;width:100%;height:100%;top:0;left:0;border-right:1px solid currentColor;opacity:.5}.code-editor .header.border:after{content:"";position:absolute;width:100%;height:1px;bottom:0;left:0;background:currentColor;opacity:.15}
diff --git a/src/static/app/dist/assets/editConfiguration-DQFnw2_V.js b/src/static/app/dist/assets/editConfiguration-DQFnw2_V.js
new file mode 100644
index 0000000..3787e33
--- /dev/null
+++ b/src/static/app/dist/assets/editConfiguration-DQFnw2_V.js
@@ -0,0 +1,7 @@
+import{J as O,r as x,I as E,D as z,o as j,a as c,c as v,b as e,d as i,n as $,m as B,y as P,u as K,i as N,A as W,g as M,_ as I,p as Q,q as X,w as T,e as S,j as Y,t as D,f as V,F as H,h as F,T as G,E as J,W as q,H as Z,k as ee,a3 as te,s as R,C as se,a4 as ne,O as ae,v as ie}from"./index-CUmHRwBw.js";import{L as l}from"./localeText-DvaqSAco.js";import{d as le}from"./dayjs.min-C3XPfvH9.js";const oe={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll"},re={class:"container d-flex h-100 w-100"},de={class:"m-auto modal-dialog-centered dashboardModal",style:{width:"700px"}},ue={class:"card rounded-3 shadow flex-grow-1 bg-danger-subtle border-danger-subtle",id:"deleteConfigurationContainer"},ce={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0"},fe={class:"mb-0"},ge={class:"card-body px-4 text-muted"},me={class:"mb-0"},be={key:0},ve={key:1},pe={key:2,class:"d-flex align-items-center gap-2"},he=["placeholder"],ye=["disabled"],xe={__name:"deleteConfiguration",emits:["backup","close"],setup(o,{emit:r}){const p=O().params.id,s=x(""),g=E(),u=z(),h=x(!1),f=()=>{clearInterval(u.Peers.RefreshInterval),h.value=!0,W("/api/deleteWireguardConfiguration",{ConfigurationName:p},_=>{_.status?(g.push("/"),u.newMessage("Server","Configuration deleted","success")):h.value=!1})},b=x(!0),y=x([]),w=()=>{b.value=!0,M("/api/getWireguardConfigurationBackup",{configurationName:p},_=>{y.value=_.data,b.value=!1})};j(()=>{w()});const C=r;return(_,d)=>(c(),v("div",oe,[e("div",re,[e("div",de,[e("div",ue,[e("div",ce,[e("h5",fe,[i(l,{t:"Are you sure to delete this configuration?"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:d[0]||(d[0]=n=>C("close"))})]),e("div",ge,[e("p",me,[i(l,{t:"Once you deleted this configuration:"})]),e("ul",null,[e("li",null,[i(l,{t:"All connected peers will get disconnected"})]),e("li",null,[i(l,{t:"Both configuration file (.conf) and database table related to this configuration will get deleted"})])]),e("div",{class:$(["alert",[b.value?"alert-secondary":y.value.length>0?"alert-success":"alert-danger"]])},[b.value?(c(),v("div",be,[d[5]||(d[5]=e("i",{class:"bi bi-search me-2"},null,-1)),i(l,{t:"Checking backups..."})])):y.value.length>0?(c(),v("div",ve,[d[6]||(d[6]=e("i",{class:"bi bi-check-circle-fill me-2"},null,-1)),i(l,{t:"This configuration have "+y.value.length+" backups"},null,8,["t"])])):(c(),v("div",pe,[d[9]||(d[9]=e("i",{class:"bi bi-x-circle-fill me-2"},null,-1)),i(l,{t:"This configuration have no backup"}),e("a",{role:"button",onClick:d[1]||(d[1]=n=>C("backup")),class:"ms-auto btn btn-sm btn-primary rounded-3"},[d[7]||(d[7]=e("i",{class:"bi bi-clock-history me-2"},null,-1)),i(l,{t:"Backup"})]),e("a",{role:"button",onClick:d[2]||(d[2]=n=>w()),class:"btn btn-sm btn-primary rounded-3"},d[8]||(d[8]=[e("i",{class:"bi bi-arrow-clockwise"},null,-1)]))]))],2),d[11]||(d[11]=e("hr",null,null,-1)),e("p",null,[i(l,{t:"If you're sure, please type in the configuration name below and click Delete"})]),B(e("input",{class:"form-control rounded-3 mb-3",placeholder:K(p),"onUpdate:modelValue":d[3]||(d[3]=n=>s.value=n),type:"text"},null,8,he),[[P,s.value]]),e("button",{class:"btn btn-danger w-100",onClick:d[4]||(d[4]=n=>f()),disabled:s.value!==K(p)||h.value},[d[10]||(d[10]=e("i",{class:"bi bi-trash-fill me-2 rounded-3"},null,-1)),h.value?(c(),N(l,{key:1,t:"Deleting..."})):(c(),N(l,{key:0,t:"Delete"}))],8,ye)])])])])]))}},we={class:"card my-0 rounded-3"},Ce={class:"card-body position-relative"},ke={key:0,class:"position-absolute w-100 h-100 confirmationContainer start-0 top-0 rounded-3 d-flex p-2"},_e={class:"m-auto"},Se={class:"d-flex gap-2 align-items-center justify-content-center"},Ne=["disabled"],$e=["disabled"],Be={key:0,class:"position-absolute w-100 h-100 confirmationContainer start-0 top-0 rounded-3 d-flex p-2"},Re={class:"m-auto"},Pe={class:"d-flex gap-2 align-items-center justify-content-center"},De=["disabled"],We=["disabled"],Le={class:"d-flex gap-3"},Ve={class:"d-flex flex-column"},He={class:"text-muted"},Me={class:"d-flex flex-column"},ze={class:"text-muted"},Ie={class:"d-flex gap-2 align-items-center ms-auto"},Ke={class:"card rounded-3"},Te={key:0,class:"card-body"},Fe=["value"],Oe={class:"d-flex"},Ue={__name:"backup",props:["b","delay"],emits:["refresh","refreshPeersList"],setup(o,{emit:r}){Q(d=>({b32c1fd8:C.value}));const t=o,p=x(!1),s=x(!1),g=O(),u=r,h=z(),f=x(!1),b=()=>{f.value=!0,W("/api/deleteWireguardConfigurationBackup",{ConfigurationName:g.params.id,BackupFileName:t.b.filename},d=>{f.value=!1,d.status?(u("refresh"),h.newMessage("Server","Backup deleted","success")):h.newMessage("Server","Backup failed to delete","danger")})},y=()=>{f.value=!0,W("/api/restoreWireguardConfigurationBackup",{ConfigurationName:g.params.id,BackupFileName:t.b.filename},d=>{f.value=!1,s.value=!1,d.status?(u("refreshPeersList"),h.newMessage("Server","Backup restored with "+t.b.filename,"success")):h.newMessage("Server","Backup failed to restore","danger")})},w=()=>{M("/api/downloadWireguardConfigurationBackup",{configurationName:g.params.id,backupFileName:t.b.filename},d=>{d.status&&window.open(`/fileDownload?file=${d.data}`,"_blank")})},C=X(()=>t.delay+"s"),_=x(!1);return(d,n)=>(c(),v("div",we,[e("div",Ce,[i(Y,{name:"zoomReversed"},{default:T(()=>[p.value?(c(),v("div",ke,[e("div",_e,[e("h5",null,[i(l,{t:"Are you sure to delete this backup?"})]),e("div",Se,[e("button",{class:"btn btn-danger rounded-3",disabled:f.value,onClick:n[0]||(n[0]=k=>b())},[i(l,{t:"Yes"})],8,Ne),e("button",{onClick:n[1]||(n[1]=k=>p.value=!1),disabled:f.value,class:"btn bg-secondary-subtle text-secondary-emphasis border-secondary-subtle rounded-3"},[i(l,{t:"No"})],8,$e)])])])):S("",!0)]),_:1}),i(Y,{name:"zoomReversed"},{default:T(()=>[s.value?(c(),v("div",Be,[e("div",Re,[e("h5",null,[i(l,{t:"Are you sure to restore this backup?"})]),e("div",Pe,[e("button",{disabled:f.value,onClick:n[2]||(n[2]=k=>y()),class:"btn btn-success rounded-3"},[i(l,{t:"Yes"})],8,De),e("button",{onClick:n[3]||(n[3]=k=>s.value=!1),disabled:f.value,class:"btn bg-secondary-subtle text-secondary-emphasis border-secondary-subtle rounded-3"},[i(l,{t:"No"})],8,We)])])])):S("",!0)]),_:1}),e("div",Le,[e("div",Ve,[e("small",He,[i(l,{t:"Backup"})]),e("samp",null,D(o.b.filename),1)]),e("div",Me,[e("small",ze,[i(l,{t:"Backup Date"})]),V(" "+D(K(le)(o.b.backupDate,"YYYYMMDDHHmmss").format("YYYY-MM-DD HH:mm:ss")),1)]),e("div",Ie,[e("button",{onClick:n[4]||(n[4]=k=>w()),class:"btn bg-primary-subtle text-primary-emphasis border-primary-subtle rounded-3 btn-sm"},n[8]||(n[8]=[e("i",{class:"bi bi-download"},null,-1)])),e("button",{onClick:n[5]||(n[5]=k=>s.value=!0),class:"btn bg-warning-subtle text-warning-emphasis border-warning-subtle rounded-3 btn-sm"},n[9]||(n[9]=[e("i",{class:"bi bi-clock-history"},null,-1)])),e("button",{onClick:n[6]||(n[6]=k=>p.value=!0),class:"btn bg-danger-subtle text-danger-emphasis border-danger-subtle rounded-3 btn-sm"},n[10]||(n[10]=[e("i",{class:"bi bi-trash-fill"},null,-1)]))])]),n[14]||(n[14]=e("hr",null,null,-1)),e("div",Ke,[e("a",{role:"button",class:$(["card-header d-flex text-decoration-none align-items-center",{"border-bottom-0":!_.value}]),style:{cursor:"pointer"},onClick:n[7]||(n[7]=k=>_.value=!_.value)},[e("small",null,[n[11]||(n[11]=V(".conf ")),i(l,{t:"File"})]),n[12]||(n[12]=e("i",{class:"bi bi-chevron-down ms-auto"},null,-1))],2),_.value?(c(),v("div",Te,[e("textarea",{class:"form-control rounded-3",value:o.b.content,disabled:"",style:{height:"300px","font-family":"var(--bs-font-monospace),sans-serif !important"}},null,8,Fe)])):S("",!0)]),n[15]||(n[15]=e("hr",null,null,-1)),e("div",Oe,[e("span",null,[n[13]||(n[13]=e("i",{class:"bi bi-database me-1"},null,-1)),i(l,{t:"Database File"})]),e("i",{class:$(["bi ms-auto",[o.b.database?"text-success bi-check-circle-fill":"text-danger bi-x-circle-fill"]])},null,2)])])]))}},Ae=I(Ue,[["__scopeId","data-v-9f0c0156"]]),Je={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll",ref:"editConfigurationContainer"},je={class:"d-flex h-100 w-100"},Ye={class:"modal-dialog-centered dashboardModal w-100 h-100 overflow-x-scroll flex-column gap-3 mx-3"},Ee={class:"my-5 d-flex gap-3 flex-column position-relative"},Ge={class:"title"},qe={class:"d-flex mb-3"},Ze={class:"mb-0"},Qe={class:"position-relative d-flex flex-column gap-3"},Xe={class:"text-center title",key:"spinner"},et={class:"card my-0 rounded-3",key:"noBackups"},tt={class:"card-body text-center text-muted"},st={__name:"configurationBackupRestore",emits:["close","refreshPeersList"],setup(o,{emit:r}){const t=O(),p=x([]),s=x(!0),g=r;j(()=>{u()});const u=()=>{s.value=!0,M("/api/getWireguardConfigurationBackup",{configurationName:t.params.id},f=>{p.value=f.data,s.value=!1})},h=()=>{M("/api/createWireguardConfigurationBackup",{configurationName:t.params.id},f=>{p.value=f.data,s.value=!1})};return(f,b)=>(c(),v("div",Je,[e("div",je,[e("div",Ye,[e("div",Ee,[e("div",Ge,[e("div",qe,[e("h4",Ze,[i(l,{t:"Backup & Restore"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:b[0]||(b[0]=y=>f.$emit("close"))})]),e("button",{onClick:b[1]||(b[1]=y=>h()),class:"btn bg-primary-subtle text-primary-emphasis border-primary-subtle rounded-3 w-100"},[b[4]||(b[4]=e("i",{class:"bi bi-plus-circle-fill me-2"},null,-1)),i(l,{t:"Create Backup"})])]),e("div",Qe,[i(G,{name:"list1"},{default:T(()=>[s.value&&p.value.length===0?(c(),v("div",Xe,b[5]||(b[5]=[e("div",{class:"spinner-border"},null,-1)]))):!s.value&&p.value.length===0?(c(),v("div",et,[e("div",tt,[b[6]||(b[6]=e("i",{class:"bi bi-x-circle-fill me-2"},null,-1)),i(l,{t:"No backup yet, click the button above to create backup."})])])):S("",!0),(c(!0),v(H,null,F(p.value,y=>(c(),N(Ae,{onRefresh:b[2]||(b[2]=w=>u()),onRefreshPeersList:b[3]||(b[3]=w=>g("refreshPeersList")),b:y,key:y.filename},null,8,["b"]))),128))]),_:1})])])])])],512))}},nt=I(st,[["__scopeId","data-v-1f718118"]]),at={class:"card rounded-3 flex-grow-1 bg-danger-subtle border-danger-subtle border shadow"},it={class:"card-body"},lt={class:"d-flex align-items-center gap-3 inputGroup"},ot=["value"],rt={class:"mb-0"},dt={class:"d-flex mt-3"},ut=["disabled"],ct={__name:"updateConfigurationName",props:{configurationName:String},emits:["close"],setup(o,{emit:r}){const t=o,p=r,s=J({data:"",valid:!1}),g=q();j(()=>{Z(()=>s.data,y=>{s.valid=/^[a-zA-Z0-9_=+.-]{1,15}$/.test(y)&&y.length>0&&!g.Configurations.find(w=>w.Name===y)})});const u=z(),h=x(!1),f=E(),b=async()=>{s.data&&(h.value=!0,clearInterval(u.Peers.RefreshInterval),await W("/api/renameWireguardConfiguration",{ConfigurationName:t.configurationName,NewConfigurationName:s.data},async y=>{y.status?(await g.getConfigurations(),u.newMessage("Server","Configuration renamed","success"),f.push(`/configuration/${s.data}/peers`)):(u.newMessage("Server",y.message,"danger"),h.value=!1)}))};return(y,w)=>(c(),v("div",at,[e("div",it,[e("p",null,[i(l,{t:"To update this configuration's name, WGDashboard will execute the following operations:"})]),e("ol",null,[e("li",null,[i(l,{t:"Duplicate current configuration's database table and .conf file with the new name"})]),e("li",null,[i(l,{t:"Delete current configuration's database table and .conf file"})])]),e("div",lt,[e("input",{class:"form-control form-control-sm rounded-3",value:o.configurationName,disabled:""},null,8,ot),w[3]||(w[3]=e("h3",{class:"mb-0"},[e("i",{class:"bi bi-arrow-right"})],-1)),B(e("input",{class:$(["form-control form-control-sm rounded-3",[s.data?s.valid?"is-valid":"is-invalid":""]]),id:"newConfigurationName","onUpdate:modelValue":w[0]||(w[0]=C=>s.data=C)},null,2),[[P,s.data]])]),e("div",{class:$(["invalid-feedback",{"d-block":!s.valid&&s.data}])},[i(l,{t:"Configuration name is invalid. Possible reasons:"}),e("ul",rt,[e("li",null,[i(l,{t:"Configuration name already exist"})]),e("li",null,[i(l,{t:"Configuration name can only contain 15 lower/uppercase alphabet, numbers, underscore, equal sign, plus sign, period and hyphen."})])])],2),e("div",dt,[e("button",{onClick:w[1]||(w[1]=C=>p("close")),class:"btn btn-sm bg-secondary-subtle border-secondary-subtle text-secondary-emphasis rounded-3"},[i(l,{t:"Cancel"})]),e("button",{onClick:w[2]||(w[2]=C=>b()),disabled:!s.data||h.value,class:"btn btn-sm btn-danger rounded-3 ms-auto"},[i(l,{t:"Save"})],8,ut)])])]))}},ft=I(ct,[["__scopeId","data-v-33ea9576"]]),gt={name:"Dropdown",props:{width:{type:String,default:"80px"},height:{type:String,default:"auto"},title:{type:String,default:""},disabled:{type:Boolean,default:!1},defaultDisplay:{type:Boolean,default:!1}}},mt={class:"title"};function bt(o,r,t,p,s,g){return c(),v("div",{class:$(["dropdown",{disabled:t.disabled}]),onClick:r[0]||(r[0]=(...u)=>o.toggleDropdown&&o.toggleDropdown(...u)),onFocusout:r[1]||(r[1]=(...u)=>o.hideDropdown&&o.hideDropdown(...u)),tabindex:"0"},[e("div",mt,[e("div",null,D(t.title),1)])],34)}const vt=I(gt,[["render",bt]]),pt={components:{Dropdown:vt},name:"CodeEditor",props:{lineNums:{type:Boolean,default:!1},modelValue:{type:String},value:{type:String},theme:{type:String,default:"github-dark"},tabSpaces:{type:Number,default:2},wrap:{type:Boolean,default:!1},readOnly:{type:Boolean,default:!1},autofocus:{type:Boolean,default:!1},header:{type:Boolean,default:!0},width:{type:String,default:"540px"},height:{type:String,default:"auto"},maxWidth:{type:String},minWidth:{type:String},maxHeight:{type:String},minHeight:{type:String},borderRadius:{type:String,default:"12px"},languages:{type:Array,default:function(){return[["javascript","JS"]]}},langListWidth:{type:String,default:"110px"},langListHeight:{type:String,default:"auto"},langListDisplay:{type:Boolean,default:!1},displayLanguage:{type:Boolean,default:!0},zIndex:{type:String,default:"0"},fontSize:{type:String,default:"17px"},padding:{type:String,default:"20px"}},directives:{highlight:{mounted(o,r){o.textContent=r.value},updated(o,r){o.scrolling?o.scrolling=!1:o.textContent=r.value}}},data(){return{scrollBarWidth:0,scrollBarHeight:0,top:0,left:0,languageClass:"hljs language-"+this.languages[0][0],languageTitle:this.languages[0][1]?this.languages[0][1]:this.languages[0][0],content:this.value,cursorPosition:0,insertTab:!1,lineNum:0,lineNumsWidth:0,scrolling:!1,textareaHeight:0,showLineNums:this.wrap?!1:this.lineNums}},computed:{tabWidth(){let o="";for(let r=0;r{this.scrollBarWidth=t[0].target.offsetWidth-t[0].target.clientWidth,this.scrollBarHeight=t[0].target.offsetHeight-t[0].target.clientHeight,this.textareaHeight=t[0].target.offsetHeight}).observe(this.$refs.textarea);const r=new ResizeObserver(t=>{this.lineNumsWidth=t[0].target.offsetWidth});this.$refs.lineNums&&r.observe(this.$refs.lineNums)},copy(){document.execCommand("copy")?(this.$refs.textarea.select(),document.execCommand("copy"),window.getSelection().removeAllRanges()):navigator.clipboard.writeText(this.$refs.textarea.value)},getLineNum(){const o=this.$refs.textarea.value;let r=0,t=o.indexOf(`
+`);for(;t!==-1;)r++,t=o.indexOf(`
+`,t+1);const p=this.$refs.lineNums.firstChild.offsetHeight,s=parseInt(this.textareaHeight/p)-1;this.lineNum=this.height=="auto"||r>s?r:s}},mounted(){this.$emit("lang",this.languages[0][0]),this.$emit("content",this.content),this.$emit("textarea",this.$refs.textarea),this.resizer()},updated(){this.insertTab&&(this.$refs.textarea.setSelectionRange(this.cursorPosition,this.cursorPosition),this.insertTab=!1),this.lineNums&&(this.scrolling?this.scrolling=!1:this.getLineNum())}},ht=["theme"],yt=["readOnly","autofocus","value"];function xt(o,r,t,p,s,g){const u=ee("Dropdown"),h=te("highlight");return c(),v("div",{theme:t.theme,class:$(["code-editor",{"hide-header":!t.header,scroll:g.scroll,"read-only":t.readOnly,wrap:t.wrap}]),style:R({width:t.width,height:t.height,zIndex:t.zIndex,maxWidth:t.maxWidth,minWidth:t.minWidth,maxHeight:t.maxHeight,minHeight:t.minHeight})},[e("div",{class:"hljs",style:R({borderRadius:t.borderRadius})},[t.header?(c(),v("div",{key:0,class:$(["header",{border:s.showLineNums}]),style:R({borderRadius:t.borderRadius+" "+t.borderRadius+" 0 0"})},[t.displayLanguage?(c(),N(u,{key:0,width:t.langListWidth,title:s.languageTitle,disabled:t.languages.length<=1,defaultDisplay:t.langListDisplay},null,8,["width","title","disabled","defaultDisplay"])):S("",!0)],6)):S("",!0),e("div",{class:"code-area",style:R({borderRadius:t.header?"0 0 "+t.borderRadius+" "+t.borderRadius:t.borderRadius})},[s.showLineNums?(c(),v("div",{key:0,ref:"lineNums",class:"line-nums hljs",style:R({fontSize:t.fontSize,paddingTop:t.header?"10px":t.padding,paddingBottom:t.padding,top:s.top+"px"})},[r[3]||(r[3]=e("div",null,"1",-1)),(c(!0),v(H,null,F(s.lineNum,f=>(c(),v("div",null,D(f+1),1))),256)),r[4]||(r[4]=e("div",null," ",-1))],4)):S("",!0),e("textarea",{title:"textarea",readOnly:t.readOnly,style:R({fontSize:t.fontSize,padding:t.header?t.lineNums?"10px "+t.padding+" "+t.padding:"0 "+t.padding+" "+t.padding:t.padding,marginLeft:s.showLineNums?s.lineNumsWidth+"px":"0",width:s.showLineNums?"calc(100% - "+s.lineNumsWidth+"px)":"100%"}),ref:"textarea",autofocus:t.autofocus,spellcheck:"false",onKeydown:r[0]||(r[0]=se(ne((...f)=>g.tab&&g.tab(...f),["prevent","stop"]),["tab"])),onScroll:r[1]||(r[1]=(...f)=>g.calcScrollDistance&&g.calcScrollDistance(...f)),value:t.modelValue==null?s.content:t.modelValue,onInput:r[2]||(r[2]=(...f)=>g.updateValue&&g.updateValue(...f))},null,44,yt),e("pre",{style:R({paddingRight:s.scrollBarWidth+"px",paddingBottom:s.scrollBarHeight+"px",marginLeft:s.showLineNums?s.lineNumsWidth+"px":"0",width:s.showLineNums?"calc(100% - "+s.lineNumsWidth+"px)":"100%"})},[r[6]||(r[6]=V(" ")),B((c(),v("code",{ref:"code",class:$(s.languageClass),style:R({top:s.top+"px",left:s.left+"px",fontSize:t.fontSize,padding:t.header?t.lineNums?"10px "+t.padding+" "+t.padding:"0 "+t.padding+" "+t.padding:t.padding})},r[5]||(r[5]=[V(`
+ `)]),6)),[[h,g.contentValue]]),r[7]||(r[7]=V(`
+ `))],4)],4)],4)],14,ht)}const wt=I(pt,[["render",xt]]),Ct={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll"},kt={class:"container d-flex h-100 w-100"},_t={class:"m-auto modal-dialog-centered dashboardModal",style:{width:"1000px"}},St={class:"card rounded-3 shadow flex-grow-1",id:"deleteConfigurationContainer"},Nt={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0"},$t={class:"mb-0"},Bt={class:"card-body px-4 d-flex flex-column gap-3"},Rt={key:0,class:"alert alert-danger rounded-3 mb-0"},Pt={class:"mb-2"},Dt={class:"bg-body w-100 p-2 rounded-3"},Wt={class:"d-flex gap-2"},Lt=["disabled"],Vt=["disabled"],Ht={__name:"editRawConfigurationFile",emits:["close"],async setup(o,{emit:r}){let t,p;const s=r,g=O(),u=x(""),h=x(""),f=x(!1),b=x(""),y=async()=>{await M("/api/getWireguardConfigurationRawFile",{configurationName:g.params.id},d=>{u.value=d.data.content,h.value=d.data.path})};[t,p]=ae(()=>y()),await t,p();const w=z(),C=x(!1),_=async()=>{C.value=!0,await W("/api/updateWireguardConfigurationRawFile",{configurationName:g.params.id,rawConfiguration:u.value},d=>{d.status?(f.value=!1,w.newMessage("Server","Configuration saved","success")):(f.value=!0,b.value=d.message),C.value=!1})};return(d,n)=>(c(),v("div",Ct,[e("div",kt,[e("div",_t,[e("div",St,[e("div",Nt,[e("h5",$t,[i(l,{t:"Edit Raw Configuration File"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:n[0]||(n[0]=k=>s("close"))})]),e("div",Bt,[f.value?(c(),v("div",Rt,[e("div",Pt,[e("strong",null,[i(l,{t:"Failed to save configuration. Please see the following error message:"})])]),e("div",Dt,[e("pre",null,D(b.value),1)])])):S("",!0),i(wt,{disabled:!0,"read-only":C.value,modelValue:u.value,"onUpdate:modelValue":n[1]||(n[1]=k=>u.value=k),theme:K(w).Configuration.Server.dashboard_theme==="dark"?"github-dark":"github",languages:[["ini",h.value]],width:"100%",height:"600px"},null,8,["read-only","modelValue","theme","languages"]),e("div",Wt,[e("button",{class:"btn bg-secondary-subtle border-secondary-subtle text-secondary-emphasis rounded-3 shadow ms-auto px-3 py-2",disabled:C.value,onClick:n[2]||(n[2]=k=>y())},[n[4]||(n[4]=e("i",{class:"bi bi-arrow-clockwise me-2"},null,-1)),i(l,{t:"Reset"})],8,Lt),e("button",{onClick:n[3]||(n[3]=k=>_()),disabled:C.value,class:"btn bg-danger-subtle border-danger-subtle text-danger-emphasis rounded-3 px-3 py-2 shadow"},[n[5]||(n[5]=e("i",{class:"bi bi-save-fill me-2"},null,-1)),C.value?(c(),N(l,{key:1,t:"Saving..."})):(c(),N(l,{key:0,t:"Save"}))],8,Vt)])])])])])]))}},Mt={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll",ref:"editConfigurationContainer"},zt={class:"container d-flex h-100 w-100"},It={class:"m-auto modal-dialog-centered dashboardModal",style:{width:"700px"}},Kt={class:"card rounded-3 shadow flex-grow-1"},Tt={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4"},Ft={class:"mb-0"},Ot={class:"card-body px-4 pb-4"},Ut={class:"d-flex gap-2 flex-column"},At={key:0,class:"d-flex align-items-center gap-3"},Jt={class:"text-muted"},jt={class:"d-flex align-items-center gap-3"},Yt={class:"text-muted",style:{"word-break":"keep-all"}},Et={class:"ms-auto",style:{"word-break":"break-all"}},Gt={class:"d-flex"},qt={for:"configuration_private_key",class:"form-label"},Zt={class:"text-muted d-block"},Qt={class:"form-check form-switch ms-auto"},Xt=["disabled"],es={for:"configuration_ipaddress_cidr",class:"form-label"},ts={class:"text-muted"},ss=["disabled"],ns={for:"configuration_listen_port",class:"form-label"},as={class:"text-muted"},is=["disabled"],ls=["for"],os={class:"text-muted"},rs=["disabled","onUpdate:modelValue","id"],ds=["for"],us={class:"text-muted"},cs=["disabled","onUpdate:modelValue","id"],fs={class:"d-flex align-items-center gap-2 mt-4"},gs=["disabled"],ms=["disabled"],bs={class:"mb-3"},vs={class:"d-flex gap-2 flex-column"},xs={__name:"editConfiguration",props:{configurationInfo:Object},emits:["changed","close","refresh"],setup(o,{emit:r}){const t=o,p=q(),s=z(),g=x(!1),u=J(JSON.parse(JSON.stringify(t.configurationInfo))),h=x(!1),f=x(!1),b=J({PrivateKey:!0,IPAddress:!0,ListenPort:!0}),y=()=>{p.checkWGKeyLength(u.PrivateKey)?(b.PrivateKey=!0,u.PublicKey=window.wireguard.generatePublicKey(u.PrivateKey)):b.PrivateKey=!1},w=()=>{f.value=!1,Object.assign(u,JSON.parse(JSON.stringify(t.configurationInfo)))},C=r,_=()=>{g.value=!0,W("/api/updateWireguardConfiguration",u,L=>{g.value=!1,L.status?(s.newMessage("Server","Configuration saved","success"),f.value=!1,C("dataChanged",L.data)):s.newMessage("Server",L.message,"danger")})},d=x(!1);Z(u,()=>{f.value=JSON.stringify(u)!==JSON.stringify(t.configurationInfo)},{deep:!0});const n=x(!1),k=x(!1),U=x(!1);return(L,a)=>(c(),v("div",Mt,[i(G,{name:"zoom"},{default:T(()=>[n.value?(c(),N(Ht,{key:0,name:"EditRawConfigurationFile",onClose:a[0]||(a[0]=m=>n.value=!1)})):S("",!0),U.value?(c(),N(xe,{key:"DeleteConfiguration",onBackup:a[1]||(a[1]=m=>k.value=!0),onClose:a[2]||(a[2]=m=>U.value=!1)})):S("",!0),k.value?(c(),N(nt,{key:2,onClose:a[3]||(a[3]=m=>k.value=!1),onRefreshPeersList:a[4]||(a[4]=m=>C("refresh"))})):S("",!0)]),_:1}),e("div",zt,[e("div",It,[e("div",Kt,[e("div",Tt,[e("h4",Ft,[i(l,{t:"Configuration Settings"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:a[5]||(a[5]=m=>L.$emit("close"))})]),e("div",Ot,[e("div",Ut,[d.value?S("",!0):(c(),v("div",At,[e("small",Jt,[i(l,{t:"Name"})]),e("small",null,D(u.Name),1),e("button",{onClick:a[6]||(a[6]=m=>d.value=!0),class:"btn btn-sm bg-danger-subtle border-danger-subtle text-danger-emphasis rounded-3 ms-auto"},[i(l,{t:"Update Name"})])])),d.value?(c(),N(ft,{key:1,onClose:a[7]||(a[7]=m=>d.value=!1),"configuration-name":u.Name},null,8,["configuration-name"])):(c(),v(H,{key:2},[a[24]||(a[24]=e("hr",null,null,-1)),e("div",jt,[e("small",Yt,[i(l,{t:"Public Key"})]),e("small",Et,D(u.PublicKey),1)]),a[25]||(a[25]=e("hr",null,null,-1)),e("div",null,[e("div",Gt,[e("label",qt,[e("small",Zt,[i(l,{t:"Private Key"})])]),e("div",Qt,[B(e("input",{class:"form-check-input",type:"checkbox",role:"switch",id:"editPrivateKeySwitch","onUpdate:modelValue":a[8]||(a[8]=m=>h.value=m)},null,512),[[ie,h.value]]),a[18]||(a[18]=e("label",{class:"form-check-label",for:"editPrivateKeySwitch"},[e("small",null,"Edit")],-1))])]),B(e("input",{type:"text",class:$(["form-control form-control-sm rounded-3",{"is-invalid":!b.PrivateKey}]),disabled:g.value||!h.value,onKeyup:a[9]||(a[9]=m=>y()),"onUpdate:modelValue":a[10]||(a[10]=m=>u.PrivateKey=m),id:"configuration_private_key"},null,42,Xt),[[P,u.PrivateKey]])]),e("div",null,[e("label",es,[e("small",ts,[i(l,{t:"IP Address/CIDR"})])]),B(e("input",{type:"text",class:"form-control form-control-sm rounded-3",disabled:g.value,"onUpdate:modelValue":a[11]||(a[11]=m=>u.Address=m),id:"configuration_ipaddress_cidr"},null,8,ss),[[P,u.Address]])]),e("div",null,[e("label",ns,[e("small",as,[i(l,{t:"Listen Port"})])]),B(e("input",{type:"number",class:"form-control form-control-sm rounded-3",disabled:g.value,"onUpdate:modelValue":a[12]||(a[12]=m=>u.ListenPort=m),id:"configuration_listen_port"},null,8,is),[[P,u.ListenPort]])]),(c(),v(H,null,F(["PreUp","PreDown","PostUp","PostDown"],m=>e("div",null,[e("label",{for:"configuration_"+m,class:"form-label"},[e("small",os,[i(l,{t:m},null,8,["t"])])],8,ls),B(e("input",{type:"text",class:"form-control form-control-sm rounded-3",disabled:g.value,"onUpdate:modelValue":A=>u[m]=A,id:"configuration_"+m},null,8,rs),[[P,u[m]]])])),64)),o.configurationInfo.Protocol==="awg"?(c(),v(H,{key:0},F(["Jc","Jmin","Jmax","S1","S2","H1","H2","H3","H4"],m=>e("div",null,[e("label",{for:"configuration_"+m,class:"form-label"},[e("small",us,[i(l,{t:m},null,8,["t"])])],8,ds),B(e("input",{type:"number",class:"form-control form-control-sm rounded-3",disabled:g.value,"onUpdate:modelValue":A=>u[m]=A,id:"configuration_"+m},null,8,cs),[[P,u[m]]])])),64)):S("",!0),e("div",fs,[e("button",{class:"btn bg-secondary-subtle border-secondary-subtle text-secondary-emphasis rounded-3 shadow ms-auto",onClick:a[13]||(a[13]=m=>w()),disabled:!f.value||g.value},[a[19]||(a[19]=e("i",{class:"bi bi-arrow-clockwise me-2"},null,-1)),i(l,{t:"Reset"})],8,gs),e("button",{class:"btn bg-primary-subtle border-primary-subtle text-primary-emphasis rounded-3 shadow",disabled:!f.value||g.value,onClick:a[14]||(a[14]=m=>_())},[a[20]||(a[20]=e("i",{class:"bi bi-save-fill me-2"},null,-1)),i(l,{t:"Save"})],8,ms)]),a[26]||(a[26]=e("hr",null,null,-1)),e("h5",bs,[i(l,{t:"Danger Zone"})]),e("div",vs,[e("button",{onClick:a[15]||(a[15]=m=>k.value=!0),class:"btn bg-warning-subtle border-warning-subtle text-warning-emphasis rounded-3 text-start d-flex"},[a[21]||(a[21]=e("i",{class:"bi bi-copy me-auto"},null,-1)),i(l,{t:"Backup & Restore"})]),e("button",{onClick:a[16]||(a[16]=m=>n.value=!0),class:"btn bg-warning-subtle border-warning-subtle text-warning-emphasis rounded-3 d-flex"},[a[22]||(a[22]=e("i",{class:"bi bi-pen me-auto"},null,-1)),i(l,{t:"Edit Raw Configuration File"})]),e("button",{onClick:a[17]||(a[17]=m=>U.value=!0),class:"btn bg-danger-subtle border-danger-subtle text-danger-emphasis rounded-3 d-flex mt-4"},[a[23]||(a[23]=e("i",{class:"bi bi-trash-fill me-auto"},null,-1)),i(l,{t:"Delete Configuration"})])])],64))])])])])])],512))}};export{xs as default};
diff --git a/src/static/app/dist/assets/editConfiguration-Oqrarw_V.css b/src/static/app/dist/assets/editConfiguration-Oqrarw_V.css
deleted file mode 100644
index 179e589..0000000
--- a/src/static/app/dist/assets/editConfiguration-Oqrarw_V.css
+++ /dev/null
@@ -1 +0,0 @@
-@media screen and (max-width: 567px){.inputGroup{&[data-v-4be4f48a]{flex-direction:column}h3[data-v-4be4f48a]{transform:rotate(90deg)}}}
diff --git a/src/static/app/dist/assets/index-24JS7yYN.css b/src/static/app/dist/assets/index-24JS7yYN.css
new file mode 100644
index 0000000..93dfd7c
--- /dev/null
+++ b/src/static/app/dist/assets/index-24JS7yYN.css
@@ -0,0 +1 @@
+.agentMessage[data-v-8635cf47]{white-space:break-spaces;max-width:80%;display:flex;flex-direction:column;word-wrap:break-word}.text-bg-secondary[data-v-8635cf47]{background-color:RGBA(var(--bs-secondary-rgb),.7)!important}.text-bg-primary[data-v-8635cf47]{background-color:RGBA(var(--bs-primary-rgb),.7)!important}.agentContainer[data-v-694cfad1]{--agentHeight: 100vh;position:absolute;z-index:9999;top:0;left:100%;width:450px;box-shadow:0 10px 30px #0000004d;backdrop-filter:blur(8px);background:linear-gradient(var(--degree),#009dff52 var(--distance2),#ff4a0052 100%)}.agentContainer.enabled[data-v-694cfad1]{height:calc(var(--agentHeight) - 1rem)}@media screen and (max-width: 768px){.agentContainer[data-v-694cfad1]{--agentHeight: 100vh !important;top:0;left:0;max-height:calc(var(--agentHeight) - 58px - 1rem);width:calc(100% - 1rem)}}.agentChatroomBody[data-v-694cfad1]{flex:1 1 auto;overflow-y:auto;max-height:calc(var(--agentHeight) - 70px - 244px)}@media screen and (max-width: 768px){.navbar-container[data-v-58e71749]{position:absolute!important;z-index:1000;animation-duration:.4s;animation-fill-mode:both;display:none;animation-timing-function:cubic-bezier(.82,.58,.17,.9)}.navbar-container.active[data-v-58e71749]{animation-direction:normal;display:block!important;animation-name:zoomInFade-58e71749}}.navbar-container[data-v-58e71749]{height:100vh;position:relative}@supports (height: 100dvh){@media screen and (max-width: 768px){.navbar-container[data-v-58e71749]{height:calc(100dvh - 58px)}}}@keyframes zoomInFade-58e71749{0%{opacity:0;transform:translateY(60px);filter:blur(3px)}to{opacity:1;transform:translateY(0);filter:blur(0px)}}.slideIn-enter-active[data-v-58e71749],.slideIn-leave-active[data-v-58e71749]{transition:all .3s cubic-bezier(.82,.58,.17,1)}.slideIn-enter-from[data-v-58e71749],.slideIn-leave-to[data-v-58e71749]{transform:translateY(30px);filter:blur(3px);opacity:0}main[data-v-0c6a5068]{height:100vh}@supports (height: 100dvh){@media screen and (max-width: 768px){main[data-v-0c6a5068]{height:calc(100dvh - 58px)}}}
diff --git a/src/static/app/dist/assets/index-B6Dvwg0x.js b/src/static/app/dist/assets/index-B6Dvwg0x.js
new file mode 100644
index 0000000..ce03a8f
--- /dev/null
+++ b/src/static/app/dist/assets/index-B6Dvwg0x.js
@@ -0,0 +1,18 @@
+import{P as Ws,Q as Vs,R as Ue,U as Hn,r as Wn,o as Vn,V as Nn,H as jn,X as Xe,Y as $n,Z as Ns}from"./index-CUmHRwBw.js";/*!
+ * @kurkle/color v0.3.2
+ * https://github.com/kurkle/color#readme
+ * (c) 2023 Jukka Kurkela
+ * Released under the MIT License
+ */function se(i){return i+.5|0}const lt=(i,t,e)=>Math.max(Math.min(i,e),t);function jt(i){return lt(se(i*2.55),0,255)}function dt(i){return lt(se(i*255),0,255)}function at(i){return lt(se(i/2.55)/100,0,1)}function yi(i){return lt(se(i*100),0,100)}const X={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Ke=[..."0123456789ABCDEF"],Yn=i=>Ke[i&15],Un=i=>Ke[(i&240)>>4]+Ke[i&15],le=i=>(i&240)>>4===(i&15),Xn=i=>le(i.r)&&le(i.g)&&le(i.b)&&le(i.a);function Kn(i){var t=i.length,e;return i[0]==="#"&&(t===4||t===5?e={r:255&X[i[1]]*17,g:255&X[i[2]]*17,b:255&X[i[3]]*17,a:t===5?X[i[4]]*17:255}:(t===7||t===9)&&(e={r:X[i[1]]<<4|X[i[2]],g:X[i[3]]<<4|X[i[4]],b:X[i[5]]<<4|X[i[6]],a:t===9?X[i[7]]<<4|X[i[8]]:255})),e}const qn=(i,t)=>i<255?t(i):"";function Gn(i){var t=Xn(i)?Yn:Un;return i?"#"+t(i.r)+t(i.g)+t(i.b)+qn(i.a,t):void 0}const Zn=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function js(i,t,e){const s=t*Math.min(e,1-e),n=(o,r=(o+i/30)%12)=>e-s*Math.max(Math.min(r-3,9-r,1),-1);return[n(0),n(8),n(4)]}function Qn(i,t,e){const s=(n,o=(n+i/60)%6)=>e-e*t*Math.max(Math.min(o,4-o,1),0);return[s(5),s(3),s(1)]}function Jn(i,t,e){const s=js(i,1,.5);let n;for(t+e>1&&(n=1/(t+e),t*=n,e*=n),n=0;n<3;n++)s[n]*=1-t-e,s[n]+=t;return s}function to(i,t,e,s,n){return i===n?(t-e)/s+(t.5?h/(2-o-r):h/(o+r),l=to(e,s,n,h,o),l=l*60+.5),[l|0,c||0,a]}function ni(i,t,e,s){return(Array.isArray(t)?i(t[0],t[1],t[2]):i(t,e,s)).map(dt)}function oi(i,t,e){return ni(js,i,t,e)}function eo(i,t,e){return ni(Jn,i,t,e)}function io(i,t,e){return ni(Qn,i,t,e)}function $s(i){return(i%360+360)%360}function so(i){const t=Zn.exec(i);let e=255,s;if(!t)return;t[5]!==s&&(e=t[6]?jt(+t[5]):dt(+t[5]));const n=$s(+t[2]),o=+t[3]/100,r=+t[4]/100;return t[1]==="hwb"?s=eo(n,o,r):t[1]==="hsv"?s=io(n,o,r):s=oi(n,o,r),{r:s[0],g:s[1],b:s[2],a:e}}function no(i,t){var e=si(i);e[0]=$s(e[0]+t),e=oi(e),i.r=e[0],i.g=e[1],i.b=e[2]}function oo(i){if(!i)return;const t=si(i),e=t[0],s=yi(t[1]),n=yi(t[2]);return i.a<255?`hsla(${e}, ${s}%, ${n}%, ${at(i.a)})`:`hsl(${e}, ${s}%, ${n}%)`}const vi={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},ki={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};function ro(){const i={},t=Object.keys(ki),e=Object.keys(vi);let s,n,o,r,a;for(s=0;s>16&255,o>>8&255,o&255]}return i}let ce;function ao(i){ce||(ce=ro(),ce.transparent=[0,0,0,0]);const t=ce[i.toLowerCase()];return t&&{r:t[0],g:t[1],b:t[2],a:t.length===4?t[3]:255}}const lo=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;function co(i){const t=lo.exec(i);let e=255,s,n,o;if(t){if(t[7]!==s){const r=+t[7];e=t[8]?jt(r):lt(r*255,0,255)}return s=+t[1],n=+t[3],o=+t[5],s=255&(t[2]?jt(s):lt(s,0,255)),n=255&(t[4]?jt(n):lt(n,0,255)),o=255&(t[6]?jt(o):lt(o,0,255)),{r:s,g:n,b:o,a:e}}}function ho(i){return i&&(i.a<255?`rgba(${i.r}, ${i.g}, ${i.b}, ${at(i.a)})`:`rgb(${i.r}, ${i.g}, ${i.b})`)}const Ie=i=>i<=.0031308?i*12.92:Math.pow(i,1/2.4)*1.055-.055,Ot=i=>i<=.04045?i/12.92:Math.pow((i+.055)/1.055,2.4);function fo(i,t,e){const s=Ot(at(i.r)),n=Ot(at(i.g)),o=Ot(at(i.b));return{r:dt(Ie(s+e*(Ot(at(t.r))-s))),g:dt(Ie(n+e*(Ot(at(t.g))-n))),b:dt(Ie(o+e*(Ot(at(t.b))-o))),a:i.a+e*(t.a-i.a)}}function he(i,t,e){if(i){let s=si(i);s[t]=Math.max(0,Math.min(s[t]+s[t]*e,t===0?360:1)),s=oi(s),i.r=s[0],i.g=s[1],i.b=s[2]}}function Ys(i,t){return i&&Object.assign(t||{},i)}function wi(i){var t={r:0,g:0,b:0,a:255};return Array.isArray(i)?i.length>=3&&(t={r:i[0],g:i[1],b:i[2],a:255},i.length>3&&(t.a=dt(i[3]))):(t=Ys(i,{r:0,g:0,b:0,a:1}),t.a=dt(t.a)),t}function uo(i){return i.charAt(0)==="r"?co(i):so(i)}class Gt{constructor(t){if(t instanceof Gt)return t;const e=typeof t;let s;e==="object"?s=wi(t):e==="string"&&(s=Kn(t)||ao(t)||uo(t)),this._rgb=s,this._valid=!!s}get valid(){return this._valid}get rgb(){var t=Ys(this._rgb);return t&&(t.a=at(t.a)),t}set rgb(t){this._rgb=wi(t)}rgbString(){return this._valid?ho(this._rgb):void 0}hexString(){return this._valid?Gn(this._rgb):void 0}hslString(){return this._valid?oo(this._rgb):void 0}mix(t,e){if(t){const s=this.rgb,n=t.rgb;let o;const r=e===o?.5:e,a=2*r-1,l=s.a-n.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,s.r=255&c*s.r+o*n.r+.5,s.g=255&c*s.g+o*n.g+.5,s.b=255&c*s.b+o*n.b+.5,s.a=r*s.a+(1-r)*n.a,this.rgb=s}return this}interpolate(t,e){return t&&(this._rgb=fo(this._rgb,t._rgb,e)),this}clone(){return new Gt(this.rgb)}alpha(t){return this._rgb.a=dt(t),this}clearer(t){const e=this._rgb;return e.a*=1-t,this}greyscale(){const t=this._rgb,e=se(t.r*.3+t.g*.59+t.b*.11);return t.r=t.g=t.b=e,this}opaquer(t){const e=this._rgb;return e.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return he(this._rgb,2,t),this}darken(t){return he(this._rgb,2,-t),this}saturate(t){return he(this._rgb,1,t),this}desaturate(t){return he(this._rgb,1,-t),this}rotate(t){return no(this._rgb,t),this}}/*!
+ * Chart.js v4.4.4
+ * https://www.chartjs.org
+ * (c) 2024 Chart.js Contributors
+ * Released under the MIT License
+ */function nt(){}const go=(()=>{let i=0;return()=>i++})();function A(i){return i===null||typeof i>"u"}function z(i){if(Array.isArray&&Array.isArray(i))return!0;const t=Object.prototype.toString.call(i);return t.slice(0,7)==="[object"&&t.slice(-6)==="Array]"}function O(i){return i!==null&&Object.prototype.toString.call(i)==="[object Object]"}function V(i){return(typeof i=="number"||i instanceof Number)&&isFinite(+i)}function J(i,t){return V(i)?i:t}function D(i,t){return typeof i>"u"?t:i}const po=(i,t)=>typeof i=="string"&&i.endsWith("%")?parseFloat(i)/100*t:+i;function F(i,t,e){if(i&&typeof i.call=="function")return i.apply(e,t)}function L(i,t,e,s){let n,o,r;if(z(i))for(o=i.length,n=0;ni,x:i=>i.x,y:i=>i.y};function _o(i){const t=i.split("."),e=[];let s="";for(const n of t)s+=n,s.endsWith("\\")?s=s.slice(0,-1)+".":(e.push(s),s="");return e}function xo(i){const t=_o(i);return e=>{for(const s of t){if(s==="")break;e=e&&e[s]}return e}}function At(i,t){return(Si[t]||(Si[t]=xo(t)))(i)}function ri(i){return i.charAt(0).toUpperCase()+i.slice(1)}const Qt=i=>typeof i<"u",ft=i=>typeof i=="function",Mi=(i,t)=>{if(i.size!==t.size)return!1;for(const e of i)if(!t.has(e))return!1;return!0};function yo(i){return i.type==="mouseup"||i.type==="click"||i.type==="contextmenu"}const E=Math.PI,Z=2*E,vo=Z+E,Pe=Number.POSITIVE_INFINITY,ko=E/180,G=E/2,mt=E/4,Pi=E*2/3,qe=Math.log10,st=Math.sign;function Xt(i,t,e){return Math.abs(i-t)n-o).pop(),t}function Jt(i){return!isNaN(parseFloat(i))&&isFinite(i)}function So(i,t){const e=Math.round(i);return e-t<=i&&e+t>=i}function Mo(i,t,e){let s,n,o;for(s=0,n=i.length;sl&&c=Math.min(t,e)-s&&i<=Math.max(t,e)+s}function ai(i,t,e){e=e||(r=>i[r]1;)o=n+s>>1,e(o)?n=o:s=o;return{lo:n,hi:s}}const kt=(i,t,e,s)=>ai(i,e,s?n=>{const o=i[n][t];return oi[n][t]ai(i,e,s=>i[s][t]>=e);function Lo(i,t,e){let s=0,n=i.length;for(;ss&&i[n-1]>e;)n--;return s>0||n{const s="_onData"+ri(e),n=i[e];Object.defineProperty(i,e,{configurable:!0,enumerable:!1,value(...o){const r=n.apply(this,o);return i._chartjs.listeners.forEach(a=>{typeof a[s]=="function"&&a[s](...o)}),r}})})}function Ci(i,t){const e=i._chartjs;if(!e)return;const s=e.listeners,n=s.indexOf(t);n!==-1&&s.splice(n,1),!(s.length>0)&&(Ks.forEach(o=>{delete i[o]}),delete i._chartjs)}function qs(i){const t=new Set(i);return t.size===i.length?i:Array.from(t)}const Gs=function(){return typeof window>"u"?function(i){return i()}:window.requestAnimationFrame}();function Zs(i,t){let e=[],s=!1;return function(...n){e=n,s||(s=!0,Gs.call(window,()=>{s=!1,i.apply(t,e)}))}}function Fo(i,t){let e;return function(...s){return t?(clearTimeout(e),e=setTimeout(i,t,s)):i.apply(this,s),t}}const li=i=>i==="start"?"left":i==="end"?"right":"center",H=(i,t,e)=>i==="start"?t:i==="end"?e:(t+e)/2,Io=(i,t,e,s)=>i===(s?"left":"right")?e:i==="center"?(t+e)/2:t;function Ro(i,t,e){const s=t.length;let n=0,o=s;if(i._sorted){const{iScale:r,_parsed:a}=i,l=r.axis,{min:c,max:h,minDefined:d,maxDefined:f}=r.getUserBounds();d&&(n=Y(Math.min(kt(a,l,c).lo,e?s:kt(t,l,r.getPixelForValue(c)).lo),0,s-1)),f?o=Y(Math.max(kt(a,r.axis,h,!0).hi+1,e?0:kt(t,l,r.getPixelForValue(h),!0).hi+1),n,s)-n:o=s-n}return{start:n,count:o}}function zo(i){const{xScale:t,yScale:e,_scaleRanges:s}=i,n={xmin:t.min,xmax:t.max,ymin:e.min,ymax:e.max};if(!s)return i._scaleRanges=n,!0;const o=s.xmin!==t.min||s.xmax!==t.max||s.ymin!==e.min||s.ymax!==e.max;return Object.assign(s,n),o}const de=i=>i===0||i===1,Ti=(i,t,e)=>-(Math.pow(2,10*(i-=1))*Math.sin((i-t)*Z/e)),Li=(i,t,e)=>Math.pow(2,-10*i)*Math.sin((i-t)*Z/e)+1,Kt={linear:i=>i,easeInQuad:i=>i*i,easeOutQuad:i=>-i*(i-2),easeInOutQuad:i=>(i/=.5)<1?.5*i*i:-.5*(--i*(i-2)-1),easeInCubic:i=>i*i*i,easeOutCubic:i=>(i-=1)*i*i+1,easeInOutCubic:i=>(i/=.5)<1?.5*i*i*i:.5*((i-=2)*i*i+2),easeInQuart:i=>i*i*i*i,easeOutQuart:i=>-((i-=1)*i*i*i-1),easeInOutQuart:i=>(i/=.5)<1?.5*i*i*i*i:-.5*((i-=2)*i*i*i-2),easeInQuint:i=>i*i*i*i*i,easeOutQuint:i=>(i-=1)*i*i*i*i+1,easeInOutQuint:i=>(i/=.5)<1?.5*i*i*i*i*i:.5*((i-=2)*i*i*i*i+2),easeInSine:i=>-Math.cos(i*G)+1,easeOutSine:i=>Math.sin(i*G),easeInOutSine:i=>-.5*(Math.cos(E*i)-1),easeInExpo:i=>i===0?0:Math.pow(2,10*(i-1)),easeOutExpo:i=>i===1?1:-Math.pow(2,-10*i)+1,easeInOutExpo:i=>de(i)?i:i<.5?.5*Math.pow(2,10*(i*2-1)):.5*(-Math.pow(2,-10*(i*2-1))+2),easeInCirc:i=>i>=1?i:-(Math.sqrt(1-i*i)-1),easeOutCirc:i=>Math.sqrt(1-(i-=1)*i),easeInOutCirc:i=>(i/=.5)<1?-.5*(Math.sqrt(1-i*i)-1):.5*(Math.sqrt(1-(i-=2)*i)+1),easeInElastic:i=>de(i)?i:Ti(i,.075,.3),easeOutElastic:i=>de(i)?i:Li(i,.075,.3),easeInOutElastic(i){return de(i)?i:i<.5?.5*Ti(i*2,.1125,.45):.5+.5*Li(i*2-1,.1125,.45)},easeInBack(i){return i*i*((1.70158+1)*i-1.70158)},easeOutBack(i){return(i-=1)*i*((1.70158+1)*i+1.70158)+1},easeInOutBack(i){let t=1.70158;return(i/=.5)<1?.5*(i*i*(((t*=1.525)+1)*i-t)):.5*((i-=2)*i*(((t*=1.525)+1)*i+t)+2)},easeInBounce:i=>1-Kt.easeOutBounce(1-i),easeOutBounce(i){return i<1/2.75?7.5625*i*i:i<2/2.75?7.5625*(i-=1.5/2.75)*i+.75:i<2.5/2.75?7.5625*(i-=2.25/2.75)*i+.9375:7.5625*(i-=2.625/2.75)*i+.984375},easeInOutBounce:i=>i<.5?Kt.easeInBounce(i*2)*.5:Kt.easeOutBounce(i*2-1)*.5+.5};function ci(i){if(i&&typeof i=="object"){const t=i.toString();return t==="[object CanvasPattern]"||t==="[object CanvasGradient]"}return!1}function Ai(i){return ci(i)?i:new Gt(i)}function Re(i){return ci(i)?i:new Gt(i).saturate(.5).darken(.1).hexString()}const Eo=["x","y","borderWidth","radius","tension"],Bo=["color","borderColor","backgroundColor"];function Ho(i){i.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),i.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>t!=="onProgress"&&t!=="onComplete"&&t!=="fn"}),i.set("animations",{colors:{type:"color",properties:Bo},numbers:{type:"number",properties:Eo}}),i.describe("animations",{_fallback:"animation"}),i.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>t|0}}}})}function Wo(i){i.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})}const Fi=new Map;function Vo(i,t){t=t||{};const e=i+JSON.stringify(t);let s=Fi.get(e);return s||(s=new Intl.NumberFormat(i,t),Fi.set(e,s)),s}function Qs(i,t,e){return Vo(t,e).format(i)}const Js={values(i){return z(i)?i:""+i},numeric(i,t,e){if(i===0)return"0";const s=this.chart.options.locale;let n,o=i;if(e.length>1){const c=Math.max(Math.abs(e[0].value),Math.abs(e[e.length-1].value));(c<1e-4||c>1e15)&&(n="scientific"),o=No(i,e)}const r=qe(Math.abs(o)),a=isNaN(r)?1:Math.max(Math.min(-1*Math.floor(r),20),0),l={notation:n,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),Qs(i,s,l)},logarithmic(i,t,e){if(i===0)return"0";const s=e[t].significand||i/Math.pow(10,Math.floor(qe(i)));return[1,2,3,5,10,15].includes(s)||t>.8*e.length?Js.numeric.call(this,i,t,e):""}};function No(i,t){let e=t.length>3?t[2].value-t[1].value:t[1].value-t[0].value;return Math.abs(e)>=1&&i!==Math.floor(i)&&(e=i-Math.floor(i)),e}var tn={formatters:Js};function jo(i){i.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:tn.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),i.route("scale.ticks","color","","color"),i.route("scale.grid","color","","borderColor"),i.route("scale.border","color","","borderColor"),i.route("scale.title","color","","color"),i.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&t!=="callback"&&t!=="parser",_indexable:t=>t!=="borderDash"&&t!=="tickBorderDash"&&t!=="dash"}),i.describe("scales",{_fallback:"scale"}),i.describe("scale.ticks",{_scriptable:t=>t!=="backdropPadding"&&t!=="callback",_indexable:t=>t!=="backdropPadding"})}const St=Object.create(null),Ze=Object.create(null);function qt(i,t){if(!t)return i;const e=t.split(".");for(let s=0,n=e.length;ss.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(s,n)=>Re(n.backgroundColor),this.hoverBorderColor=(s,n)=>Re(n.borderColor),this.hoverColor=(s,n)=>Re(n.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ze(this,t,e)}get(t){return qt(this,t)}describe(t,e){return ze(Ze,t,e)}override(t,e){return ze(St,t,e)}route(t,e,s,n){const o=qt(this,t),r=qt(this,s),a="_"+e;Object.defineProperties(o,{[a]:{value:o[e],writable:!0},[e]:{enumerable:!0,get(){const l=this[a],c=r[n];return O(l)?Object.assign({},c,l):D(l,c)},set(l){this[a]=l}}})}apply(t){t.forEach(e=>e(this))}}var R=new $o({_scriptable:i=>!i.startsWith("on"),_indexable:i=>i!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[Ho,Wo,jo]);function Yo(i){return!i||A(i.size)||A(i.family)?null:(i.style?i.style+" ":"")+(i.weight?i.weight+" ":"")+i.size+"px "+i.family}function Ii(i,t,e,s,n){let o=t[n];return o||(o=t[n]=i.measureText(n).width,e.push(n)),o>s&&(s=o),s}function bt(i,t,e){const s=i.currentDevicePixelRatio,n=e!==0?Math.max(e/2,.5):0;return Math.round((t-n)*s)/s+n}function Ri(i,t){!t&&!i||(t=t||i.getContext("2d"),t.save(),t.resetTransform(),t.clearRect(0,0,i.width,i.height),t.restore())}function Qe(i,t,e,s){en(i,t,e,s,null)}function en(i,t,e,s,n){let o,r,a,l,c,h,d,f;const u=t.pointStyle,p=t.rotation,g=t.radius;let m=(p||0)*ko;if(u&&typeof u=="object"&&(o=u.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]")){i.save(),i.translate(e,s),i.rotate(m),i.drawImage(u,-u.width/2,-u.height/2,u.width,u.height),i.restore();return}if(!(isNaN(g)||g<=0)){switch(i.beginPath(),u){default:n?i.ellipse(e,s,n/2,g,0,0,Z):i.arc(e,s,g,0,Z),i.closePath();break;case"triangle":h=n?n/2:g,i.moveTo(e+Math.sin(m)*h,s-Math.cos(m)*g),m+=Pi,i.lineTo(e+Math.sin(m)*h,s-Math.cos(m)*g),m+=Pi,i.lineTo(e+Math.sin(m)*h,s-Math.cos(m)*g),i.closePath();break;case"rectRounded":c=g*.516,l=g-c,r=Math.cos(m+mt)*l,d=Math.cos(m+mt)*(n?n/2-c:l),a=Math.sin(m+mt)*l,f=Math.sin(m+mt)*(n?n/2-c:l),i.arc(e-d,s-a,c,m-E,m-G),i.arc(e+f,s-r,c,m-G,m),i.arc(e+d,s+a,c,m,m+G),i.arc(e-f,s+r,c,m+G,m+E),i.closePath();break;case"rect":if(!p){l=Math.SQRT1_2*g,h=n?n/2:l,i.rect(e-h,s-l,2*h,2*l);break}m+=mt;case"rectRot":d=Math.cos(m)*(n?n/2:g),r=Math.cos(m)*g,a=Math.sin(m)*g,f=Math.sin(m)*(n?n/2:g),i.moveTo(e-d,s-a),i.lineTo(e+f,s-r),i.lineTo(e+d,s+a),i.lineTo(e-f,s+r),i.closePath();break;case"crossRot":m+=mt;case"cross":d=Math.cos(m)*(n?n/2:g),r=Math.cos(m)*g,a=Math.sin(m)*g,f=Math.sin(m)*(n?n/2:g),i.moveTo(e-d,s-a),i.lineTo(e+d,s+a),i.moveTo(e+f,s-r),i.lineTo(e-f,s+r);break;case"star":d=Math.cos(m)*(n?n/2:g),r=Math.cos(m)*g,a=Math.sin(m)*g,f=Math.sin(m)*(n?n/2:g),i.moveTo(e-d,s-a),i.lineTo(e+d,s+a),i.moveTo(e+f,s-r),i.lineTo(e-f,s+r),m+=mt,d=Math.cos(m)*(n?n/2:g),r=Math.cos(m)*g,a=Math.sin(m)*g,f=Math.sin(m)*(n?n/2:g),i.moveTo(e-d,s-a),i.lineTo(e+d,s+a),i.moveTo(e+f,s-r),i.lineTo(e-f,s+r);break;case"line":r=n?n/2:Math.cos(m)*g,a=Math.sin(m)*g,i.moveTo(e-r,s-a),i.lineTo(e+r,s+a);break;case"dash":i.moveTo(e,s),i.lineTo(e+Math.cos(m)*(n?n/2:g),s+Math.sin(m)*g);break;case!1:i.closePath();break}i.fill(),t.borderWidth>0&&i.stroke()}}function te(i,t,e){return e=e||.5,!t||i&&i.x>t.left-e&&i.xt.top-e&&i.y0&&o.strokeColor!=="";let l,c;for(i.save(),i.font=n.string,Ko(i,o),l=0;l+i||0;function sn(i,t){const e={},s=O(t),n=s?Object.keys(t):t,o=O(i)?s?r=>D(i[r],i[t[r]]):r=>i[r]:()=>i;for(const r of n)e[r]=tr(o(r));return e}function nn(i){return sn(i,{top:"y",right:"x",bottom:"y",left:"x"})}function Tt(i){return sn(i,["topLeft","topRight","bottomLeft","bottomRight"])}function q(i){const t=nn(i);return t.width=t.left+t.right,t.height=t.top+t.bottom,t}function W(i,t){i=i||{},t=t||R.font;let e=D(i.size,t.size);typeof e=="string"&&(e=parseInt(e,10));let s=D(i.style,t.style);s&&!(""+s).match(Qo)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:D(i.family,t.family),lineHeight:Jo(D(i.lineHeight,t.lineHeight),e),size:e,style:s,weight:D(i.weight,t.weight),string:""};return n.string=Yo(n),n}function fe(i,t,e,s){let n,o,r;for(n=0,o=i.length;ne&&a===0?0:a+l;return{min:r(s,-Math.abs(o)),max:r(n,o)}}function Mt(i,t){return Object.assign(Object.create(i),t)}function hi(i,t=[""],e,s,n=()=>i[0]){const o=e||i;typeof s>"u"&&(s=ln("_fallback",i));const r={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:i,_rootScopes:o,_fallback:s,_getTarget:n,override:a=>hi([a,...i],t,o,s)};return new Proxy(r,{deleteProperty(a,l){return delete a[l],delete a._keys,delete i[0][l],!0},get(a,l){return rn(a,l,()=>cr(l,t,i,a))},getOwnPropertyDescriptor(a,l){return Reflect.getOwnPropertyDescriptor(a._scopes[0],l)},getPrototypeOf(){return Reflect.getPrototypeOf(i[0])},has(a,l){return Ei(a).includes(l)},ownKeys(a){return Ei(a)},set(a,l,c){const h=a._storage||(a._storage=n());return a[l]=h[l]=c,delete a._keys,!0}})}function Ft(i,t,e,s){const n={_cacheable:!1,_proxy:i,_context:t,_subProxy:e,_stack:new Set,_descriptors:on(i,s),setContext:o=>Ft(i,o,e,s),override:o=>Ft(i.override(o),t,e,s)};return new Proxy(n,{deleteProperty(o,r){return delete o[r],delete i[r],!0},get(o,r,a){return rn(o,r,()=>sr(o,r,a))},getOwnPropertyDescriptor(o,r){return o._descriptors.allKeys?Reflect.has(i,r)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(i,r)},getPrototypeOf(){return Reflect.getPrototypeOf(i)},has(o,r){return Reflect.has(i,r)},ownKeys(){return Reflect.ownKeys(i)},set(o,r,a){return i[r]=a,delete o[r],!0}})}function on(i,t={scriptable:!0,indexable:!0}){const{_scriptable:e=t.scriptable,_indexable:s=t.indexable,_allKeys:n=t.allKeys}=i;return{allKeys:n,scriptable:e,indexable:s,isScriptable:ft(e)?e:()=>e,isIndexable:ft(s)?s:()=>s}}const ir=(i,t)=>i?i+ri(t):t,di=(i,t)=>O(t)&&i!=="adapters"&&(Object.getPrototypeOf(t)===null||t.constructor===Object);function rn(i,t,e){if(Object.prototype.hasOwnProperty.call(i,t)||t==="constructor")return i[t];const s=e();return i[t]=s,s}function sr(i,t,e){const{_proxy:s,_context:n,_subProxy:o,_descriptors:r}=i;let a=s[t];return ft(a)&&r.isScriptable(t)&&(a=nr(t,a,i,e)),z(a)&&a.length&&(a=or(t,a,i,r.isIndexable)),di(t,a)&&(a=Ft(a,n,o&&o[t],r)),a}function nr(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_stack:a}=e;if(a.has(i))throw new Error("Recursion detected: "+Array.from(a).join("->")+"->"+i);a.add(i);let l=t(o,r||s);return a.delete(i),di(i,l)&&(l=fi(n._scopes,n,i,l)),l}function or(i,t,e,s){const{_proxy:n,_context:o,_subProxy:r,_descriptors:a}=e;if(typeof o.index<"u"&&s(i))return t[o.index%t.length];if(O(t[0])){const l=t,c=n._scopes.filter(h=>h!==l);t=[];for(const h of l){const d=fi(c,n,i,h);t.push(Ft(d,o,r&&r[i],a))}}return t}function an(i,t,e){return ft(i)?i(t,e):i}const rr=(i,t)=>i===!0?t:typeof i=="string"?At(t,i):void 0;function ar(i,t,e,s,n){for(const o of t){const r=rr(e,o);if(r){i.add(r);const a=an(r._fallback,e,n);if(typeof a<"u"&&a!==e&&a!==s)return a}else if(r===!1&&typeof s<"u"&&e!==s)return null}return!1}function fi(i,t,e,s){const n=t._rootScopes,o=an(t._fallback,e,s),r=[...i,...n],a=new Set;a.add(s);let l=zi(a,r,e,o||e,s);return l===null||typeof o<"u"&&o!==e&&(l=zi(a,r,o,l,s),l===null)?!1:hi(Array.from(a),[""],n,o,()=>lr(t,e,s))}function zi(i,t,e,s,n){for(;e;)e=ar(i,t,e,s,n);return e}function lr(i,t,e){const s=i._getTarget();t in s||(s[t]={});const n=s[t];return z(n)&&O(e)?e:n||{}}function cr(i,t,e,s){let n;for(const o of t)if(n=ln(ir(o,i),e),typeof n<"u")return di(i,n)?fi(e,s,i,n):n}function ln(i,t){for(const e of t){if(!e)continue;const s=e[i];if(typeof s<"u")return s}}function Ei(i){let t=i._keys;return t||(t=i._keys=hr(i._scopes)),t}function hr(i){const t=new Set;for(const e of i)for(const s of Object.keys(e).filter(n=>!n.startsWith("_")))t.add(s);return Array.from(t)}const dr=Number.EPSILON||1e-14,It=(i,t)=>ti==="x"?"y":"x";function fr(i,t,e,s){const n=i.skip?t:i,o=t,r=e.skip?t:e,a=Ge(o,n),l=Ge(r,o);let c=a/(a+l),h=l/(a+l);c=isNaN(c)?0:c,h=isNaN(h)?0:h;const d=s*c,f=s*h;return{previous:{x:o.x-d*(r.x-n.x),y:o.y-d*(r.y-n.y)},next:{x:o.x+f*(r.x-n.x),y:o.y+f*(r.y-n.y)}}}function ur(i,t,e){const s=i.length;let n,o,r,a,l,c=It(i,0);for(let h=0;h!c.skip)),t.cubicInterpolationMode==="monotone")pr(i,n);else{let c=s?i[i.length-1]:i[0];for(o=0,r=i.length;oi.ownerDocument.defaultView.getComputedStyle(i,null);function _r(i,t){return Ae(i).getPropertyValue(t)}const xr=["top","right","bottom","left"];function wt(i,t,e){const s={};e=e?"-"+e:"";for(let n=0;n<4;n++){const o=xr[n];s[o]=parseFloat(i[t+"-"+o+e])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const yr=(i,t,e)=>(i>0||t>0)&&(!e||!e.shadowRoot);function vr(i,t){const e=i.touches,s=e&&e.length?e[0]:i,{offsetX:n,offsetY:o}=s;let r=!1,a,l;if(yr(n,o,i.target))a=n,l=o;else{const c=t.getBoundingClientRect();a=s.clientX-c.left,l=s.clientY-c.top,r=!0}return{x:a,y:l,box:r}}function xt(i,t){if("native"in i)return i;const{canvas:e,currentDevicePixelRatio:s}=t,n=Ae(e),o=n.boxSizing==="border-box",r=wt(n,"padding"),a=wt(n,"border","width"),{x:l,y:c,box:h}=vr(i,e),d=r.left+(h&&a.left),f=r.top+(h&&a.top);let{width:u,height:p}=t;return o&&(u-=r.width+a.width,p-=r.height+a.height),{x:Math.round((l-d)/u*e.width/s),y:Math.round((c-f)/p*e.height/s)}}function kr(i,t,e){let s,n;if(t===void 0||e===void 0){const o=i&&gi(i);if(!o)t=i.clientWidth,e=i.clientHeight;else{const r=o.getBoundingClientRect(),a=Ae(o),l=wt(a,"border","width"),c=wt(a,"padding");t=r.width-c.width-l.width,e=r.height-c.height-l.height,s=Oe(a.maxWidth,o,"clientWidth"),n=Oe(a.maxHeight,o,"clientHeight")}}return{width:t,height:e,maxWidth:s||Pe,maxHeight:n||Pe}}const ge=i=>Math.round(i*10)/10;function wr(i,t,e,s){const n=Ae(i),o=wt(n,"margin"),r=Oe(n.maxWidth,i,"clientWidth")||Pe,a=Oe(n.maxHeight,i,"clientHeight")||Pe,l=kr(i,t,e);let{width:c,height:h}=l;if(n.boxSizing==="content-box"){const f=wt(n,"border","width"),u=wt(n,"padding");c-=u.width+f.width,h-=u.height+f.height}return c=Math.max(0,c-o.width),h=Math.max(0,s?c/s:h-o.height),c=ge(Math.min(c,r,l.maxWidth)),h=ge(Math.min(h,a,l.maxHeight)),c&&!h&&(h=ge(c/2)),(t!==void 0||e!==void 0)&&s&&l.height&&h>l.height&&(h=l.height,c=ge(Math.floor(h*s))),{width:c,height:h}}function Bi(i,t,e){const s=t||1,n=Math.floor(i.height*s),o=Math.floor(i.width*s);i.height=Math.floor(i.height),i.width=Math.floor(i.width);const r=i.canvas;return r.style&&(e||!r.style.height&&!r.style.width)&&(r.style.height=`${i.height}px`,r.style.width=`${i.width}px`),i.currentDevicePixelRatio!==s||r.height!==n||r.width!==o?(i.currentDevicePixelRatio=s,r.height=n,r.width=o,i.ctx.setTransform(s,0,0,s,0,0),!0):!1}const Sr=function(){let i=!1;try{const t={get passive(){return i=!0,!1}};ui()&&(window.addEventListener("test",null,t),window.removeEventListener("test",null,t))}catch{}return i}();function Hi(i,t){const e=_r(i,t),s=e&&e.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function yt(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:i.y+e*(t.y-i.y)}}function Mr(i,t,e,s){return{x:i.x+e*(t.x-i.x),y:s==="middle"?e<.5?i.y:t.y:s==="after"?e<1?i.y:t.y:e>0?t.y:i.y}}function Pr(i,t,e,s){const n={x:i.cp2x,y:i.cp2y},o={x:t.cp1x,y:t.cp1y},r=yt(i,n,e),a=yt(n,o,e),l=yt(o,t,e),c=yt(r,a,e),h=yt(a,l,e);return yt(c,h,e)}const Dr=function(i,t){return{x(e){return i+i+t-e},setWidth(e){t=e},textAlign(e){return e==="center"?e:e==="right"?"left":"right"},xPlus(e,s){return e-s},leftForLtr(e,s){return e-s}}},Or=function(){return{x(i){return i},setWidth(i){},textAlign(i){return i},xPlus(i,t){return i+t},leftForLtr(i,t){return i}}};function Lt(i,t,e){return i?Dr(t,e):Or()}function hn(i,t){let e,s;(t==="ltr"||t==="rtl")&&(e=i.canvas.style,s=[e.getPropertyValue("direction"),e.getPropertyPriority("direction")],e.setProperty("direction",t,"important"),i.prevTextDirection=s)}function dn(i,t){t!==void 0&&(delete i.prevTextDirection,i.canvas.style.setProperty("direction",t[0],t[1]))}function fn(i){return i==="angle"?{between:Xs,compare:Oo,normalize:it}:{between:ct,compare:(t,e)=>t-e,normalize:t=>t}}function Wi({start:i,end:t,count:e,loop:s,style:n}){return{start:i%e,end:t%e,loop:s&&(t-i+1)%e===0,style:n}}function Cr(i,t,e){const{property:s,start:n,end:o}=e,{between:r,normalize:a}=fn(s),l=t.length;let{start:c,end:h,loop:d}=i,f,u;if(d){for(c+=l,h+=l,f=0,u=l;fl(n,y,b)&&a(n,y)!==0,x=()=>a(o,b)===0||l(o,y,b),S=()=>g||v(),w=()=>!g||x();for(let k=h,M=h;k<=d;++k)_=t[k%r],!_.skip&&(b=c(_[s]),b!==y&&(g=l(b,n,o),m===null&&S()&&(m=a(b,n)===0?k:M),m!==null&&w()&&(p.push(Wi({start:m,end:k,loop:f,count:r,style:u})),m=null),M=k,y=b));return m!==null&&p.push(Wi({start:m,end:d,loop:f,count:r,style:u})),p}function gn(i,t){const e=[],s=i.segments;for(let n=0;nn&&i[o%t].skip;)o--;return o%=t,{start:n,end:o}}function Lr(i,t,e,s){const n=i.length,o=[];let r=t,a=i[t],l;for(l=t+1;l<=e;++l){const c=i[l%n];c.skip||c.stop?a.skip||(s=!1,o.push({start:t%n,end:(l-1)%n,loop:s}),t=r=c.stop?l:null):(r=l,a.skip&&(t=l)),a=c}return r!==null&&o.push({start:t%n,end:r%n,loop:s}),o}function Ar(i,t){const e=i.points,s=i.options.spanGaps,n=e.length;if(!n)return[];const o=!!i._loop,{start:r,end:a}=Tr(e,n,o,s);if(s===!0)return Vi(i,[{start:r,end:a,loop:o}],e,t);const l=aa({chart:t,initial:e.initial,numSteps:r,currentStep:Math.min(s-e.start,r)}))}_refresh(){this._request||(this._running=!0,this._request=Gs.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(t=Date.now()){let e=0;this._charts.forEach((s,n)=>{if(!s.running||!s.items.length)return;const o=s.items;let r=o.length-1,a=!1,l;for(;r>=0;--r)l=o[r],l._active?(l._total>s.duration&&(s.duration=l._total),l.tick(t),a=!0):(o[r]=o[o.length-1],o.pop());a&&(n.draw(),this._notify(n,s,t,"progress")),o.length||(s.running=!1,this._notify(n,s,t,"complete"),s.initial=!1),e+=o.length}),this._lastDate=t,e===0&&(this._running=!1)}_getAnims(t){const e=this._charts;let s=e.get(t);return s||(s={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,s)),s}listen(t,e,s){this._getAnims(t).listeners[e].push(s)}add(t,e){!e||!e.length||this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce((s,n)=>Math.max(s,n._duration),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!(!e||!e.running||!e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const s=e.items;let n=s.length-1;for(;n>=0;--n)s[n].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var ot=new Rr;const ji="transparent",zr={boolean(i,t,e){return e>.5?t:i},color(i,t,e){const s=Ai(i||ji),n=s.valid&&Ai(t||ji);return n&&n.valid?n.mix(s,e).hexString():t},number(i,t,e){return i+(t-i)*e}};class Er{constructor(t,e,s,n){const o=e[s];n=fe([t.to,n,o,t.from]);const r=fe([t.from,o,n]);this._active=!0,this._fn=t.fn||zr[t.type||typeof r],this._easing=Kt[t.easing]||Kt.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=s,this._from=r,this._to=n,this._promises=void 0}active(){return this._active}update(t,e,s){if(this._active){this._notify(!1);const n=this._target[this._prop],o=s-this._start,r=this._duration-o;this._start=s,this._duration=Math.floor(Math.max(r,t.duration)),this._total+=o,this._loop=!!t.loop,this._to=fe([t.to,e,n,t.from]),this._from=fe([t.from,n,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,s=this._duration,n=this._prop,o=this._from,r=this._loop,a=this._to;let l;if(this._active=o!==a&&(r||e1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[n]=this._fn(o,a,l)}wait(){const t=this._promises||(this._promises=[]);return new Promise((e,s)=>{t.push({res:e,rej:s})})}_notify(t){const e=t?"res":"rej",s=this._promises||[];for(let n=0;n{const o=t[n];if(!O(o))return;const r={};for(const a of e)r[a]=o[a];(z(o.properties)&&o.properties||[n]).forEach(a=>{(a===n||!s.has(a))&&s.set(a,r)})})}_animateOptions(t,e){const s=e.options,n=Hr(t,s);if(!n)return[];const o=this._createAnimations(n,s);return s.$shared&&Br(t.options.$animations,s).then(()=>{t.options=s},()=>{}),o}_createAnimations(t,e){const s=this._properties,n=[],o=t.$animations||(t.$animations={}),r=Object.keys(e),a=Date.now();let l;for(l=r.length-1;l>=0;--l){const c=r[l];if(c.charAt(0)==="$")continue;if(c==="options"){n.push(...this._animateOptions(t,e));continue}const h=e[c];let d=o[c];const f=s.get(c);if(d)if(f&&d.active()){d.update(f,h,a);continue}else d.cancel();if(!f||!f.duration){t[c]=h;continue}o[c]=d=new Er(f,t,c,h),n.push(d)}return n}update(t,e){if(this._properties.size===0){Object.assign(t,e);return}const s=this._createAnimations(t,e);if(s.length)return ot.add(this._chart,s),!0}}function Br(i,t){const e=[],s=Object.keys(t);for(let n=0;n0||!e&&o<0)return n.index}return null}function Ki(i,t){const{chart:e,_cachedMeta:s}=i,n=e._stacks||(e._stacks={}),{iScale:o,vScale:r,index:a}=s,l=o.axis,c=r.axis,h=jr(o,r,s),d=t.length;let f;for(let u=0;ue[s].axis===t).shift()}function Ur(i,t){return Mt(i,{active:!1,dataset:void 0,datasetIndex:t,index:t,mode:"default",type:"dataset"})}function Xr(i,t,e){return Mt(i,{active:!1,dataIndex:t,parsed:void 0,raw:void 0,element:e,index:t,mode:"default",type:"data"})}function Bt(i,t){const e=i.controller.index,s=i.vScale&&i.vScale.axis;if(s){t=t||i._parsed;for(const n of t){const o=n._stacks;if(!o||o[s]===void 0||o[s][e]===void 0)return;delete o[s][e],o[s]._visualValues!==void 0&&o[s]._visualValues[e]!==void 0&&delete o[s]._visualValues[e]}}}const Be=i=>i==="reset"||i==="none",qi=(i,t)=>t?i:Object.assign({},i),Kr=(i,t,e)=>i&&!t.hidden&&t._stacked&&{keys:mn(e,!0),values:null};class pi{static defaults={};static datasetElementType=null;static dataElementType=null;constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=Ui(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Bt(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,s=this.getDataset(),n=(d,f,u,p)=>d==="x"?f:d==="r"?p:u,o=e.xAxisID=D(s.xAxisID,Ee(t,"x")),r=e.yAxisID=D(s.yAxisID,Ee(t,"y")),a=e.rAxisID=D(s.rAxisID,Ee(t,"r")),l=e.indexAxis,c=e.iAxisID=n(l,o,r,a),h=e.vAxisID=n(l,r,o,a);e.xScale=this.getScaleForId(o),e.yScale=this.getScaleForId(r),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(c),e.vScale=this.getScaleForId(h)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&Ci(this._data,this),t._stacked&&Bt(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),s=this._data;if(O(e)){const n=this._cachedMeta;this._data=Nr(e,n)}else if(s!==e){if(s){Ci(s,this);const n=this._cachedMeta;Bt(n),n._parsed=[]}e&&Object.isExtensible(e)&&Ao(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,s=this.getDataset();let n=!1;this._dataCheck();const o=e._stacked;e._stacked=Ui(e.vScale,e),e.stack!==s.stack&&(n=!0,Bt(e),e.stack=s.stack),this._resyncElements(t),(n||o!==e._stacked)&&Ki(this,e._parsed)}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),s=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(s,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:s,_data:n}=this,{iScale:o,_stacked:r}=s,a=o.axis;let l=t===0&&e===n.length?!0:s._sorted,c=t>0&&s._parsed[t-1],h,d,f;if(this._parsing===!1)s._parsed=n,s._sorted=!0,f=n;else{z(n[t])?f=this.parseArrayData(s,n,t,e):O(n[t])?f=this.parseObjectData(s,n,t,e):f=this.parsePrimitiveData(s,n,t,e);const u=()=>d[a]===null||c&&d[a]g||d=0;--f)if(!p()){this.updateRangeFromParsed(c,t,u,l);break}}return c}getAllParsedValues(t){const e=this._cachedMeta._parsed,s=[];let n,o,r;for(n=0,o=e.length;n=0&&tthis.getContext(s,n,e),g=c.resolveNamedOptions(f,u,p,d);return g.$shared&&(g.$shared=l,o[r]=Object.freeze(qi(g,l))),g}_resolveAnimations(t,e,s){const n=this.chart,o=this._cachedDataOpts,r=`animation-${e}`,a=o[r];if(a)return a;let l;if(n.options.animation!==!1){const h=this.chart.config,d=h.datasetAnimationScopeKeys(this._type,e),f=h.getOptionScopes(this.getDataset(),d);l=h.createResolver(f,this.getContext(t,s,e))}const c=new pn(n,l&&l.animations);return l&&l._cacheable&&(o[r]=Object.freeze(c)),c}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Be(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const s=this.resolveDataElementOptions(t,e),n=this._sharedOptions,o=this.getSharedOptions(s),r=this.includeOptions(e,o)||o!==n;return this.updateSharedOptions(o,e,s),{sharedOptions:o,includeOptions:r}}updateElement(t,e,s,n){Be(n)?Object.assign(t,s):this._resolveAnimations(e,n).update(t,s)}updateSharedOptions(t,e,s){t&&!Be(e)&&this._resolveAnimations(void 0,e).update(t,s)}_setStyle(t,e,s,n){t.active=n;const o=this.getStyle(e,n);this._resolveAnimations(e,s,n).update(t,{options:!n&&this.getSharedOptions(o)||o})}removeHoverStyle(t,e,s){this._setStyle(t,s,"active",!1)}setHoverStyle(t,e,s){this._setStyle(t,s,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,s=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const n=s.length,o=e.length,r=Math.min(o,n);r&&this.parse(0,r),o>n?this._insertElements(n,o-n,t):o{for(c.length+=e,a=c.length-1;a>=r;a--)c[a]=c[a-e]};for(l(o),a=t;an-o))}return i._cache.$bar}function Gr(i){const t=i.iScale,e=qr(t,i.type);let s=t._length,n,o,r,a;const l=()=>{r===32767||r===-32768||(Qt(a)&&(s=Math.min(s,Math.abs(r-a)||s)),a=r)};for(n=0,o=e.length;n0?n[i-1]:null,a=iMath.abs(a)&&(l=a,c=r),t[e.axis]=c,t._custom={barStart:l,barEnd:c,start:n,end:o,min:r,max:a}}function bn(i,t,e,s){return z(i)?Jr(i,t,e,s):t[e.axis]=e.parse(i,s),t}function Gi(i,t,e,s){const n=i.iScale,o=i.vScale,r=n.getLabels(),a=n===o,l=[];let c,h,d,f;for(c=e,h=e+s;c=e?1:-1)}function ea(i){let t,e,s,n,o;return i.horizontal?(t=i.base>i.x,e="left",s="right"):(t=i.baseh.controller.options.grouped),o=s.options.stacked,r=[],a=this._cachedMeta.controller.getParsed(e),l=a&&a[s.axis],c=h=>{const d=h._parsed.find(u=>u[s.axis]===l),f=d&&d[h.vScale.axis];if(A(f)||isNaN(f))return!0};for(const h of n)if(!(e!==void 0&&c(h))&&((o===!1||r.indexOf(h.stack)===-1||o===void 0&&h.stack===void 0)&&r.push(h.stack),h.index===t))break;return r.length||r.push(void 0),r}_getStackCount(t){return this._getStacks(void 0,t).length}_getStackIndex(t,e,s){const n=this._getStacks(t,s),o=e!==void 0?n.indexOf(e):-1;return o===-1?n.length-1:o}_getRuler(){const t=this.options,e=this._cachedMeta,s=e.iScale,n=[];let o,r;for(o=0,r=e.data.length;o0&&this.getParsed(e-1);for(let x=0;x=_){w.skip=!0;continue}const k=this.getParsed(x),M=A(k[u]),C=w[f]=r.getPixelForValue(k[f],x),P=w[u]=o||M?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,k,l):k[u],x);w.skip=isNaN(C)||isNaN(P)||M,w.stop=x>0&&Math.abs(k[f]-v[f])>m,g&&(w.parsed=k,w.raw=c.data[x]),d&&(w.options=h||this.resolveDataElementOptions(x,S.active?"active":n)),b||this.updateElement(S,x,w,n),v=k}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,s=e.options&&e.options.borderWidth||0,n=t.data||[];if(!n.length)return s;const o=n[0].size(this.resolveDataElementOptions(0)),r=n[n.length-1].size(this.resolveDataElementOptions(n.length-1));return Math.max(s,o,r)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}}function _t(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class mi{static override(t){Object.assign(mi.prototype,t)}options;constructor(t){this.options=t||{}}init(){}formats(){return _t()}parse(){return _t()}format(){return _t()}add(){return _t()}diff(){return _t()}startOf(){return _t()}endOf(){return _t()}}var aa={_date:mi};function la(i,t,e,s){const{controller:n,data:o,_sorted:r}=i,a=n._cachedMeta.iScale;if(a&&t===a.axis&&t!=="r"&&r&&o.length){const l=a._reversePixels?To:kt;if(s){if(n._sharedOptions){const c=o[0],h=typeof c.getRange=="function"&&c.getRange(t);if(h){const d=l(o,t,e-h),f=l(o,t,e+h);return{lo:d.lo,hi:f.hi}}}}else return l(o,t,e)}return{lo:0,hi:o.length-1}}function ne(i,t,e,s,n){const o=i.getSortedVisibleDatasetMetas(),r=e[t];for(let a=0,l=o.length;a{l[r]&&l[r](t[e],n)&&(o.push({element:l,datasetIndex:c,index:h}),a=a||l.inRange(t.x,t.y,n))}),s&&!a?[]:o}var fa={evaluateInteractionItems:ne,modes:{index(i,t,e,s){const n=xt(t,i),o=e.axis||"x",r=e.includeInvisible||!1,a=e.intersect?We(i,n,o,s,r):Ve(i,n,o,!1,s,r),l=[];return a.length?(i.getSortedVisibleDatasetMetas().forEach(c=>{const h=a[0].index,d=c.data[h];d&&!d.skip&&l.push({element:d,datasetIndex:c.index,index:h})}),l):[]},dataset(i,t,e,s){const n=xt(t,i),o=e.axis||"xy",r=e.includeInvisible||!1;let a=e.intersect?We(i,n,o,s,r):Ve(i,n,o,!1,s,r);if(a.length>0){const l=a[0].datasetIndex,c=i.getDatasetMeta(l).data;a=[];for(let h=0;he.pos===t)}function ts(i,t){return i.filter(e=>_n.indexOf(e.pos)===-1&&e.box.axis===t)}function Wt(i,t){return i.sort((e,s)=>{const n=t?s:e,o=t?e:s;return n.weight===o.weight?n.index-o.index:n.weight-o.weight})}function ua(i){const t=[];let e,s,n,o,r,a;for(e=0,s=(i||[]).length;ec.box.fullSize),!0),s=Wt(Ht(t,"left"),!0),n=Wt(Ht(t,"right")),o=Wt(Ht(t,"top"),!0),r=Wt(Ht(t,"bottom")),a=ts(t,"x"),l=ts(t,"y");return{fullSize:e,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(r).concat(a),chartArea:Ht(t,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(r).concat(a)}}function es(i,t,e,s){return Math.max(i[e],t[e])+Math.max(i[s],t[s])}function xn(i,t){i.top=Math.max(i.top,t.top),i.left=Math.max(i.left,t.left),i.bottom=Math.max(i.bottom,t.bottom),i.right=Math.max(i.right,t.right)}function ba(i,t,e,s){const{pos:n,box:o}=e,r=i.maxPadding;if(!O(n)){e.size&&(i[n]-=e.size);const d=s[e.stack]||{size:0,count:1};d.size=Math.max(d.size,e.horizontal?o.height:o.width),e.size=d.size/d.count,i[n]+=e.size}o.getPadding&&xn(r,o.getPadding());const a=Math.max(0,t.outerWidth-es(r,i,"left","right")),l=Math.max(0,t.outerHeight-es(r,i,"top","bottom")),c=a!==i.w,h=l!==i.h;return i.w=a,i.h=l,e.horizontal?{same:c,other:h}:{same:h,other:c}}function _a(i){const t=i.maxPadding;function e(s){const n=Math.max(t[s]-i[s],0);return i[s]+=n,n}i.y+=e("top"),i.x+=e("left"),e("right"),e("bottom")}function xa(i,t){const e=t.maxPadding;function s(n){const o={left:0,top:0,right:0,bottom:0};return n.forEach(r=>{o[r]=Math.max(t[r],e[r])}),o}return s(i?["left","right"]:["top","bottom"])}function $t(i,t,e,s){const n=[];let o,r,a,l,c,h;for(o=0,r=i.length,c=0;o{typeof g.beforeLayout=="function"&&g.beforeLayout()});const h=l.reduce((g,m)=>m.box.options&&m.box.options.display===!1?g:g+1,0)||1,d=Object.freeze({outerWidth:t,outerHeight:e,padding:n,availableWidth:o,availableHeight:r,vBoxMaxWidth:o/2/h,hBoxMaxHeight:r/2}),f=Object.assign({},n);xn(f,q(s));const u=Object.assign({maxPadding:f,w:o,h:r,x:n.left,y:n.top},n),p=pa(l.concat(c),d);$t(a.fullSize,u,d,p),$t(l,u,d,p),$t(c,u,d,p)&&$t(l,u,d,p),_a(u),is(a.leftAndTop,u,d,p),u.x+=u.w,u.y+=u.h,is(a.rightAndBottom,u,d,p),i.chartArea={left:u.left,top:u.top,right:u.left+u.w,bottom:u.top+u.h,height:u.h,width:u.w},L(a.chartArea,g=>{const m=g.box;Object.assign(m,i.chartArea),m.update(u.w,u.h,{left:0,top:0,right:0,bottom:0})})}};class yn{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,s){}removeEventListener(t,e,s){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,s,n){return e=Math.max(0,e||t.width),s=s||t.height,{width:e,height:Math.max(0,n?Math.floor(e/n):s)}}isAttached(t){return!0}updateConfig(t){}}class ya extends yn{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const ke="$chartjs",va={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},ss=i=>i===null||i==="";function ka(i,t){const e=i.style,s=i.getAttribute("height"),n=i.getAttribute("width");if(i[ke]={initial:{height:s,width:n,style:{display:e.display,height:e.height,width:e.width}}},e.display=e.display||"block",e.boxSizing=e.boxSizing||"border-box",ss(n)){const o=Hi(i,"width");o!==void 0&&(i.width=o)}if(ss(s))if(i.style.height==="")i.height=i.width/(t||2);else{const o=Hi(i,"height");o!==void 0&&(i.height=o)}return i}const vn=Sr?{passive:!0}:!1;function wa(i,t,e){i&&i.addEventListener(t,e,vn)}function Sa(i,t,e){i&&i.canvas&&i.canvas.removeEventListener(t,e,vn)}function Ma(i,t){const e=va[i.type]||i.type,{x:s,y:n}=xt(i,t);return{type:e,chart:t,native:i,x:s!==void 0?s:null,y:n!==void 0?n:null}}function Ce(i,t){for(const e of i)if(e===t||e.contains(t))return!0}function Pa(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||Ce(a.addedNodes,s),r=r&&!Ce(a.removedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}function Da(i,t,e){const s=i.canvas,n=new MutationObserver(o=>{let r=!1;for(const a of o)r=r||Ce(a.removedNodes,s),r=r&&!Ce(a.addedNodes,s);r&&e()});return n.observe(document,{childList:!0,subtree:!0}),n}const ie=new Map;let ns=0;function kn(){const i=window.devicePixelRatio;i!==ns&&(ns=i,ie.forEach((t,e)=>{e.currentDevicePixelRatio!==i&&t()}))}function Oa(i,t){ie.size||window.addEventListener("resize",kn),ie.set(i,t)}function Ca(i){ie.delete(i),ie.size||window.removeEventListener("resize",kn)}function Ta(i,t,e){const s=i.canvas,n=s&&gi(s);if(!n)return;const o=Zs((a,l)=>{const c=n.clientWidth;e(a,l),c{const l=a[0],c=l.contentRect.width,h=l.contentRect.height;c===0&&h===0||o(c,h)});return r.observe(n),Oa(i,o),r}function Ne(i,t,e){e&&e.disconnect(),t==="resize"&&Ca(i)}function La(i,t,e){const s=i.canvas,n=Zs(o=>{i.ctx!==null&&e(Ma(o,i))},i);return wa(s,t,n),n}class Aa extends yn{acquireContext(t,e){const s=t&&t.getContext&&t.getContext("2d");return s&&s.canvas===t?(ka(t,e),s):null}releaseContext(t){const e=t.canvas;if(!e[ke])return!1;const s=e[ke].initial;["height","width"].forEach(o=>{const r=s[o];A(r)?e.removeAttribute(o):e.setAttribute(o,r)});const n=s.style||{};return Object.keys(n).forEach(o=>{e.style[o]=n[o]}),e.width=e.width,delete e[ke],!0}addEventListener(t,e,s){this.removeEventListener(t,e);const n=t.$proxies||(t.$proxies={}),r={attach:Pa,detach:Da,resize:Ta}[e]||La;n[e]=r(t,e,s)}removeEventListener(t,e){const s=t.$proxies||(t.$proxies={}),n=s[e];if(!n)return;({attach:Ne,detach:Ne,resize:Ne}[e]||Sa)(t,e,n),s[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,s,n){return wr(t,e,s,n)}isAttached(t){const e=t&&gi(t);return!!(e&&e.isConnected)}}function Fa(i){return!ui()||typeof OffscreenCanvas<"u"&&i instanceof OffscreenCanvas?ya:Aa}class ut{static defaults={};static defaultRoutes=void 0;x;y;active=!1;options;$animations;tooltipPosition(t){const{x:e,y:s}=this.getProps(["x","y"],t);return{x:e,y:s}}hasValue(){return Jt(this.x)&&Jt(this.y)}getProps(t,e){const s=this.$animations;if(!e||!s)return this;const n={};return t.forEach(o=>{n[o]=s[o]&&s[o].active()?s[o]._to:this[o]}),n}}function Ia(i,t){const e=i.options.ticks,s=Ra(i),n=Math.min(e.maxTicksLimit||s,s),o=e.major.enabled?Ea(t):[],r=o.length,a=o[0],l=o[r-1],c=[];if(r>n)return Ba(t,c,o,r/n),c;const h=za(o,t,n);if(r>0){let d,f;const u=r>1?Math.round((l-a)/(r-1)):null;for(me(t,c,h,A(u)?0:a-u,a),d=0,f=r-1;dn)return l}return Math.max(n,1)}function Ea(i){const t=[];let e,s;for(e=0,s=i.length;ei==="left"?"right":i==="right"?"left":i,os=(i,t,e)=>t==="top"||t==="left"?i[t]+e:i[t]-e,rs=(i,t)=>Math.min(t||i,i);function as(i,t){const e=[],s=i.length/t,n=i.length;let o=0;for(;or+a)))return l}function Na(i,t){L(i,e=>{const s=e.gc,n=s.length/2;let o;if(n>t){for(o=0;os?s:e,s=n&&e>s?e:s,{min:J(e,J(s,e)),max:J(s,J(e,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){F(this.options.beforeUpdate,[this])}update(t,e,s){const{beginAtZero:n,grace:o,ticks:r}=this.options,a=r.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=s=Object.assign({left:0,right:0,top:0,bottom:0},s),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+s.left+s.right:this.height+s.top+s.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=er(this,o,n),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||s<=1||!this.isHorizontal()){this.labelRotation=n;return}const h=this._getLabelSizes(),d=h.widest.width,f=h.highest.height,u=Y(this.chart.width-d,0,this.maxWidth);a=t.offset?this.maxWidth/s:u/(s-1),d+6>a&&(a=u/(s-(t.offset?.5:1)),l=this.maxHeight-Vt(t.grid)-e.padding-ls(t.title,this.chart.options.font),c=Math.sqrt(d*d+f*f),r=Po(Math.min(Math.asin(Y((h.highest.height+6)/a,-1,1)),Math.asin(Y(l/c,-1,1))-Math.asin(Y(f/c,-1,1)))),r=Math.max(n,Math.min(o,r))),this.labelRotation=r}afterCalculateLabelRotation(){F(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){F(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:s,title:n,grid:o}}=this,r=this._isVisible(),a=this.isHorizontal();if(r){const l=ls(n,e.options.font);if(a?(t.width=this.maxWidth,t.height=Vt(o)+l):(t.height=this.maxHeight,t.width=Vt(o)+l),s.display&&this.ticks.length){const{first:c,last:h,widest:d,highest:f}=this._getLabelSizes(),u=s.padding*2,p=vt(this.labelRotation),g=Math.cos(p),m=Math.sin(p);if(a){const b=s.mirror?0:m*d.width+g*f.height;t.height=Math.min(this.maxHeight,t.height+b+u)}else{const b=s.mirror?0:g*d.width+m*f.height;t.width=Math.min(this.maxWidth,t.width+b+u)}this._calculatePadding(c,h,m,g)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,s,n){const{ticks:{align:o,padding:r},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const h=this.getPixelForTick(0)-this.left,d=this.right-this.getPixelForTick(this.ticks.length-1);let f=0,u=0;l?c?(f=n*t.width,u=s*e.height):(f=s*t.height,u=n*e.width):o==="start"?u=e.width:o==="end"?f=t.width:o!=="inner"&&(f=t.width/2,u=e.width/2),this.paddingLeft=Math.max((f-h+r)*this.width/(this.width-h),0),this.paddingRight=Math.max((u-d+r)*this.width/(this.width-d),0)}else{let h=e.height/2,d=t.height/2;o==="start"?(h=0,d=t.height):o==="end"&&(h=e.height,d=0),this.paddingTop=h+r,this.paddingBottom=d+r}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){F(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return e==="top"||e==="bottom"||t==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){this.beforeTickToLabelConversion(),this.generateTickLabels(t);let e,s;for(e=0,s=t.length;e({width:r[M]||0,height:a[M]||0});return{first:k(0),last:k(e-1),widest:k(S),highest:k(w),widths:r,heights:a}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Co(this._alignToPixels?bt(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*n?a/s:l/n:l*n0}_computeGridLineItems(t){const e=this.axis,s=this.chart,n=this.options,{grid:o,position:r,border:a}=n,l=o.offset,c=this.isHorizontal(),d=this.ticks.length+(l?1:0),f=Vt(o),u=[],p=a.setContext(this.getContext()),g=p.display?p.width:0,m=g/2,b=function(B){return bt(s,B,g)};let _,y,v,x,S,w,k,M,C,P,T,N;if(r==="top")_=b(this.bottom),w=this.bottom-f,M=_-m,P=b(t.top)+m,N=t.bottom;else if(r==="bottom")_=b(this.top),P=t.top,N=b(t.bottom)-m,w=_+m,M=this.top+f;else if(r==="left")_=b(this.right),S=this.right-f,k=_-m,C=b(t.left)+m,T=t.right;else if(r==="right")_=b(this.left),C=t.left,T=b(t.right)-m,S=_+m,k=this.left+f;else if(e==="x"){if(r==="center")_=b((t.top+t.bottom)/2+.5);else if(O(r)){const B=Object.keys(r)[0],U=r[B];_=b(this.chart.scales[B].getPixelForValue(U))}P=t.top,N=t.bottom,w=_+m,M=w+f}else if(e==="y"){if(r==="center")_=b((t.left+t.right)/2);else if(O(r)){const B=Object.keys(r)[0],U=r[B];_=b(this.chart.scales[B].getPixelForValue(U))}S=_-m,k=S-f,C=t.left,T=t.right}const Q=D(n.ticks.maxTicksLimit,d),I=Math.max(1,Math.ceil(d/Q));for(y=0;y0&&(pt-=gt/2);break}ae={left:pt,top:Et,width:gt+Dt.width,height:zt+Dt.height,color:I.backdropColor}}m.push({label:v,font:M,textOffset:T,options:{rotation:g,color:U,strokeColor:oe,strokeWidth:re,textAlign:Pt,textBaseline:N,translation:[x,S],backdrop:ae}})}return m}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-vt(this.labelRotation))return t==="top"?"left":"right";let n="center";return e.align==="start"?n="left":e.align==="end"?n="right":e.align==="inner"&&(n="inner"),n}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:s,mirror:n,padding:o}}=this.options,r=this._getLabelSizes(),a=t+o,l=r.widest.width;let c,h;return e==="left"?n?(h=this.right+o,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h+=l)):(h=this.right-a,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h=this.left)):e==="right"?n?(h=this.left+o,s==="near"?c="right":s==="center"?(c="center",h-=l/2):(c="left",h-=l)):(h=this.left+a,s==="near"?c="left":s==="center"?(c="center",h+=l/2):(c="right",h=this.right)):c="right",{textAlign:c,x:h}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;if(e==="left"||e==="right")return{top:0,left:this.left,bottom:t.height,right:this.right};if(e==="top"||e==="bottom")return{top:this.top,left:0,bottom:this.bottom,right:t.width}}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:s,top:n,width:o,height:r}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(s,n,o,r),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const n=this.ticks.findIndex(o=>o.value===t);return n>=0?e.setContext(this.getContext(n)).lineWidth:0}drawGrid(t){const e=this.options.grid,s=this.ctx,n=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let o,r;const a=(l,c,h)=>{!h.width||!h.color||(s.save(),s.lineWidth=h.width,s.strokeStyle=h.color,s.setLineDash(h.borderDash||[]),s.lineDashOffset=h.borderDashOffset,s.beginPath(),s.moveTo(l.x,l.y),s.lineTo(c.x,c.y),s.stroke(),s.restore())};if(e.display)for(o=0,r=n.length;o{this.draw(o)}}]:[{z:s,draw:o=>{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:n,draw:()=>{this.drawBorder()}},{z:e,draw:o=>{this.drawLabels(o)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),s=this.axis+"AxisID",n=[];let o,r;for(o=0,r=e.length;o{const s=e.split("."),n=s.pop(),o=[i].concat(s).join("."),r=t[e].split("."),a=r.pop(),l=r.join(".");R.route(o,n,l,a)})}function qa(i){return"id"in i&&"defaults"in i}class Ga{constructor(){this.controllers=new be(pi,"datasets",!0),this.elements=new be(ut,"elements"),this.plugins=new be(Object,"plugins"),this.scales=new be(Rt,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,s){[...e].forEach(n=>{const o=s||this._getRegistryForType(n);s||o.isForType(n)||o===this.plugins&&n.id?this._exec(t,o,n):L(n,r=>{const a=s||this._getRegistryForType(r);this._exec(t,a,r)})})}_exec(t,e,s){const n=ri(t);F(s["before"+n],[],s),e[t](s),F(s["after"+n],[],s)}_getRegistryForType(t){for(let e=0;eo.filter(a=>!r.some(l=>a.plugin.id===l.plugin.id));this._notify(n(e,s),t,"stop"),this._notify(n(s,e),t,"start")}}function Qa(i){const t={},e=[],s=Object.keys(et.plugins.items);for(let o=0;o1&&cs(i[0].toLowerCase());if(s)return s}throw new Error(`Cannot determine type of '${i}' axis. Please provide 'axis' or 'position' option.`)}function hs(i,t,e){if(e[t+"AxisID"]===i)return{axis:t}}function ol(i,t){if(t.data&&t.data.datasets){const e=t.data.datasets.filter(s=>s.xAxisID===i||s.yAxisID===i);if(e.length)return hs(i,"x",e[0])||hs(i,"y",e[0])}return{}}function rl(i,t){const e=St[i.type]||{scales:{}},s=t.scales||{},n=Je(i.type,t),o=Object.create(null);return Object.keys(s).forEach(r=>{const a=s[r];if(!O(a))return console.error(`Invalid scale configuration for scale: ${r}`);if(a._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${r}`);const l=ti(r,a,ol(r,i),R.scales[a.type]),c=sl(l,n),h=e.scales||{};o[r]=Ut(Object.create(null),[{axis:l},a,h[l],h[c]])}),i.data.datasets.forEach(r=>{const a=r.type||i.type,l=r.indexAxis||Je(a,t),h=(St[a]||{}).scales||{};Object.keys(h).forEach(d=>{const f=il(d,l),u=r[f+"AxisID"]||f;o[u]=o[u]||Object.create(null),Ut(o[u],[{axis:f},s[u],h[d]])})}),Object.keys(o).forEach(r=>{const a=o[r];Ut(a,[R.scales[a.type],R.scale])}),o}function wn(i){const t=i.options||(i.options={});t.plugins=D(t.plugins,{}),t.scales=rl(i,t)}function Sn(i){return i=i||{},i.datasets=i.datasets||[],i.labels=i.labels||[],i}function al(i){return i=i||{},i.data=Sn(i.data),wn(i),i}const ds=new Map,Mn=new Set;function _e(i,t){let e=ds.get(i);return e||(e=t(),ds.set(i,e),Mn.add(e)),e}const Nt=(i,t,e)=>{const s=At(t,e);s!==void 0&&i.add(s)};class ll{constructor(t){this._config=al(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=Sn(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),wn(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return _e(t,()=>[[`datasets.${t}`,""]])}datasetAnimationScopeKeys(t,e){return _e(`${t}.transition.${e}`,()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]])}datasetElementScopeKeys(t,e){return _e(`${t}-${e}`,()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]])}pluginScopeKeys(t){const e=t.id,s=this.type;return _e(`${s}-plugin-${e}`,()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]])}_cachedScopes(t,e){const s=this._scopeCache;let n=s.get(t);return(!n||e)&&(n=new Map,s.set(t,n)),n}getOptionScopes(t,e,s){const{options:n,type:o}=this,r=this._cachedScopes(t,s),a=r.get(e);if(a)return a;const l=new Set;e.forEach(h=>{t&&(l.add(t),h.forEach(d=>Nt(l,t,d))),h.forEach(d=>Nt(l,n,d)),h.forEach(d=>Nt(l,St[o]||{},d)),h.forEach(d=>Nt(l,R,d)),h.forEach(d=>Nt(l,Ze,d))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),Mn.has(e)&&r.set(e,c),c}chartOptionScopes(){const{options:t,type:e}=this;return[t,St[e]||{},R.datasets[e]||{},{type:e},R,Ze]}resolveNamedOptions(t,e,s,n=[""]){const o={$shared:!0},{resolver:r,subPrefixes:a}=fs(this._resolverCache,t,n);let l=r;if(hl(r,e)){o.$shared=!1,s=ft(s)?s():s;const c=this.createResolver(t,s,a);l=Ft(r,s,c)}for(const c of e)o[c]=l[c];return o}createResolver(t,e,s=[""],n){const{resolver:o}=fs(this._resolverCache,t,s);return O(e)?Ft(o,e,void 0,n):o}}function fs(i,t,e){let s=i.get(t);s||(s=new Map,i.set(t,s));const n=e.join();let o=s.get(n);return o||(o={resolver:hi(t,e),subPrefixes:e.filter(a=>!a.toLowerCase().includes("hover"))},s.set(n,o)),o}const cl=i=>O(i)&&Object.getOwnPropertyNames(i).some(t=>ft(i[t]));function hl(i,t){const{isScriptable:e,isIndexable:s}=on(i);for(const n of t){const o=e(n),r=s(n),a=(r||o)&&i[n];if(o&&(ft(a)||cl(a))||r&&z(a))return!0}return!1}var dl="4.4.4";const fl=["top","bottom","left","right","chartArea"];function us(i,t){return i==="top"||i==="bottom"||fl.indexOf(i)===-1&&t==="x"}function gs(i,t){return function(e,s){return e[i]===s[i]?e[t]-s[t]:e[i]-s[i]}}function ps(i){const t=i.chart,e=t.options.animation;t.notifyPlugins("afterRender"),F(e&&e.onComplete,[i],t)}function ul(i){const t=i.chart,e=t.options.animation;F(e&&e.onProgress,[i],t)}function Pn(i){return ui()&&typeof i=="string"?i=document.getElementById(i):i&&i.length&&(i=i[0]),i&&i.canvas&&(i=i.canvas),i}const we={},ms=i=>{const t=Pn(i);return Object.values(we).filter(e=>e.canvas===t).pop()};function gl(i,t,e){const s=Object.keys(i);for(const n of s){const o=+n;if(o>=t){const r=i[n];delete i[n],(e>0||o>t)&&(i[o+e]=r)}}}function pl(i,t,e,s){return!e||i.type==="mouseout"?null:s?t:i}function xe(i,t,e){return i.options.clip?i[e]:t[e]}function ml(i,t){const{xScale:e,yScale:s}=i;return e&&s?{left:xe(e,t,"left"),right:xe(e,t,"right"),top:xe(s,t,"top"),bottom:xe(s,t,"bottom")}:t}let bi=class{static defaults=R;static instances=we;static overrides=St;static registry=et;static version=dl;static getChart=ms;static register(...t){et.add(...t),bs()}static unregister(...t){et.remove(...t),bs()}constructor(t,e){const s=this.config=new ll(e),n=Pn(t),o=ms(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const r=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||Fa(n)),this.platform.updateConfig(s);const a=this.platform.acquireContext(n,r.aspectRatio),l=a&&a.canvas,c=l&&l.height,h=l&&l.width;if(this.id=go(),this.ctx=a,this.canvas=l,this.width=h,this.height=c,this._options=r,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new Za,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=Fo(d=>this.update(d),r.resizeDelay||0),this._dataChanges=[],we[this.id]=this,!a||!l){console.error("Failed to create chart: can't acquire context from the given item");return}ot.listen(this,"complete",ps),ot.listen(this,"progress",ul),this._initialize(),this.attached&&this.update()}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:s,height:n,_aspectRatio:o}=this;return A(t)?e&&o?o:n?s/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return et}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():Bi(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Ri(this.canvas,this.ctx),this}stop(){return ot.stop(this),this}resize(t,e){ot.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const s=this.options,n=this.canvas,o=s.maintainAspectRatio&&this.aspectRatio,r=this.platform.getMaximumSize(n,t,e,o),a=s.devicePixelRatio||this.platform.getDevicePixelRatio(),l=this.width?"resize":"attach";this.width=r.width,this.height=r.height,this._aspectRatio=this.aspectRatio,Bi(this,a,!0)&&(this.notifyPlugins("resize",{size:r}),F(s.onResize,[this,r],this),this.attached&&this._doResize(l)&&this.render())}ensureScalesHaveIDs(){const e=this.options.scales||{};L(e,(s,n)=>{s.id=n})}buildOrUpdateScales(){const t=this.options,e=t.scales,s=this.scales,n=Object.keys(s).reduce((r,a)=>(r[a]=!1,r),{});let o=[];e&&(o=o.concat(Object.keys(e).map(r=>{const a=e[r],l=ti(r,a),c=l==="r",h=l==="x";return{options:a,dposition:c?"chartArea":h?"bottom":"left",dtype:c?"radialLinear":h?"category":"linear"}}))),L(o,r=>{const a=r.options,l=a.id,c=ti(l,a),h=D(a.type,r.dtype);(a.position===void 0||us(a.position,c)!==us(r.dposition))&&(a.position=r.dposition),n[l]=!0;let d=null;if(l in s&&s[l].type===h)d=s[l];else{const f=et.getScale(h);d=new f({id:l,type:h,ctx:this.ctx,chart:this}),s[d.id]=d}d.init(a,t)}),L(n,(r,a)=>{r||delete s[a]}),L(s,r=>{K.configure(this,r,r.options),K.addBox(this,r)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,s=t.length;if(t.sort((n,o)=>n.index-o.index),s>e){for(let n=e;ne.length&&delete this._stacks,t.forEach((s,n)=>{e.filter(o=>o===s._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let s,n;for(this._removeUnreferencedMetasets(),s=0,n=e.length;s{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const s=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),n=this._animationsDisabled=!s.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const o=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let r=0;for(let c=0,h=this.data.datasets.length;c{c.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(gs("z","_idx"));const{_active:a,_lastEvent:l}=this;l?this._eventHandler(l,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){L(this.scales,t=>{K.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),s=new Set(t.events);(!Mi(e,s)||!!this._responsiveListeners!==t.responsive)&&(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:s,start:n,count:o}of e){const r=s==="_removeElements"?-o:o;gl(t,n,r)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,s=o=>new Set(t.filter(r=>r[0]===o).map((r,a)=>a+","+r.splice(1).join(","))),n=s(0);for(let o=1;oo.split(",")).map(o=>({method:o[1],start:+o[2],count:+o[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;K.update(this,this.width,this.height,t);const e=this.chartArea,s=e.width<=0||e.height<=0;this._layers=[],L(this.boxes,n=>{s&&n.position==="chartArea"||(n.configure&&n.configure(),this._layers.push(...n._layers()))},this),this._layers.forEach((n,o)=>{n._idx=o}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,s=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,s=t._clip,n=!s.disabled,o=ml(t,this.chartArea),r={meta:t,index:t.index,cancelable:!0};this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(n&&Te(e,{left:s.left===!1?0:o.left-s.left,right:s.right===!1?this.width:o.right+s.right,top:s.top===!1?0:o.top-s.top,bottom:s.bottom===!1?this.height:o.bottom+s.bottom}),t.controller.draw(),n&&Le(e),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(t){return te(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,s,n){const o=fa.modes[e];return typeof o=="function"?o(this,t,s,n):[]}getDatasetMeta(t){const e=this.data.datasets[t],s=this._metasets;let n=s.filter(o=>o&&o._dataset===e).pop();return n||(n={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},s.push(n)),n}getContext(){return this.$context||(this.$context=Mt(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const s=this.getDatasetMeta(t);return typeof s.hidden=="boolean"?!s.hidden:!e.hidden}setDatasetVisibility(t,e){const s=this.getDatasetMeta(t);s.hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,s){const n=s?"show":"hide",o=this.getDatasetMeta(t),r=o.controller._resolveAnimations(void 0,n);Qt(e)?(o.data[e].hidden=!s,this.update()):(this.setDatasetVisibility(t,s),r.update(o,{visible:s}),this.update(a=>a.datasetIndex===t?n:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),ot.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,o,r),t[o]=r},n=(o,r,a)=>{o.offsetX=r,o.offsetY=a,this._eventHandler(o)};L(this.options.events,o=>s(o,n))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,s=(l,c)=>{e.addEventListener(this,l,c),t[l]=c},n=(l,c)=>{t[l]&&(e.removeEventListener(this,l,c),delete t[l])},o=(l,c)=>{this.canvas&&this.resize(l,c)};let r;const a=()=>{n("attach",a),this.attached=!0,this.resize(),s("resize",o),s("detach",r)};r=()=>{this.attached=!1,n("resize",o),this._stop(),this._resize(0,0),s("attach",a)},e.isAttached(this.canvas)?a():r()}unbindEvents(){L(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},L(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,s){const n=s?"set":"remove";let o,r,a,l;for(e==="dataset"&&(o=this.getDatasetMeta(t[0].datasetIndex),o.controller["_"+n+"DatasetHoverStyle"]()),a=0,l=t.length;a{const a=this.getDatasetMeta(o);if(!a)throw new Error("No dataset found at index "+o);return{datasetIndex:o,element:a.data[r],index:r}});!Se(s,e)&&(this._active=s,this._lastEvent=null,this._updateHoverStyles(s,e))}notifyPlugins(t,e,s){return this._plugins.notify(this,t,e,s)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,s){const n=this.options.hover,o=(l,c)=>l.filter(h=>!c.some(d=>h.datasetIndex===d.datasetIndex&&h.index===d.index)),r=o(e,t),a=s?t:o(t,e);r.length&&this.updateHoverStyle(r,n.mode,!1),a.length&&n.mode&&this.updateHoverStyle(a,n.mode,!0)}_eventHandler(t,e){const s={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},n=r=>(r.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",s,n)===!1)return;const o=this._handleEvent(t,e,s.inChartArea);return s.cancelable=!1,this.notifyPlugins("afterEvent",s,n),(o||s.changed)&&this.render(),this}_handleEvent(t,e,s){const{_active:n=[],options:o}=this,r=e,a=this._getActiveElements(t,n,s,r),l=yo(t),c=pl(t,this._lastEvent,s,l);s&&(this._lastEvent=null,F(o.onHover,[t,a,this],this),l&&F(o.onClick,[t,a,this],this));const h=!Se(a,n);return(h||e)&&(this._active=a,this._updateHoverStyles(a,n,e)),this._lastEvent=c,h}_getActiveElements(t,e,s,n){if(t.type==="mouseout")return[];if(!s)return e;const o=this.options.hover;return this.getElementsAtEventForMode(t,o.mode,o,n)}};function bs(){return L(bi.instances,i=>i._plugins.invalidate())}function Dn(i,t,e=t){i.lineCap=D(e.borderCapStyle,t.borderCapStyle),i.setLineDash(D(e.borderDash,t.borderDash)),i.lineDashOffset=D(e.borderDashOffset,t.borderDashOffset),i.lineJoin=D(e.borderJoinStyle,t.borderJoinStyle),i.lineWidth=D(e.borderWidth,t.borderWidth),i.strokeStyle=D(e.borderColor,t.borderColor)}function bl(i,t,e){i.lineTo(e.x,e.y)}function _l(i){return i.stepped?Uo:i.tension||i.cubicInterpolationMode==="monotone"?Xo:bl}function On(i,t,e={}){const s=i.length,{start:n=0,end:o=s-1}=e,{start:r,end:a}=t,l=Math.max(n,r),c=Math.min(o,a),h=na&&o>a;return{count:s,start:l,loop:t.loop,ilen:c(r+(c?a-v:v))%o,y=()=>{g!==m&&(i.lineTo(h,m),i.lineTo(h,g),i.lineTo(h,b))};for(l&&(u=n[_(0)],i.moveTo(u.x,u.y)),f=0;f<=a;++f){if(u=n[_(f)],u.skip)continue;const v=u.x,x=u.y,S=v|0;S===p?(xm&&(m=x),h=(d*h+v)/++d):(y(),i.lineTo(v,x),p=S,d=0,g=m=x),b=x}y()}function ei(i){const t=i.options,e=t.borderDash&&t.borderDash.length;return!i._decimated&&!i._loop&&!t.tension&&t.cubicInterpolationMode!=="monotone"&&!t.stepped&&!e?yl:xl}function vl(i){return i.stepped?Mr:i.tension||i.cubicInterpolationMode==="monotone"?Pr:yt}function kl(i,t,e,s){let n=t._path;n||(n=t._path=new Path2D,t.path(n,e,s)&&n.closePath()),Dn(i,t.options),i.stroke(n)}function wl(i,t,e,s){const{segments:n,options:o}=t,r=ei(t);for(const a of n)Dn(i,o,a.style),i.beginPath(),r(i,t,a,{start:e,end:e+s-1})&&i.closePath(),i.stroke()}const Sl=typeof Path2D=="function";function Ml(i,t,e,s){Sl&&!t.options.segment?kl(i,t,e,s):wl(i,t,e,s)}class _i extends ut{static id="line";static defaults={borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderWidth:3,capBezierPoints:!0,cubicInterpolationMode:"default",fill:!1,spanGaps:!1,stepped:!1,tension:0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};static descriptors={_scriptable:!0,_indexable:t=>t!=="borderDash"&&t!=="fill"};constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const s=this.options;if((s.tension||s.cubicInterpolationMode==="monotone")&&!s.stepped&&!this._pointsUpdated){const n=s.spanGaps?this._loop:this._fullLoop;br(this._points,s,t,n,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=Ar(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,s=t.length;return s&&e[t[s-1].end]}interpolate(t,e){const s=this.options,n=t[e],o=this.points,r=gn(this,{property:e,start:n,end:n});if(!r.length)return;const a=[],l=vl(s);let c,h;for(c=0,h=r.length;c{a=xi(r,a,n);const l=n[r],c=n[a];s!==null?(o.push({x:l.x,y:s}),o.push({x:c.x,y:s})):e!==null&&(o.push({x:e,y:l.y}),o.push({x:e,y:c.y}))}),o}function xi(i,t,e){for(;t>i;t--){const s=e[t];if(!isNaN(s.x)&&!isNaN(s.y))break}return t}function xs(i,t,e,s){return i&&t?s(i[e],t[e]):i?i[e]:t?t[e]:0}function Tn(i,t){let e=[],s=!1;return z(i)?(s=!0,e=i):e=Al(i,t),e.length?new _i({points:e,options:{tension:0},_loop:s,_fullLoop:s}):null}function ys(i){return i&&i.fill!==!1}function Fl(i,t,e){let n=i[t].fill;const o=[t];let r;if(!e)return n;for(;n!==!1&&o.indexOf(n)===-1;){if(!V(n))return n;if(r=i[n],!r)return!1;if(r.visible)return n;o.push(n),n=r.fill}return!1}function Il(i,t,e){const s=Bl(i);if(O(s))return isNaN(s.value)?!1:s;let n=parseFloat(s);return V(n)&&Math.floor(n)===n?Rl(s[0],t,n,e):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function Rl(i,t,e,s){return(i==="-"||i==="+")&&(e=t+e),e===t||e<0||e>=s?!1:e}function zl(i,t){let e=null;return i==="start"?e=t.bottom:i==="end"?e=t.top:O(i)?e=t.getPixelForValue(i.value):t.getBasePixel&&(e=t.getBasePixel()),e}function El(i,t,e){let s;return i==="start"?s=e:i==="end"?s=t.options.reverse?t.min:t.max:O(i)?s=i.value:s=t.getBaseValue(),s}function Bl(i){const t=i.options,e=t.fill;let s=D(e&&e.target,e);return s===void 0&&(s=!!t.backgroundColor),s===!1||s===null?!1:s===!0?"origin":s}function Hl(i){const{scale:t,index:e,line:s}=i,n=[],o=s.segments,r=s.points,a=Wl(t,e);a.push(Tn({x:null,y:t.bottom},s));for(let l=0;l=0;--r){const a=n[r].$filler;a&&(a.line.updateControlPoints(o,a.axis),s&&a.fill&&Ye(i.ctx,a,o))}},beforeDatasetsDraw(i,t,e){if(e.drawTime!=="beforeDatasetsDraw")return;const s=i.getSortedVisibleDatasetMetas();for(let n=s.length-1;n>=0;--n){const o=s[n].$filler;ys(o)&&Ye(i.ctx,o,i.chartArea)}},beforeDatasetDraw(i,t,e){const s=t.meta.$filler;!ys(s)||e.drawTime!=="beforeDatasetDraw"||Ye(i.ctx,s,i.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Ss=(i,t)=>{let{boxHeight:e=t,boxWidth:s=t}=i;return i.usePointStyle&&(e=Math.min(e,t),s=i.pointStyleWidth||Math.min(s,t)),{boxWidth:s,boxHeight:e,itemHeight:Math.max(t,e)}},Gl=(i,t)=>i!==null&&t!==null&&i.datasetIndex===t.datasetIndex&&i.index===t.index;class Ms extends ut{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,s){this.maxWidth=t,this.maxHeight=e,this._margins=s,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=F(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter(s=>t.filter(s,this.chart.data))),t.sort&&(e=e.sort((s,n)=>t.sort(s,n,this.chart.data))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display){this.width=this.height=0;return}const s=t.labels,n=W(s.font),o=n.size,r=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=Ss(s,o);let c,h;e.font=n.string,this.isHorizontal()?(c=this.maxWidth,h=this._fitRows(r,o,a,l)+10):(h=this.maxHeight,c=this._fitCols(r,n,a,l)+10),this.width=Math.min(c,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,s,n){const{ctx:o,maxWidth:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],h=n+a;let d=t;o.textAlign="left",o.textBaseline="middle";let f=-1,u=-h;return this.legendItems.forEach((p,g)=>{const m=s+e/2+o.measureText(p.text).width;(g===0||c[c.length-1]+m+2*a>r)&&(d+=h,c[c.length-(g>0?0:1)]=0,u+=h,f++),l[g]={left:0,top:u,row:f,width:m,height:n},c[c.length-1]+=m+a}),d}_fitCols(t,e,s,n){const{ctx:o,maxHeight:r,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],h=r-t;let d=a,f=0,u=0,p=0,g=0;return this.legendItems.forEach((m,b)=>{const{itemWidth:_,itemHeight:y}=Zl(s,e,o,m,n);b>0&&u+y+2*a>h&&(d+=f+a,c.push({width:f,height:u}),p+=f+a,g++,f=u=0),l[b]={left:p,top:u,col:g,width:_,height:y},f=Math.max(f,_),u+=y+a}),d+=f,c.push({width:f,height:u}),d}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:s,labels:{padding:n},rtl:o}}=this,r=Lt(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=H(s,this.left+n,this.right-this.lineWidths[a]);for(const c of e)a!==c.row&&(a=c.row,l=H(s,this.left+n,this.right-this.lineWidths[a])),c.top+=this.top+t+n,c.left=r.leftForLtr(r.x(l),c.width),l+=c.width+n}else{let a=0,l=H(s,this.top+t+n,this.bottom-this.columnSizes[a].height);for(const c of e)c.col!==a&&(a=c.col,l=H(s,this.top+t+n,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+n,c.left=r.leftForLtr(r.x(c.left),c.width),l+=c.height+n}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const t=this.ctx;Te(t,this),this._draw(),Le(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:s,ctx:n}=this,{align:o,labels:r}=t,a=R.color,l=Lt(t.rtl,this.left,this.width),c=W(r.font),{padding:h}=r,d=c.size,f=d/2;let u;this.drawTitle(),n.textAlign=l.textAlign("left"),n.textBaseline="middle",n.lineWidth=.5,n.font=c.string;const{boxWidth:p,boxHeight:g,itemHeight:m}=Ss(r,d),b=function(S,w,k){if(isNaN(p)||p<=0||isNaN(g)||g<0)return;n.save();const M=D(k.lineWidth,1);if(n.fillStyle=D(k.fillStyle,a),n.lineCap=D(k.lineCap,"butt"),n.lineDashOffset=D(k.lineDashOffset,0),n.lineJoin=D(k.lineJoin,"miter"),n.lineWidth=M,n.strokeStyle=D(k.strokeStyle,a),n.setLineDash(D(k.lineDash,[])),r.usePointStyle){const C={radius:g*Math.SQRT2/2,pointStyle:k.pointStyle,rotation:k.rotation,borderWidth:M},P=l.xPlus(S,p/2),T=w+f;en(n,C,P,T,r.pointStyleWidth&&p)}else{const C=w+Math.max((d-g)/2,0),P=l.leftForLtr(S,p),T=Tt(k.borderRadius);n.beginPath(),Object.values(T).some(N=>N!==0)?De(n,{x:P,y:C,w:p,h:g,radius:T}):n.rect(P,C,p,g),n.fill(),M!==0&&n.stroke()}n.restore()},_=function(S,w,k){ee(n,k.text,S,w+m/2,c,{strikethrough:k.hidden,textAlign:l.textAlign(k.textAlign)})},y=this.isHorizontal(),v=this._computeTitleHeight();y?u={x:H(o,this.left+h,this.right-s[0]),y:this.top+h+v,line:0}:u={x:this.left+h,y:H(o,this.top+v+h,this.bottom-e[0].height),line:0},hn(this.ctx,t.textDirection);const x=m+h;this.legendItems.forEach((S,w)=>{n.strokeStyle=S.fontColor,n.fillStyle=S.fontColor;const k=n.measureText(S.text).width,M=l.textAlign(S.textAlign||(S.textAlign=r.textAlign)),C=p+f+k;let P=u.x,T=u.y;l.setWidth(this.width),y?w>0&&P+C+h>this.right&&(T=u.y+=x,u.line++,P=u.x=H(o,this.left+h,this.right-s[u.line])):w>0&&T+x>this.bottom&&(P=u.x=P+e[u.line].width+h,u.line++,T=u.y=H(o,this.top+v+h,this.bottom-e[u.line].height));const N=l.x(P);if(b(N,T,S),P=Io(M,P+p+f,y?P+C:this.right,t.rtl),_(l.x(P),T,S),y)u.x+=C+h;else if(typeof S.text!="string"){const Q=c.lineHeight;u.y+=An(S,Q)+h}else u.y+=x}),dn(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,s=W(e.font),n=q(e.padding);if(!e.display)return;const o=Lt(t.rtl,this.left,this.width),r=this.ctx,a=e.position,l=s.size/2,c=n.top+l;let h,d=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),h=this.top+c,d=H(t.align,d,this.right-f);else{const p=this.columnSizes.reduce((g,m)=>Math.max(g,m.height),0);h=c+H(t.align,this.top,this.bottom-p-t.labels.padding-this._computeTitleHeight())}const u=H(a,d,d+f);r.textAlign=o.textAlign(li(a)),r.textBaseline="middle",r.strokeStyle=e.color,r.fillStyle=e.color,r.font=s.string,ee(r,e.text,u,h,s)}_computeTitleHeight(){const t=this.options.title,e=W(t.font),s=q(t.padding);return t.display?e.lineHeight+s.height:0}_getLegendItemAt(t,e){let s,n,o;if(ct(t,this.left,this.right)&&ct(e,this.top,this.bottom)){for(o=this.legendHitBoxes,s=0;so.length>r.length?o:r)),t+e.size/2+s.measureText(n).width}function Jl(i,t,e){let s=i;return typeof t.text!="string"&&(s=An(t,e)),s}function An(i,t){const e=i.text?i.text.length:0;return t*e}function tc(i,t){return!!((i==="mousemove"||i==="mouseout")&&(t.onHover||t.onLeave)||t.onClick&&(i==="click"||i==="mouseup"))}var Cc={id:"legend",_element:Ms,start(i,t,e){const s=i.legend=new Ms({ctx:i.ctx,options:e,chart:i});K.configure(i,s,e),K.addBox(i,s)},stop(i){K.removeBox(i,i.legend),delete i.legend},beforeUpdate(i,t,e){const s=i.legend;K.configure(i,s,e),s.options=e},afterUpdate(i){const t=i.legend;t.buildLabels(),t.adjustHitBoxes()},afterEvent(i,t){t.replay||i.legend.handleEvent(t.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(i,t,e){const s=t.datasetIndex,n=e.chart;n.isDatasetVisible(s)?(n.hide(s),t.hidden=!0):(n.show(s),t.hidden=!1)},onHover:null,onLeave:null,labels:{color:i=>i.chart.options.color,boxWidth:40,padding:10,generateLabels(i){const t=i.data.datasets,{labels:{usePointStyle:e,pointStyle:s,textAlign:n,color:o,useBorderRadius:r,borderRadius:a}}=i.legend.options;return i._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(e?0:void 0),h=q(c.borderWidth);return{text:t[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:c.borderColor,pointStyle:s||c.pointStyle,rotation:c.rotation,textAlign:n||c.textAlign,borderRadius:r&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:i=>i.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:i=>!i.startsWith("on"),labels:{_scriptable:i=>!["generateLabels","filter","sort"].includes(i)}}};class Fn extends ut{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const s=this.options;if(this.left=0,this.top=0,!s.display){this.width=this.height=this.right=this.bottom=0;return}this.width=this.right=t,this.height=this.bottom=e;const n=z(s.text)?s.text.length:1;this._padding=q(s.padding);const o=n*W(s.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return t==="top"||t==="bottom"}_drawArgs(t){const{top:e,left:s,bottom:n,right:o,options:r}=this,a=r.align;let l=0,c,h,d;return this.isHorizontal()?(h=H(a,s,o),d=e+t,c=o-s):(r.position==="left"?(h=s+t,d=H(a,n,e),l=E*-.5):(h=o-t,d=H(a,e,n),l=E*.5),c=n-e),{titleX:h,titleY:d,maxWidth:c,rotation:l}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const s=W(e.font),o=s.lineHeight/2+this._padding.top,{titleX:r,titleY:a,maxWidth:l,rotation:c}=this._drawArgs(o);ee(t,e.text,0,0,s,{color:e.color,maxWidth:l,rotation:c,textAlign:li(e.align),textBaseline:"middle",translation:[r,a]})}}function ec(i,t){const e=new Fn({ctx:i.ctx,options:t,chart:i});K.configure(i,e,t),K.addBox(i,e),i.titleBlock=e}var Tc={id:"title",_element:Fn,start(i,t,e){ec(i,e)},stop(i){const t=i.titleBlock;K.removeBox(i,t),delete i.titleBlock},beforeUpdate(i,t,e){const s=i.titleBlock;K.configure(i,s,e),s.options=e},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Yt={average(i){if(!i.length)return!1;let t,e,s=new Set,n=0,o=0;for(t=0,e=i.length;ta+l)/s.size,y:n/o}},nearest(i,t){if(!i.length)return!1;let e=t.x,s=t.y,n=Number.POSITIVE_INFINITY,o,r,a;for(o=0,r=i.length;o-1?i.split(`
+`):i}function ic(i,t){const{element:e,datasetIndex:s,index:n}=t,o=i.getDatasetMeta(s).controller,{label:r,value:a}=o.getLabelAndValue(n);return{chart:i,label:r,parsed:o.getParsed(n),raw:i.data.datasets[s].data[n],formattedValue:a,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:e}}function Ps(i,t){const e=i.chart.ctx,{body:s,footer:n,title:o}=i,{boxWidth:r,boxHeight:a}=t,l=W(t.bodyFont),c=W(t.titleFont),h=W(t.footerFont),d=o.length,f=n.length,u=s.length,p=q(t.padding);let g=p.height,m=0,b=s.reduce((v,x)=>v+x.before.length+x.lines.length+x.after.length,0);if(b+=i.beforeBody.length+i.afterBody.length,d&&(g+=d*c.lineHeight+(d-1)*t.titleSpacing+t.titleMarginBottom),b){const v=t.displayColors?Math.max(a,l.lineHeight):l.lineHeight;g+=u*v+(b-u)*l.lineHeight+(b-1)*t.bodySpacing}f&&(g+=t.footerMarginTop+f*h.lineHeight+(f-1)*t.footerSpacing);let _=0;const y=function(v){m=Math.max(m,e.measureText(v).width+_)};return e.save(),e.font=c.string,L(i.title,y),e.font=l.string,L(i.beforeBody.concat(i.afterBody),y),_=t.displayColors?r+2+t.boxPadding:0,L(s,v=>{L(v.before,y),L(v.lines,y),L(v.after,y)}),_=0,e.font=h.string,L(i.footer,y),e.restore(),m+=p.width,{width:m,height:g}}function sc(i,t){const{y:e,height:s}=t;return ei.height-s/2?"bottom":"center"}function nc(i,t,e,s){const{x:n,width:o}=s,r=e.caretSize+e.caretPadding;if(i==="left"&&n+o+r>t.width||i==="right"&&n-o-r<0)return!0}function oc(i,t,e,s){const{x:n,width:o}=e,{width:r,chartArea:{left:a,right:l}}=i;let c="center";return s==="center"?c=n<=(a+l)/2?"left":"right":n<=o/2?c="left":n>=r-o/2&&(c="right"),nc(c,i,t,e)&&(c="center"),c}function Ds(i,t,e){const s=e.yAlign||t.yAlign||sc(i,e);return{xAlign:e.xAlign||t.xAlign||oc(i,t,e,s),yAlign:s}}function rc(i,t){let{x:e,width:s}=i;return t==="right"?e-=s:t==="center"&&(e-=s/2),e}function ac(i,t,e){let{y:s,height:n}=i;return t==="top"?s+=e:t==="bottom"?s-=n+e:s-=n/2,s}function Os(i,t,e,s){const{caretSize:n,caretPadding:o,cornerRadius:r}=i,{xAlign:a,yAlign:l}=e,c=n+o,{topLeft:h,topRight:d,bottomLeft:f,bottomRight:u}=Tt(r);let p=rc(t,a);const g=ac(t,l,c);return l==="center"?a==="left"?p+=c:a==="right"&&(p-=c):a==="left"?p-=Math.max(h,f)+n:a==="right"&&(p+=Math.max(d,u)+n),{x:Y(p,0,s.width-t.width),y:Y(g,0,s.height-t.height)}}function ye(i,t,e){const s=q(e.padding);return t==="center"?i.x+i.width/2:t==="right"?i.x+i.width-s.right:i.x+s.left}function Cs(i){return tt([],rt(i))}function lc(i,t,e){return Mt(i,{tooltip:t,tooltipItems:e,type:"tooltip"})}function Ts(i,t){const e=t&&t.dataset&&t.dataset.tooltip&&t.dataset.tooltip.callbacks;return e?i.override(e):i}const In={beforeTitle:nt,title(i){if(i.length>0){const t=i[0],e=t.chart.data.labels,s=e?e.length:0;if(this&&this.options&&this.options.mode==="dataset")return t.dataset.label||"";if(t.label)return t.label;if(s>0&&t.dataIndex"u"?In[t].call(e,s):n}class Ls extends ut{static positioners=Yt;constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,s=this.options.setContext(this.getContext()),n=s.enabled&&e.options.animation&&s.animations,o=new pn(this.chart,n);return n._cacheable&&(this._cachedAnimations=Object.freeze(o)),o}getContext(){return this.$context||(this.$context=lc(this.chart.getContext(),this,this._tooltipItems))}getTitle(t,e){const{callbacks:s}=e,n=j(s,"beforeTitle",this,t),o=j(s,"title",this,t),r=j(s,"afterTitle",this,t);let a=[];return a=tt(a,rt(n)),a=tt(a,rt(o)),a=tt(a,rt(r)),a}getBeforeBody(t,e){return Cs(j(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:s}=e,n=[];return L(t,o=>{const r={before:[],lines:[],after:[]},a=Ts(s,o);tt(r.before,rt(j(a,"beforeLabel",this,o))),tt(r.lines,j(a,"label",this,o)),tt(r.after,rt(j(a,"afterLabel",this,o))),n.push(r)}),n}getAfterBody(t,e){return Cs(j(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:s}=e,n=j(s,"beforeFooter",this,t),o=j(s,"footer",this,t),r=j(s,"afterFooter",this,t);let a=[];return a=tt(a,rt(n)),a=tt(a,rt(o)),a=tt(a,rt(r)),a}_createItems(t){const e=this._active,s=this.chart.data,n=[],o=[],r=[];let a=[],l,c;for(l=0,c=e.length;lt.filter(h,d,f,s))),t.itemSort&&(a=a.sort((h,d)=>t.itemSort(h,d,s))),L(a,h=>{const d=Ts(t.callbacks,h);n.push(j(d,"labelColor",this,h)),o.push(j(d,"labelPointStyle",this,h)),r.push(j(d,"labelTextColor",this,h))}),this.labelColors=n,this.labelPointStyles=o,this.labelTextColors=r,this.dataPoints=a,a}update(t,e){const s=this.options.setContext(this.getContext()),n=this._active;let o,r=[];if(!n.length)this.opacity!==0&&(o={opacity:0});else{const a=Yt[s.position].call(this,n,this._eventPosition);r=this._createItems(s),this.title=this.getTitle(r,s),this.beforeBody=this.getBeforeBody(r,s),this.body=this.getBody(r,s),this.afterBody=this.getAfterBody(r,s),this.footer=this.getFooter(r,s);const l=this._size=Ps(this,s),c=Object.assign({},a,l),h=Ds(this.chart,s,c),d=Os(s,c,h,this.chart);this.xAlign=h.xAlign,this.yAlign=h.yAlign,o={opacity:1,x:d.x,y:d.y,width:l.width,height:l.height,caretX:a.x,caretY:a.y}}this._tooltipItems=r,this.$context=void 0,o&&this._resolveAnimations().update(this,o),t&&s.external&&s.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,s,n){const o=this.getCaretPosition(t,s,n);e.lineTo(o.x1,o.y1),e.lineTo(o.x2,o.y2),e.lineTo(o.x3,o.y3)}getCaretPosition(t,e,s){const{xAlign:n,yAlign:o}=this,{caretSize:r,cornerRadius:a}=s,{topLeft:l,topRight:c,bottomLeft:h,bottomRight:d}=Tt(a),{x:f,y:u}=t,{width:p,height:g}=e;let m,b,_,y,v,x;return o==="center"?(v=u+g/2,n==="left"?(m=f,b=m-r,y=v+r,x=v-r):(m=f+p,b=m+r,y=v-r,x=v+r),_=m):(n==="left"?b=f+Math.max(l,h)+r:n==="right"?b=f+p-Math.max(c,d)-r:b=this.caretX,o==="top"?(y=u,v=y-r,m=b-r,_=b+r):(y=u+g,v=y+r,m=b+r,_=b-r),x=y),{x1:m,x2:b,x3:_,y1:y,y2:v,y3:x}}drawTitle(t,e,s){const n=this.title,o=n.length;let r,a,l;if(o){const c=Lt(s.rtl,this.x,this.width);for(t.x=ye(this,s.titleAlign,s),e.textAlign=c.textAlign(s.titleAlign),e.textBaseline="middle",r=W(s.titleFont),a=s.titleSpacing,e.fillStyle=s.titleColor,e.font=r.string,l=0;l_!==0)?(t.beginPath(),t.fillStyle=o.multiKeyBackground,De(t,{x:g,y:p,w:c,h:l,radius:b}),t.fill(),t.stroke(),t.fillStyle=r.backgroundColor,t.beginPath(),De(t,{x:m,y:p+1,w:c-2,h:l-2,radius:b}),t.fill()):(t.fillStyle=o.multiKeyBackground,t.fillRect(g,p,c,l),t.strokeRect(g,p,c,l),t.fillStyle=r.backgroundColor,t.fillRect(m,p+1,c-2,l-2))}t.fillStyle=this.labelTextColors[s]}drawBody(t,e,s){const{body:n}=this,{bodySpacing:o,bodyAlign:r,displayColors:a,boxHeight:l,boxWidth:c,boxPadding:h}=s,d=W(s.bodyFont);let f=d.lineHeight,u=0;const p=Lt(s.rtl,this.x,this.width),g=function(k){e.fillText(k,p.x(t.x+u),t.y+f/2),t.y+=f+o},m=p.textAlign(r);let b,_,y,v,x,S,w;for(e.textAlign=r,e.textBaseline="middle",e.font=d.string,t.x=ye(this,m,s),e.fillStyle=s.bodyColor,L(this.beforeBody,g),u=a&&m!=="right"?r==="center"?c/2+h:c+2+h:0,v=0,S=n.length;v0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,s=this.$animations,n=s&&s.x,o=s&&s.y;if(n||o){const r=Yt[t.position].call(this,this._active,this._eventPosition);if(!r)return;const a=this._size=Ps(this,t),l=Object.assign({},r,this._size),c=Ds(e,t,l),h=Os(t,l,c,e);(n._to!==h.x||o._to!==h.y)&&(this.xAlign=c.xAlign,this.yAlign=c.yAlign,this.width=a.width,this.height=a.height,this.caretX=r.x,this.caretY=r.y,this._resolveAnimations().update(this,h))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let s=this.opacity;if(!s)return;this._updateAnimationTarget(e);const n={width:this.width,height:this.height},o={x:this.x,y:this.y};s=Math.abs(s)<.001?0:s;const r=q(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=s,this.drawBackground(o,t,n,e),hn(t,e.textDirection),o.y+=r.top,this.drawTitle(o,t,e),this.drawBody(o,t,e),this.drawFooter(o,t,e),dn(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const s=this._active,n=t.map(({datasetIndex:a,index:l})=>{const c=this.chart.getDatasetMeta(a);if(!c)throw new Error("Cannot find a dataset at index "+a);return{datasetIndex:a,element:c.data[l],index:l}}),o=!Se(s,n),r=this._positionChanged(n,e);(o||r)&&(this._active=n,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,s=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const n=this.options,o=this._active||[],r=this._getActiveElements(t,o,e,s),a=this._positionChanged(r,t),l=e||!Se(r,o)||a;return l&&(this._active=r,(n.enabled||n.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),l}_getActiveElements(t,e,s,n){const o=this.options;if(t.type==="mouseout")return[];if(!n)return e.filter(a=>this.chart.data.datasets[a.datasetIndex]&&this.chart.getDatasetMeta(a.datasetIndex).controller.getParsed(a.index)!==void 0);const r=this.chart.getElementsAtEventForMode(t,o.mode,o,s);return o.reverse&&r.reverse(),r}_positionChanged(t,e){const{caretX:s,caretY:n,options:o}=this,r=Yt[o.position].call(this,t,e);return r!==!1&&(s!==r.x||n!==r.y)}}var Lc={id:"tooltip",_element:Ls,positioners:Yt,afterInit(i,t,e){e&&(i.tooltip=new Ls({chart:i,options:e}))},beforeUpdate(i,t,e){i.tooltip&&i.tooltip.initialize(e)},reset(i,t,e){i.tooltip&&i.tooltip.initialize(e)},afterDraw(i){const t=i.tooltip;if(t&&t._willRender()){const e={tooltip:t};if(i.notifyPlugins("beforeTooltipDraw",{...e,cancelable:!0})===!1)return;t.draw(i.ctx),i.notifyPlugins("afterTooltipDraw",e)}},afterEvent(i,t){if(i.tooltip){const e=t.replay;i.tooltip.handleEvent(t.event,e,t.inChartArea)&&(t.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(i,t)=>t.bodyFont.size,boxWidth:(i,t)=>t.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:In},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:i=>i!=="filter"&&i!=="itemSort"&&i!=="external",_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};const cc=(i,t,e,s)=>(typeof t=="string"?(e=i.push(t)-1,s.unshift({index:e,label:t})):isNaN(t)&&(e=null),e);function hc(i,t,e,s){const n=i.indexOf(t);if(n===-1)return cc(i,t,e,s);const o=i.lastIndexOf(t);return n!==o?e:n}const dc=(i,t)=>i===null?null:Y(Math.round(i),0,t);function As(i){const t=this.getLabels();return i>=0&&ie.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}}function fc(i,t){const e=[],{bounds:n,step:o,min:r,max:a,precision:l,count:c,maxTicks:h,maxDigits:d,includeBounds:f}=i,u=o||1,p=h-1,{min:g,max:m}=t,b=!A(r),_=!A(a),y=!A(c),v=(m-g)/(d+1);let x=Di((m-g)/p/u)*u,S,w,k,M;if(x<1e-14&&!b&&!_)return[{value:g},{value:m}];M=Math.ceil(m/x)-Math.floor(g/x),M>p&&(x=Di(M*x/p/u)*u),A(l)||(S=Math.pow(10,l),x=Math.ceil(x*S)/S),n==="ticks"?(w=Math.floor(g/x)*x,k=Math.ceil(m/x)*x):(w=g,k=m),b&&_&&o&&So((a-r)/o,x/1e3)?(M=Math.round(Math.min((a-r)/x,h)),x=(a-r)/M,w=r,k=a):y?(w=b?r:w,k=_?a:k,M=c-1,x=(k-w)/M):(M=(k-w)/x,Xt(M,Math.round(M),x/1e3)?M=Math.round(M):M=Math.ceil(M));const C=Math.max(Oi(x),Oi(w));S=Math.pow(10,A(l)?C:l),w=Math.round(w*S)/S,k=Math.round(k*S)/S;let P=0;for(b&&(f&&w!==r?(e.push({value:r}),wa)break;e.push({value:T})}return _&&f&&k!==a?e.length&&Xt(e[e.length-1].value,a,Fs(a,v,i))?e[e.length-1].value=a:e.push({value:a}):(!_||k===a)&&e.push({value:k}),e}function Fs(i,t,{horizontal:e,minRotation:s}){const n=vt(s),o=(e?Math.sin(n):Math.cos(n))||.001,r=.75*t*(""+i).length;return Math.min(t/o,r)}class uc extends Rt{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return A(t)||(typeof t=="number"||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:s}=this.getUserBounds();let{min:n,max:o}=this;const r=l=>n=e?n:l,a=l=>o=s?o:l;if(t){const l=st(n),c=st(o);l<0&&c<0?a(0):l>0&&c>0&&r(0)}if(n===o){let l=o===0?1:Math.abs(o*.05);a(o+l),t||r(n-l)}this.min=n,this.max=o}getTickLimit(){const t=this.options.ticks;let{maxTicksLimit:e,stepSize:s}=t,n;return s?(n=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,n>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${n} ticks. Limiting to 1000.`),n=1e3)):(n=this.computeTickLimit(),e=e||11),e&&(n=Math.min(e,n)),n}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let s=this.getTickLimit();s=Math.max(2,s);const n={maxTicks:s,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:e.includeBounds!==!1},o=this._range||this,r=fc(n,o);return t.bounds==="ticks"&&Mo(r,this,"value"),t.reverse?(r.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),r}configure(){const t=this.ticks;let e=this.min,s=this.max;if(super.configure(),this.options.offset&&t.length){const n=(s-e)/Math.max(t.length-1,1)/2;e-=n,s+=n}this._startValue=e,this._endValue=s,this._valueRange=s-e}getLabelForValue(t){return Qs(t,this.chart.options.locale,this.options.ticks.format)}}class Fc extends uc{static id="linear";static defaults={ticks:{callback:tn.formatters.numeric}};determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=V(t)?t:0,this.max=V(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,s=vt(this.options.ticks.minRotation),n=(t?Math.sin(s):Math.cos(s))||.001,o=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,o.lineHeight/n))}getPixelForValue(t){return t===null?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}const Fe={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},$=Object.keys(Fe);function Is(i,t){return i-t}function Rs(i,t){if(A(t))return null;const e=i._adapter,{parser:s,round:n,isoWeekday:o}=i._parseOpts;let r=t;return typeof s=="function"&&(r=s(r)),V(r)||(r=typeof s=="string"?e.parse(r,s):e.parse(r)),r===null?null:(n&&(r=n==="week"&&(Jt(o)||o===!0)?e.startOf(r,"isoWeek",o):e.startOf(r,n)),+r)}function zs(i,t,e,s){const n=$.length;for(let o=$.indexOf(i);o=$.indexOf(e);o--){const r=$[o];if(Fe[r].common&&i._adapter.diff(n,s,r)>=t-1)return r}return $[e?$.indexOf(e):0]}function pc(i){for(let t=$.indexOf(i)+1,e=$.length;t=t?e[s]:e[n];i[o]=!0}}function mc(i,t,e,s){const n=i._adapter,o=+n.startOf(t[0].value,s),r=t[t.length-1].value;let a,l;for(a=o;a<=r;a=+n.add(a,1,s))l=e[a],l>=0&&(t[l].major=!0);return t}function Bs(i,t,e){const s=[],n={},o=t.length;let r,a;for(r=0;r+t.value))}initOffsets(t=[]){let e=0,s=0,n,o;this.options.offset&&t.length&&(n=this.getDecimalForValue(t[0]),t.length===1?e=1-n:e=(this.getDecimalForValue(t[1])-n)/2,o=this.getDecimalForValue(t[t.length-1]),t.length===1?s=o:s=(o-this.getDecimalForValue(t[t.length-2]))/2);const r=t.length<3?.5:.25;e=Y(e,0,r),s=Y(s,0,r),this._offsets={start:e,end:s,factor:1/(e+1+s)}}_generate(){const t=this._adapter,e=this.min,s=this.max,n=this.options,o=n.time,r=o.unit||zs(o.minUnit,e,s,this._getLabelCapacity(e)),a=D(n.ticks.stepSize,1),l=r==="week"?o.isoWeekday:!1,c=Jt(l)||l===!0,h={};let d=e,f,u;if(c&&(d=+t.startOf(d,"isoWeek",l)),d=+t.startOf(d,c?"day":r),t.diff(s,e,r)>1e5*a)throw new Error(e+" and "+s+" are too far apart with stepSize of "+a+" "+r);const p=n.ticks.source==="data"&&this.getDataTimestamps();for(f=d,u=0;f+g)}getLabelForValue(t){const e=this._adapter,s=this.options.time;return s.tooltipFormat?e.format(t,s.tooltipFormat):e.format(t,s.displayFormats.datetime)}format(t,e){const n=this.options.time.displayFormats,o=this._unit,r=e||n[o];return this._adapter.format(t,r)}_tickFormatFunction(t,e,s,n){const o=this.options,r=o.ticks.callback;if(r)return F(r,[t,e,s],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,h=l&&a[l],d=c&&a[c],f=s[e],u=c&&d&&f&&f.major;return this._adapter.format(t,n||(u?d:h))}generateTickLabels(t){let e,s,n;for(e=0,s=t.length;e0?a:1}getDataTimestamps(){let t=this._cache.data||[],e,s;if(t.length)return t;const n=this.getMatchingVisibleMetas();if(this._normalized&&n.length)return this._cache.data=n[0].controller.getAllParsedValues(this);for(e=0,s=n.length;e=i[s].pos&&t<=i[n].pos&&({lo:s,hi:n}=kt(i,"pos",t)),{pos:o,time:a}=i[s],{pos:r,time:l}=i[n]):(t>=i[s].time&&t<=i[n].time&&({lo:s,hi:n}=kt(i,"time",t)),{time:o,pos:a}=i[s],{time:r,pos:l}=i[n]);const c=r-o;return c?a+(l-a)*(t-o)/c:a}class Ic extends Hs{static id="timeseries";static defaults=Hs.defaults;constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=ve(e,this.min),this._tableRange=ve(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:s}=this,n=[],o=[];let r,a,l,c,h;for(r=0,a=t.length;r=e&&c<=s&&n.push(c);if(n.length<2)return[{time:e,pos:0},{time:s,pos:1}];for(r=0,a=n.length;rn-o)}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),s=this.getLabelTimestamps();return e.length&&s.length?t=this.normalize(e.concat(s)):t=e.length?e:s,t=this._cache.all=t,t}getDecimalForValue(t){return(ve(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,s=this.getDecimalForPixel(t)/e.factor-e.end;return ve(this._table,s*this._tableRange+this._minPos,!0)}}const Rn={data:{type:Object,required:!0},options:{type:Object,default:()=>({})},plugins:{type:Array,default:()=>[]},datasetIdKey:{type:String,default:"label"},updateMode:{type:String,default:void 0}},bc={ariaLabel:{type:String},ariaDescribedby:{type:String}},_c={type:{type:String,required:!0},destroyDelay:{type:Number,default:0},...Rn,...bc},xc=Hn[0]==="2"?(i,t)=>Object.assign(i,{attrs:t}):(i,t)=>Object.assign(i,t);function Ct(i){return Ns(i)?Xe(i):i}function yc(i){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:i;return Ns(t)?new Proxy(i,{}):i}function vc(i,t){const e=i.options;e&&t&&Object.assign(e,t)}function zn(i,t){i.labels=t}function En(i,t,e){const s=[];i.datasets=t.map(n=>{const o=i.datasets.find(r=>r[e]===n[e]);return!o||!n.data||s.includes(o)?{...n}:(s.push(o),Object.assign(o,n),o)})}function kc(i,t){const e={labels:[],datasets:[]};return zn(e,i.labels),En(e,i.datasets,t),e}const wc=Ws({props:_c,setup(i,t){let{expose:e,slots:s}=t;const n=Wn(null),o=Vs(null);e({chart:o});const r=()=>{if(!n.value)return;const{type:c,data:h,options:d,plugins:f,datasetIdKey:u}=i,p=kc(h,u),g=yc(p,h);o.value=new bi(n.value,{type:c,data:g,options:{...d},plugins:f})},a=()=>{const c=Xe(o.value);c&&(i.destroyDelay>0?setTimeout(()=>{c.destroy(),o.value=null},i.destroyDelay):(c.destroy(),o.value=null))},l=c=>{c.update(i.updateMode)};return Vn(r),Nn(a),jn([()=>i.options,()=>i.data],(c,h)=>{let[d,f]=c,[u,p]=h;const g=Xe(o.value);if(!g)return;let m=!1;if(d){const b=Ct(d),_=Ct(u);b&&b!==_&&(vc(g,b),m=!0)}if(f){const b=Ct(f.labels),_=Ct(p.labels),y=Ct(f.datasets),v=Ct(p.datasets);b!==_&&(zn(g.config.data,b),m=!0),y&&y!==v&&(En(g.config.data,y,i.datasetIdKey),m=!0)}m&&$n(()=>{l(g)})},{deep:!0}),()=>Ue("canvas",{role:"img",ariaLabel:i.ariaLabel,ariaDescribedby:i.ariaDescribedby,ref:n},[Ue("p",{},[s.default?s.default():""])])}});function Bn(i,t){return bi.register(t),Ws({props:Rn,setup(e,s){let{expose:n}=s;const o=Vs(null),r=a=>{o.value=a?.chart};return n({chart:o}),()=>Ue(wc,xc({ref:r},{type:i,...e}))}})}const Rc=Bn("bar",oa),zc=Bn("line",ra);export{Dc as B,bi as C,_i as L,Pc as P,oa as a,ra as b,Fc as c,Tc as d,Lc as e,Ac as f,zc as g,Rc as h,Oc as i,Cc as p};
diff --git a/src/static/app/dist/assets/index-BA3_cn6G.js b/src/static/app/dist/assets/index-BA3_cn6G.js
new file mode 100644
index 0000000..9788ed1
--- /dev/null
+++ b/src/static/app/dist/assets/index-BA3_cn6G.js
@@ -0,0 +1,56 @@
+import{r as ge,o as Me,a as x,c as S,b as c,d,e as D,f as I,t as E,_ as K,D as ee,w as y,T as we,n as X,u as Ee,W as De,g as qe,G as Ge,F as _e,h as ye,i as q,j as J,k as z,l as Ne,S as Ze}from"./index-CUmHRwBw.js";import{L as C}from"./localeText-DvaqSAco.js";import"./dayjs.min-C3XPfvH9.js";import{M as He}from"./message-2XqhwcFu.js";const Oe={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll"},je={class:"container d-flex h-100 w-100"},Qe={class:"m-auto modal-dialog-centered dashboardModal"},We={class:"card rounded-3 shadow flex-grow-1"},Ue={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2"},Fe={class:"mb-0"},Ve={class:"card-body px-4 pb-4 d-flex flex-column gap-2"},Xe={class:"card text-decoration-none",target:"_blank",role:"button",href:"https://discord.gg/72TwzjeuWm"},Je={class:"card-body d-flex gap-4 align-items-center"},Ye={class:"d-flex align-items-center"},Ke={class:"badge rounded-pill text-bg-primary ms-2"},et={key:0,class:"spinner-border spinner-border-sm",style:{width:"0.7rem",height:"0.7rem"}},tt={key:1},nt={class:"text-muted"},st={class:"card text-decoration-none",href:"https://donaldzou.github.io/WGDashboard-Documentation/",target:"_blank"},it={class:"card-body d-flex gap-4 align-items-center"},rt={class:"mb-0"},ot={class:"text-muted"},lt={__name:"helpModal",setup(a){const e=ge(!0),t=ge(void 0);return Me(()=>{e.value=!0,fetch("https://discord.com/api/guilds/1276818723637956628/widget.json").then(n=>n.json()).then(n=>{t.value=n,e.value=!1}).catch(()=>{e.value=!1})}),(n,s)=>(x(),S("div",Oe,[c("div",je,[c("div",Qe,[c("div",We,[c("div",Ue,[c("h4",Fe,[d(C,{t:"Help"})]),c("button",{type:"button",class:"btn-close ms-auto",onClick:s[0]||(s[0]=r=>n.$emit("close"))})]),c("div",Ve,[c("a",Xe,[c("div",Je,[s[3]||(s[3]=c("h1",{class:"mb-0"},[c("i",{class:"bi bi-discord"})],-1)),c("div",null,[c("div",Ye,[s[2]||(s[2]=c("h5",{class:"mb-0"}," Discord Server ",-1)),c("span",Ke,[e.value?(x(),S("span",et)):D("",!0),t.value!==void 0&&!e.value?(x(),S("span",tt,[s[1]||(s[1]=c("i",{class:"bi bi-person-fill me-2"},null,-1)),I(E(t.value.presence_count)+" Online ",1)])):D("",!0)])]),c("small",nt,[d(C,{t:"Join our Discord server for quick help or chat about WGDashboard!"})])])])]),c("a",st,[c("div",it,[s[4]||(s[4]=c("h1",{class:"mb-0"},[c("i",{class:"bi bi-hash"})],-1)),c("div",null,[c("h5",rt,[d(C,{t:"Official Documentation"})]),c("small",ot,[d(C,{t:"Official documentation contains User Guides and more..."})])])])])])])])])]))}};function te(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}let L=te();function $e(a){L=a}const G={exec:()=>null};function g(a,e=""){let t=typeof a=="string"?a:a.source;const n={replace:(s,r)=>{let i=typeof r=="string"?r:r.source;return i=i.replace(_.caret,"$1"),t=t.replace(s,i),n},getRegex:()=>new RegExp(t,e)};return n}const _={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:a=>new RegExp(`^( {0,3}${a})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:a=>new RegExp(`^ {0,${Math.min(3,a-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:a=>new RegExp(`^ {0,${Math.min(3,a-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:a=>new RegExp(`^ {0,${Math.min(3,a-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:a=>new RegExp(`^ {0,${Math.min(3,a-1)}}#`),htmlBeginRegex:a=>new RegExp(`^ {0,${Math.min(3,a-1)}}<(?:[a-z].*>|!--)`,"i")},at=/^(?:[ \t]*(?:\n|$))+/,ct=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,ut=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,N=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,ht=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,ne=/(?:[*+-]|\d{1,9}[.)])/,Se=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,ve=g(Se).replace(/bull/g,ne).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),pt=g(Se).replace(/bull/g,ne).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),se=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,dt=/^[^\n]+/,ie=/(?!\s*\])(?:\\.|[^\[\]\\])+/,gt=g(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",ie).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),ft=g(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,ne).getRegex(),W="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",re=/|$))/,kt=g("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",re).replace("tag",W).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Te=g(se).replace("hr",N).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",W).getRegex(),bt=g(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",Te).getRegex(),oe={blockquote:bt,code:ct,def:gt,fences:ut,heading:ht,hr:N,html:kt,lheading:ve,list:ft,newline:at,paragraph:Te,table:G,text:dt},fe=g("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",N).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",W).getRegex(),mt={...oe,lheading:pt,table:fe,paragraph:g(se).replace("hr",N).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",fe).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",W).getRegex()},xt={...oe,html:g(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",re).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:G,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:g(se).replace("hr",N).replace("heading",` *#{1,6} *[^
+]`).replace("lheading",ve).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},wt=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,_t=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,Re=/^( {2,}|\\)\n(?!\s*$)/,yt=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\]*?>/g,Ae=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,Rt=g(Ae,"u").replace(/punct/g,U).getRegex(),Ct=g(Ae,"u").replace(/punct/g,ze).getRegex(),Le="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",zt=g(Le,"gu").replace(/notPunctSpace/g,Ce).replace(/punctSpace/g,le).replace(/punct/g,U).getRegex(),At=g(Le,"gu").replace(/notPunctSpace/g,vt).replace(/punctSpace/g,St).replace(/punct/g,ze).getRegex(),Lt=g("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,Ce).replace(/punctSpace/g,le).replace(/punct/g,U).getRegex(),It=g(/\\(punct)/,"gu").replace(/punct/g,U).getRegex(),Pt=g(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Bt=g(re).replace("(?:-->|$)","-->").getRegex(),Mt=g("^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",Bt).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),O=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Et=g(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",O).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),Ie=g(/^!?\[(label)\]\[(ref)\]/).replace("label",O).replace("ref",ie).getRegex(),Pe=g(/^!?\[(ref)\](?:\[\])?/).replace("ref",ie).getRegex(),Dt=g("reflink|nolink(?!\\()","g").replace("reflink",Ie).replace("nolink",Pe).getRegex(),ae={_backpedal:G,anyPunctuation:It,autolink:Pt,blockSkip:Tt,br:Re,code:_t,del:G,emStrongLDelim:Rt,emStrongRDelimAst:zt,emStrongRDelimUnd:Lt,escape:wt,link:Et,nolink:Pe,punctuation:$t,reflink:Ie,reflinkSearch:Dt,tag:Mt,text:yt,url:G},qt={...ae,link:g(/^!?\[(label)\]\((.*?)\)/).replace("label",O).getRegex(),reflink:g(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",O).getRegex()},Y={...ae,emStrongRDelimAst:At,emStrongLDelim:Ct,url:g(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},ke=a=>Nt[a];function R(a,e){if(e){if(_.escapeTest.test(a))return a.replace(_.escapeReplace,ke)}else if(_.escapeTestNoEncode.test(a))return a.replace(_.escapeReplaceNoEncode,ke);return a}function be(a){try{a=encodeURI(a).replace(_.percentDecode,"%")}catch{return null}return a}function me(a,e){const t=a.replace(_.findPipe,(r,i,o)=>{let l=!1,u=i;for(;--u>=0&&o[u]==="\\";)l=!l;return l?"|":" |"}),n=t.split(_.splitPipe);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),e)if(n.length>e)n.splice(e);else for(;n.length{const i=r.match(t.other.beginningSpace);if(i===null)return r;const[o]=i;return o.length>=s.length?r.slice(s.length):r}).join(`
+`)}class j{options;rules;lexer;constructor(e){this.options=e||L}space(e){const t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const n=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?n:M(n,`
+`)}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const n=t[0],s=Ht(n,t[3]||"",this.rules);return{type:"code",raw:n,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:s}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let n=t[2].trim();if(this.rules.other.endingHash.test(n)){const s=M(n,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(n=s.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:M(t[0],`
+`)}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let n=M(t[0],`
+`).split(`
+`),s="",r="";const i=[];for(;n.length>0;){let o=!1;const l=[];let u;for(u=0;u1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=this.rules.other.listItemRegex(n);let o=!1;for(;e;){let u=!1,h="",p="";if(!(t=i.exec(e))||this.rules.block.hr.test(e))break;h=t[0],e=e.substring(h.length);let m=t[2].split(`
+`,1)[0].replace(this.rules.other.listReplaceTabs,F=>" ".repeat(3*F.length)),f=e.split(`
+`,1)[0],w=!m.trim(),b=0;if(this.options.pedantic?(b=2,p=m.trimStart()):w?b=t[1].length+1:(b=t[2].search(this.rules.other.nonSpaceChar),b=b>4?1:b,p=m.slice(b),b+=t[1].length),w&&this.rules.other.blankLine.test(f)&&(h+=f+`
+`,e=e.substring(f.length+1),u=!0),!u){const F=this.rules.other.nextBulletRegex(b),he=this.rules.other.hrRegex(b),pe=this.rules.other.fencesBeginRegex(b),de=this.rules.other.headingBeginRegex(b),Be=this.rules.other.htmlBeginRegex(b);for(;e;){const V=e.split(`
+`,1)[0];let P;if(f=V,this.options.pedantic?(f=f.replace(this.rules.other.listReplaceNesting," "),P=f):P=f.replace(this.rules.other.tabCharGlobal," "),pe.test(f)||de.test(f)||Be.test(f)||F.test(f)||he.test(f))break;if(P.search(this.rules.other.nonSpaceChar)>=b||!f.trim())p+=`
+`+P.slice(b);else{if(w||m.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||pe.test(m)||de.test(m)||he.test(m))break;p+=`
+`+f}!w&&!f.trim()&&(w=!0),h+=V+`
+`,e=e.substring(V.length+1),m=P.slice(b)}}r.loose||(o?r.loose=!0:this.rules.other.doubleBlankLine.test(h)&&(o=!0));let $=null,ue;this.options.gfm&&($=this.rules.other.listIsTask.exec(p),$&&(ue=$[0]!=="[ ] ",p=p.replace(this.rules.other.listReplaceTask,""))),r.items.push({type:"list_item",raw:h,task:!!$,checked:ue,loose:!1,text:p,tokens:[]}),r.raw+=h}const l=r.items.at(-1);if(l)l.raw=l.raw.trimEnd(),l.text=l.text.trimEnd();else return;r.raw=r.raw.trimEnd();for(let u=0;um.type==="space"),p=h.length>0&&h.some(m=>this.rules.other.anyLine.test(m.raw));r.loose=p}if(r.loose)for(let u=0;u({text:l,tokens:this.lexer.inline(l),header:!1,align:i.align[u]})));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:t[2].charAt(0)==="="?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const n=t[1].charAt(t[1].length-1)===`
+`?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:n,tokens:this.lexer.inline(n)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const n=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;const i=M(n.slice(0,-1),"\\");if((n.length-i.length)%2===0)return}else{const i=Zt(t[2],"()");if(i>-1){const l=(t[0].indexOf("!")===0?5:4)+t[1].length+i;t[2]=t[2].substring(0,i),t[0]=t[0].substring(0,l).trim(),t[3]=""}}let s=t[2],r="";if(this.options.pedantic){const i=this.rules.other.pedanticHrefTitle.exec(s);i&&(s=i[1],r=i[3])}else r=t[3]?t[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?s=s.slice(1):s=s.slice(1,-1)),xe(t,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:r&&r.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const s=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),r=t[s.toLowerCase()];if(!r){const i=n[0].charAt(0);return{type:"text",raw:i,text:i}}return xe(n,r,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s||s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const i=[...s[0]].length-1;let o,l,u=i,h=0;const p=s[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(p.lastIndex=0,t=t.slice(-1*e.length+i);(s=p.exec(t))!=null;){if(o=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!o)continue;if(l=[...o].length,s[3]||s[4]){u+=l;continue}else if((s[5]||s[6])&&i%3&&!((i+l)%3)){h+=l;continue}if(u-=l,u>0)continue;l=Math.min(l,l+u+h);const m=[...s[0]][0].length,f=e.slice(0,i+s.index+m+l);if(Math.min(i,l)%2){const b=f.slice(1,-1);return{type:"em",raw:f,text:b,tokens:this.lexer.inlineTokens(b)}}const w=f.slice(2,-2);return{type:"strong",raw:f,text:w,tokens:this.lexer.inlineTokens(w)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let n=t[2].replace(this.rules.other.newLineCharGlobal," ");const s=this.rules.other.nonSpaceChar.test(n),r=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return s&&r&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:t[0],text:n}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let n,s;return t[2]==="@"?(n=t[1],s="mailto:"+n):(n=t[1],s=n),{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let n,s;if(t[2]==="@")n=t[0],s="mailto:"+n;else{let r;do r=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??"";while(r!==t[0]);n=t[0],t[1]==="www."?s="http://"+t[0]:s=t[0]}return{type:"link",raw:t[0],text:n,href:s,tokens:[{type:"text",raw:n,text:n}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){const n=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:n}}}}class v{tokens;options;state;tokenizer;inlineQueue;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||L,this.options.tokenizer=this.options.tokenizer||new j,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const t={other:_,block:Z.normal,inline:B.normal};this.options.pedantic?(t.block=Z.pedantic,t.inline=B.pedantic):this.options.gfm&&(t.block=Z.gfm,this.options.breaks?t.inline=B.breaks:t.inline=B.gfm),this.tokenizer.rules=t}static get rules(){return{block:Z,inline:B}}static lex(e,t){return new v(t).lex(e)}static lexInline(e,t){return new v(t).inlineTokens(e)}lex(e){e=e.replace(_.carriageReturn,`
+`),this.blockTokens(e,this.tokens);for(let t=0;t(s=i.call({lexer:this},e,t))?(e=e.substring(s.raw.length),t.push(s),!0):!1))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);const i=t.at(-1);s.raw.length===1&&i!==void 0?i.raw+=`
+`:t.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);const i=t.at(-1);i?.type==="paragraph"||i?.type==="text"?(i.raw+=`
+`+s.raw,i.text+=`
+`+s.text,this.inlineQueue.at(-1).src=i.text):t.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);const i=t.at(-1);i?.type==="paragraph"||i?.type==="text"?(i.raw+=`
+`+s.raw,i.text+=`
+`+s.raw,this.inlineQueue.at(-1).src=i.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title});continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),t.push(s);continue}let r=e;if(this.options.extensions?.startBlock){let i=1/0;const o=e.slice(1);let l;this.options.extensions.startBlock.forEach(u=>{l=u.call({lexer:this},o),typeof l=="number"&&l>=0&&(i=Math.min(i,l))}),i<1/0&&i>=0&&(r=e.substring(0,i+1))}if(this.state.top&&(s=this.tokenizer.paragraph(r))){const i=t.at(-1);n&&i?.type==="paragraph"?(i.raw+=`
+`+s.raw,i.text+=`
+`+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=i.text):t.push(s),n=r.length!==e.length,e=e.substring(s.raw.length);continue}if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);const i=t.at(-1);i?.type==="text"?(i.raw+=`
+`+s.raw,i.text+=`
+`+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=i.text):t.push(s);continue}if(e){const i="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(i);break}else throw new Error(i)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,s=null;if(this.tokens.links){const o=Object.keys(this.tokens.links);if(o.length>0)for(;(s=this.tokenizer.rules.inline.reflinkSearch.exec(n))!=null;)o.includes(s[0].slice(s[0].lastIndexOf("[")+1,-1))&&(n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(s=this.tokenizer.rules.inline.blockSkip.exec(n))!=null;)n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;(s=this.tokenizer.rules.inline.anyPunctuation.exec(n))!=null;)n=n.slice(0,s.index)+"++"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let r=!1,i="";for(;e;){r||(i=""),r=!1;let o;if(this.options.extensions?.inline?.some(u=>(o=u.call({lexer:this},e,t))?(e=e.substring(o.raw.length),t.push(o),!0):!1))continue;if(o=this.tokenizer.escape(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.tag(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.link(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(o.raw.length);const u=t.at(-1);o.type==="text"&&u?.type==="text"?(u.raw+=o.raw,u.text+=o.text):t.push(o);continue}if(o=this.tokenizer.emStrong(e,n,i)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.codespan(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.br(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.del(e)){e=e.substring(o.raw.length),t.push(o);continue}if(o=this.tokenizer.autolink(e)){e=e.substring(o.raw.length),t.push(o);continue}if(!this.state.inLink&&(o=this.tokenizer.url(e))){e=e.substring(o.raw.length),t.push(o);continue}let l=e;if(this.options.extensions?.startInline){let u=1/0;const h=e.slice(1);let p;this.options.extensions.startInline.forEach(m=>{p=m.call({lexer:this},h),typeof p=="number"&&p>=0&&(u=Math.min(u,p))}),u<1/0&&u>=0&&(l=e.substring(0,u+1))}if(o=this.tokenizer.inlineText(l)){e=e.substring(o.raw.length),o.raw.slice(-1)!=="_"&&(i=o.raw.slice(-1)),r=!0;const u=t.at(-1);u?.type==="text"?(u.raw+=o.raw,u.text+=o.text):t.push(o);continue}if(e){const u="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(u);break}else throw new Error(u)}}return t}}class Q{options;parser;constructor(e){this.options=e||L}space(e){return""}code({text:e,lang:t,escaped:n}){const s=(t||"").match(_.notSpaceStart)?.[0],r=e.replace(_.endingNewline,"")+`
+`;return s?'
+`}tablecell(e){const t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`${n}>
+`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${R(e,!0)}`}br(e){return" "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){const s=this.parser.parseInline(n),r=be(e);if(r===null)return s;e=r;let i='"+s+"",i}image({href:e,title:t,text:n}){const s=be(e);if(s===null)return R(n);e=s;let r=`",r}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:R(e.text)}}class ce{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}}class T{options;renderer;textRenderer;constructor(e){this.options=e||L,this.options.renderer=this.options.renderer||new Q,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new ce}static parse(e,t){return new T(t).parse(e)}static parseInline(e,t){return new T(t).parseInline(e)}parse(e,t=!0){let n="";for(let s=0;s{const o=r[i].flat(1/0);n=n.concat(this.walkTokens(o,t))}):r.tokens&&(n=n.concat(this.walkTokens(r.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(n=>{const s={...n};if(s.async=this.defaults.async||s.async||!1,n.extensions&&(n.extensions.forEach(r=>{if(!r.name)throw new Error("extension name required");if("renderer"in r){const i=t.renderers[r.name];i?t.renderers[r.name]=function(...o){let l=r.renderer.apply(this,o);return l===!1&&(l=i.apply(this,o)),l}:t.renderers[r.name]=r.renderer}if("tokenizer"in r){if(!r.level||r.level!=="block"&&r.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");const i=t[r.level];i?i.unshift(r.tokenizer):t[r.level]=[r.tokenizer],r.start&&(r.level==="block"?t.startBlock?t.startBlock.push(r.start):t.startBlock=[r.start]:r.level==="inline"&&(t.startInline?t.startInline.push(r.start):t.startInline=[r.start]))}"childTokens"in r&&r.childTokens&&(t.childTokens[r.name]=r.childTokens)}),s.extensions=t),n.renderer){const r=this.defaults.renderer||new Q(this.defaults);for(const i in n.renderer){if(!(i in r))throw new Error(`renderer '${i}' does not exist`);if(["options","parser"].includes(i))continue;const o=i,l=n.renderer[o],u=r[o];r[o]=(...h)=>{let p=l.apply(r,h);return p===!1&&(p=u.apply(r,h)),p||""}}s.renderer=r}if(n.tokenizer){const r=this.defaults.tokenizer||new j(this.defaults);for(const i in n.tokenizer){if(!(i in r))throw new Error(`tokenizer '${i}' does not exist`);if(["options","rules","lexer"].includes(i))continue;const o=i,l=n.tokenizer[o],u=r[o];r[o]=(...h)=>{let p=l.apply(r,h);return p===!1&&(p=u.apply(r,h)),p}}s.tokenizer=r}if(n.hooks){const r=this.defaults.hooks||new H;for(const i in n.hooks){if(!(i in r))throw new Error(`hook '${i}' does not exist`);if(["options","block"].includes(i))continue;const o=i,l=n.hooks[o],u=r[o];H.passThroughHooks.has(i)?r[o]=h=>{if(this.defaults.async)return Promise.resolve(l.call(r,h)).then(m=>u.call(r,m));const p=l.call(r,h);return u.call(r,p)}:r[o]=(...h)=>{let p=l.apply(r,h);return p===!1&&(p=u.apply(r,h)),p}}s.hooks=r}if(n.walkTokens){const r=this.defaults.walkTokens,i=n.walkTokens;s.walkTokens=function(o){let l=[];return l.push(i.call(this,o)),r&&(l=l.concat(r.call(this,o))),l}}this.defaults={...this.defaults,...s}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return v.lex(e,t??this.defaults)}parser(e,t){return T.parse(e,t??this.defaults)}parseMarkdown(e){return(n,s)=>{const r={...s},i={...this.defaults,...r},o=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return o(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof n>"u"||n===null)return o(new Error("marked(): input parameter is undefined or null"));if(typeof n!="string")return o(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));i.hooks&&(i.hooks.options=i,i.hooks.block=e);const l=i.hooks?i.hooks.provideLexer():e?v.lex:v.lexInline,u=i.hooks?i.hooks.provideParser():e?T.parse:T.parseInline;if(i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(n):n).then(h=>l(h,i)).then(h=>i.hooks?i.hooks.processAllTokens(h):h).then(h=>i.walkTokens?Promise.all(this.walkTokens(h,i.walkTokens)).then(()=>h):h).then(h=>u(h,i)).then(h=>i.hooks?i.hooks.postprocess(h):h).catch(o);try{i.hooks&&(n=i.hooks.preprocess(n));let h=l(n,i);i.hooks&&(h=i.hooks.processAllTokens(h)),i.walkTokens&&this.walkTokens(h,i.walkTokens);let p=u(h,i);return i.hooks&&(p=i.hooks.postprocess(p)),p}catch(h){return o(h)}}}onError(e,t){return n=>{if(n.message+=`
+Please report this to https://github.com/markedjs/marked.`,e){const s="
An error occurred:
"+R(n.message+"",!0)+"
";return t?Promise.resolve(s):s}if(t)return Promise.reject(n);throw n}}}const A=new Ot;function k(a,e){return A.parse(a,e)}k.options=k.setOptions=function(a){return A.setOptions(a),k.defaults=A.defaults,$e(k.defaults),k};k.getDefaults=te;k.defaults=L;k.use=function(...a){return A.use(...a),k.defaults=A.defaults,$e(k.defaults),k};k.walkTokens=function(a,e){return A.walkTokens(a,e)};k.parseInline=A.parseInline;k.Parser=T;k.parser=T.parse;k.Renderer=Q;k.TextRenderer=ce;k.Lexer=v;k.lexer=v.lex;k.Tokenizer=j;k.Hooks=H;k.parse=k;k.options;k.setOptions;k.use;k.walkTokens;k.parseInline;T.parse;v.lex;const jt={key:"header",class:"shadow"},Qt={class:"p-3 d-flex gap-2 flex-column"},Wt={class:"d-flex text-body"},Ut={class:"d-flex flex-column align-items-start gap-1"},Ft={class:"mb-0"},Vt={class:"mb-0"},Xt={class:"list-group"},Jt={href:"https://donaldzou.github.io/WGDashboard-Documentation/",target:"_blank",class:"list-group-item list-group-item-action d-flex align-items-center"},Yt={target:"_blank",role:"button",href:"https://discord.gg/72TwzjeuWm",class:"list-group-item list-group-item-action d-flex align-items-center"},Kt={__name:"agentModal",emits:["close"],setup(a,{emit:e}){const t=e,n=ee();return(s,r)=>(x(),S("div",{class:X(["agentContainer m-2 rounded-3 d-flex flex-column text-body",{enabled:Ee(n).HelpAgent.Enable}])},[d(we,{name:"agent-message"},{default:y(()=>[c("div",jt,[c("div",Qt,[c("div",Wt,[c("div",Ut,[c("h5",Ft,[d(C,{t:"Help"})])]),c("a",{role:"button",class:"ms-auto text-body",onClick:r[0]||(r[0]=i=>t("close"))},r[1]||(r[1]=[c("h5",{class:"mb-0"},[c("i",{class:"bi bi-x-lg"})],-1)]))]),c("p",Vt,[d(C,{t:"You can visit our: "})]),c("div",Xt,[c("a",Jt,[r[2]||(r[2]=c("i",{class:"bi bi-book-fill"},null,-1)),d(C,{class:"ms-auto",t:"Official Documentation"})]),c("a",Yt,[r[3]||(r[3]=c("i",{class:"bi bi-discord"},null,-1)),d(C,{class:"ms-auto",t:"Discord Server"})])])])])]),_:1})],2))}},en=K(Kt,[["__scopeId","data-v-694cfad1"]]),tn={name:"navbar",components:{HelpModal:lt,LocaleText:C,AgentModal:en},setup(){const a=De(),e=ee();return{wireguardConfigurationsStore:a,dashboardConfigurationStore:e}},data(){return{updateAvailable:!1,updateMessage:"Checking for update...",updateUrl:"",openHelpModal:!1,openAgentModal:!1}},computed:{getActiveCrossServer(){if(this.dashboardConfigurationStore.ActiveServerConfiguration)return new URL(this.dashboardConfigurationStore.CrossServerConfiguration.ServerList[this.dashboardConfigurationStore.ActiveServerConfiguration].host)}},mounted(){qe("/api/getDashboardUpdate",{},a=>{a.status?(a.data&&(this.updateAvailable=!0,this.updateUrl=a.data),this.updateMessage=a.message):(this.updateMessage=Ge("Failed to check available update"),console.log(`Failed to get update: ${a.message}`))})}},nn=["data-bs-theme"],sn={id:"sidebarMenu",class:"bg-body-tertiary sidebar border h-100 rounded-3 shadow overflow-y-scroll"},rn={class:"sidebar-sticky"},on={class:"text-white text-center m-0 py-3 mb-2 btn-brand"},ln={key:0,class:"ms-auto"},an={class:"nav flex-column px-2 gap-1"},cn={class:"nav-item"},un={class:"nav-item"},hn={class:"nav-item"},pn={class:"sidebar-heading px-3 mt-3 mb-1 text-muted text-center"},dn={class:"nav flex-column px-2 gap-1"},gn={class:"nav-item"},fn={class:"sidebar-heading px-3 mt-3 mb-1 text-muted text-center"},kn={class:"nav flex-column px-2 gap-1"},bn={class:"nav-item"},mn={class:"nav-item"},xn={class:"nav-item"},wn={class:"nav flex-column px-2 mb-3"},_n={class:"nav-item"},yn={class:"nav-item",style:{"font-size":"0.8rem"}},$n=["href"],Sn={class:"nav-link text-muted rounded-3"},vn={key:1,class:"nav-link text-muted rounded-3"};function Tn(a,e,t,n,s,r){const i=z("LocaleText"),o=z("RouterLink"),l=z("HelpModal"),u=z("AgentModal");return x(),S("div",{class:X(["col-md-3 col-lg-2 d-md-block p-2 navbar-container",{active:this.dashboardConfigurationStore.ShowNavBar}]),"data-bs-theme":n.dashboardConfigurationStore.Configuration.Server.dashboard_theme},[c("nav",sn,[c("div",rn,[c("div",on,[e[5]||(e[5]=c("h5",{class:"mb-0"}," WGDashboard ",-1)),r.getActiveCrossServer!==void 0?(x(),S("small",ln,[e[4]||(e[4]=c("i",{class:"bi bi-hdd-rack-fill me-2"},null,-1)),I(E(r.getActiveCrossServer.host),1)])):D("",!0)]),c("ul",an,[c("li",cn,[d(o,{class:"nav-link rounded-3",to:"/","exact-active-class":"active"},{default:y(()=>[e[6]||(e[6]=c("i",{class:"bi bi-house me-2"},null,-1)),d(i,{t:"Home"})]),_:1})]),c("li",un,[d(o,{class:"nav-link rounded-3",to:"/settings","exact-active-class":"active"},{default:y(()=>[e[7]||(e[7]=c("i",{class:"bi bi-gear me-2"},null,-1)),d(i,{t:"Settings"})]),_:1})]),c("li",hn,[c("a",{class:"nav-link rounded-3",role:"button",onClick:e[0]||(e[0]=h=>s.openAgentModal=!0)},[e[8]||(e[8]=c("i",{class:"bi bi-question-circle me-2"},null,-1)),d(i,{t:"Help"})])])]),e[11]||(e[11]=c("hr",{class:"text-body my-2"},null,-1)),c("h6",pn,[d(i,{t:"WireGuard Configurations"})]),c("ul",dn,[(x(!0),S(_e,null,ye(this.wireguardConfigurationsStore.Configurations,h=>(x(),S("li",gn,[d(o,{to:"/configuration/"+h.Name+"/peers",class:"nav-link nav-conf-link rounded-3","active-class":"active"},{default:y(()=>[c("span",{class:X(["dot me-2",{active:h.Status}])},null,2),I(" "+E(h.Name),1)]),_:2},1032,["to"])]))),256))]),e[12]||(e[12]=c("hr",{class:"text-body my-2"},null,-1)),c("h6",fn,[d(i,{t:"Tools"})]),c("ul",kn,[c("li",bn,[d(o,{to:"/system_status",class:"nav-link rounded-3","active-class":"active"},{default:y(()=>[d(i,{t:"System Status"})]),_:1})]),c("li",mn,[d(o,{to:"/ping",class:"nav-link rounded-3","active-class":"active"},{default:y(()=>[d(i,{t:"Ping"})]),_:1})]),c("li",xn,[d(o,{to:"/traceroute",class:"nav-link rounded-3","active-class":"active"},{default:y(()=>[d(i,{t:"Traceroute"})]),_:1})])]),e[13]||(e[13]=c("hr",{class:"text-body my-2"},null,-1)),c("ul",wn,[c("li",_n,[c("a",{class:"nav-link text-danger rounded-3",onClick:e[1]||(e[1]=h=>this.dashboardConfigurationStore.signOut()),role:"button",style:{"font-weight":"bold"}},[e[9]||(e[9]=c("i",{class:"bi bi-box-arrow-left me-2"},null,-1)),d(i,{t:"Sign Out"})])]),c("li",yn,[this.updateAvailable?(x(),S("a",{key:0,href:this.updateUrl,class:"text-decoration-none rounded-3",target:"_blank"},[c("small",Sn,[d(i,{t:this.updateMessage},null,8,["t"]),e[10]||(e[10]=I(" (")),d(i,{t:"Current Version:"}),I(" "+E(n.dashboardConfigurationStore.Configuration.Server.version)+") ",1)])],8,$n)):(x(),S("small",vn,[d(i,{t:this.updateMessage},null,8,["t"]),I(" ("+E(n.dashboardConfigurationStore.Configuration.Server.version)+") ",1)]))])])])]),d(J,{name:"zoom"},{default:y(()=>[this.openHelpModal?(x(),q(l,{key:0,onClose:e[2]||(e[2]=h=>{s.openHelpModal=!1})})):D("",!0)]),_:1}),d(J,{name:"slideIn"},{default:y(()=>[this.openAgentModal?(x(),q(u,{key:0,onClose:e[3]||(e[3]=h=>s.openAgentModal=!1)})):D("",!0)]),_:1})],10,nn)}const Rn=K(tn,[["render",Tn],["__scopeId","data-v-58e71749"]]),Cn={name:"index",components:{Message:He,Navbar:Rn},async setup(){return{dashboardConfigurationStore:ee()}},computed:{getMessages(){return this.dashboardConfigurationStore.Messages.filter(a=>a.show)}}},zn=["data-bs-theme"],An={class:"row h-100"},Ln={class:"col-md-9 col-lg-10 overflow-y-scroll mb-0 pt-2"},In={class:"messageCentre text-body position-absolute d-flex"};function Pn(a,e,t,n,s,r){const i=z("Navbar"),o=z("RouterView"),l=z("Message");return x(),S("div",{class:"container-fluid flex-grow-1 main","data-bs-theme":this.dashboardConfigurationStore.Configuration.Server.dashboard_theme},[c("div",An,[d(i),c("main",Ln,[(x(),q(Ze,null,{default:y(()=>[d(o,null,{default:y(({Component:u})=>[d(J,{name:"fade2",mode:"out-in",appear:""},{default:y(()=>[(x(),q(Ne(u)))]),_:2},1024)]),_:1})]),_:1})),c("div",In,[d(we,{name:"message",tag:"div",class:"position-relative flex-sm-grow-0 flex-grow-1 d-flex align-items-end ms-sm-auto flex-column gap-2"},{default:y(()=>[(x(!0),S(_e,null,ye(r.getMessages.slice().reverse(),u=>(x(),q(l,{message:u,key:u.id},null,8,["message"]))),128))]),_:1})])])])],8,zn)}const qn=K(Cn,[["render",Pn],["__scopeId","data-v-0c6a5068"]]);export{qn as default};
diff --git a/src/static/app/dist/assets/index-BKNwSldE.css b/src/static/app/dist/assets/index-BKNwSldE.css
deleted file mode 100644
index 7049406..0000000
--- a/src/static/app/dist/assets/index-BKNwSldE.css
+++ /dev/null
@@ -1 +0,0 @@
-@media screen and (max-width: 768px){.navbar-container[data-v-83a7789f]{position:absolute;z-index:1000;animation-duration:.4s;animation-fill-mode:both;display:none;animation-timing-function:cubic-bezier(.82,.58,.17,.9)}.navbar-container.active[data-v-83a7789f]{animation-direction:normal;display:block!important;animation-name:zoomInFade-83a7789f}}.navbar-container[data-v-83a7789f]{height:100vh}@supports (height: 100dvh){@media screen and (max-width: 768px){.navbar-container[data-v-83a7789f]{height:calc(100dvh - 50px)}}}@keyframes zoomInFade-83a7789f{0%{opacity:0;transform:translateY(60px);filter:blur(3px)}to{opacity:1;transform:translateY(0);filter:blur(0px)}}.messageCentre[data-v-ce114a8b]{top:1rem;right:1rem;width:calc(100% - 2rem)}main[data-v-ce114a8b]{height:100vh}@supports (height: 100dvh){@media screen and (max-width: 768px){main[data-v-ce114a8b]{height:calc(100dvh - 50px)}}}
diff --git a/src/static/app/dist/assets/index-CUmHRwBw.js b/src/static/app/dist/assets/index-CUmHRwBw.js
new file mode 100644
index 0000000..6ab8207
--- /dev/null
+++ b/src/static/app/dist/assets/index-CUmHRwBw.js
@@ -0,0 +1,44 @@
+const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-BA3_cn6G.js","assets/localeText-DvaqSAco.js","assets/dayjs.min-C3XPfvH9.js","assets/message-2XqhwcFu.js","assets/message-BaDb-qC9.css","assets/index-24JS7yYN.css","assets/configurationList-DZKIqimE.js","assets/protocolBadge-DbaEYoS7.js","assets/storageMount.vue_vue_type_style_index_0_scoped_a382214a_lang-C_2M2VRO.js","assets/storageMount-Dupif2Js.css","assets/configurationList-B0Mt7yx0.css","assets/settings-D1LSxAUz.js","assets/vue-datepicker-CUbRHf9G.js","assets/settings-DbQs6Bt_.css","assets/ping-B3bzy0ym.js","assets/osmap-C861OkPV.js","assets/osmap-CoctJCk_.css","assets/ping-DojRH9NX.css","assets/traceroute-ig4xVVA0.js","assets/traceroute-DH1nb6XH.css","assets/newConfiguration-mzP0wOFD.js","assets/index-L60y6kc9.js","assets/newConfiguration-bw8DpVTw.css","assets/restoreConfiguration-CAJMdZrL.js","assets/restoreConfiguration-VgIx_N7z.css","assets/systemStatus-BoEqFTbp.js","assets/index-B6Dvwg0x.js","assets/systemStatus-C-kMgV1x.css","assets/peerList-CRpIz0h9.js","assets/peerList-CyFwBRNy.css","assets/signin-BlxyYwNM.js","assets/signin-CHulm0U0.css","assets/setup-CuoR7X7A.js","assets/totp-DUDVhlQR.js","assets/browser-CjSdxGTc.js","assets/share-DqcU1eRK.js","assets/share-B4McccvP.css"])))=>i.map(i=>d[i]);
+(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))s(r);new MutationObserver(r=>{for(const o of r)if(o.type==="childList")for(const a of o.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&s(a)}).observe(document,{childList:!0,subtree:!0});function n(r){const o={};return r.integrity&&(o.integrity=r.integrity),r.referrerPolicy&&(o.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?o.credentials="include":r.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function s(r){if(r.ep)return;r.ep=!0;const o=n(r);fetch(r.href,o)}})();var wg=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function cb(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Cg(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var n=function s(){return this instanceof s?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};n.prototype=t.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(e).forEach(function(s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(n,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})}),n}var Sg={exports:{}},qe="top",nt="bottom",st="right",ze="left",Kr="auto",ds=[qe,nt,st,ze],On="start",ts="end",vc="clippingParents",fo="viewport",Wn="popper",Ec="reference",Bi=ds.reduce(function(e,t){return e.concat([t+"-"+On,t+"-"+ts])},[]),ho=[].concat(ds,[Kr]).reduce(function(e,t){return e.concat([t,t+"-"+On,t+"-"+ts])},[]),yc="beforeRead",bc="read",Ac="afterRead",Tc="beforeMain",wc="main",Cc="afterMain",Sc="beforeWrite",Oc="write",Nc="afterWrite",xc=[yc,bc,Ac,Tc,wc,Cc,Sc,Oc,Nc];function xt(e){return e?(e.nodeName||"").toLowerCase():null}function rt(e){if(e==null)return window;if(e.toString()!=="[object Window]"){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function Nn(e){var t=rt(e).Element;return e instanceof t||e instanceof Element}function ct(e){var t=rt(e).HTMLElement;return e instanceof t||e instanceof HTMLElement}function po(e){if(typeof ShadowRoot>"u")return!1;var t=rt(e).ShadowRoot;return e instanceof t||e instanceof ShadowRoot}function Og(e){var t=e.state;Object.keys(t.elements).forEach(function(n){var s=t.styles[n]||{},r=t.attributes[n]||{},o=t.elements[n];!ct(o)||!xt(o)||(Object.assign(o.style,s),Object.keys(r).forEach(function(a){var l=r[a];l===!1?o.removeAttribute(a):o.setAttribute(a,l===!0?"":l)}))})}function Ng(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach(function(s){var r=t.elements[s],o=t.attributes[s]||{},a=Object.keys(t.styles.hasOwnProperty(s)?t.styles[s]:n[s]),l=a.reduce(function(c,d){return c[d]="",c},{});!ct(r)||!xt(r)||(Object.assign(r.style,l),Object.keys(o).forEach(function(c){r.removeAttribute(c)}))})}}const go={name:"applyStyles",enabled:!0,phase:"write",fn:Og,effect:Ng,requires:["computeStyles"]};function Ot(e){return e.split("-")[0]}var Tn=Math.max,Dr=Math.min,ns=Math.round;function ji(){var e=navigator.userAgentData;return e!=null&&e.brands&&Array.isArray(e.brands)?e.brands.map(function(t){return t.brand+"/"+t.version}).join(" "):navigator.userAgent}function $c(){return!/^((?!chrome|android).)*safari/i.test(ji())}function ss(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!1);var s=e.getBoundingClientRect(),r=1,o=1;t&&ct(e)&&(r=e.offsetWidth>0&&ns(s.width)/e.offsetWidth||1,o=e.offsetHeight>0&&ns(s.height)/e.offsetHeight||1);var a=Nn(e)?rt(e):window,l=a.visualViewport,c=!$c()&&n,d=(s.left+(c&&l?l.offsetLeft:0))/r,f=(s.top+(c&&l?l.offsetTop:0))/o,h=s.width/r,p=s.height/o;return{width:h,height:p,top:f,right:d+h,bottom:f+p,left:d,x:d,y:f}}function mo(e){var t=ss(e),n=e.offsetWidth,s=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-s)<=1&&(s=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:s}}function Dc(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&po(n)){var s=t;do{if(s&&e.isSameNode(s))return!0;s=s.parentNode||s.host}while(s)}return!1}function Bt(e){return rt(e).getComputedStyle(e)}function xg(e){return["table","td","th"].indexOf(xt(e))>=0}function on(e){return((Nn(e)?e.ownerDocument:e.document)||window.document).documentElement}function Wr(e){return xt(e)==="html"?e:e.assignedSlot||e.parentNode||(po(e)?e.host:null)||on(e)}function Ga(e){return!ct(e)||Bt(e).position==="fixed"?null:e.offsetParent}function $g(e){var t=/firefox/i.test(ji()),n=/Trident/i.test(ji());if(n&&ct(e)){var s=Bt(e);if(s.position==="fixed")return null}var r=Wr(e);for(po(r)&&(r=r.host);ct(r)&&["html","body"].indexOf(xt(r))<0;){var o=Bt(r);if(o.transform!=="none"||o.perspective!=="none"||o.contain==="paint"||["transform","perspective"].indexOf(o.willChange)!==-1||t&&o.willChange==="filter"||t&&o.filter&&o.filter!=="none")return r;r=r.parentNode}return null}function Gs(e){for(var t=rt(e),n=Ga(e);n&&xg(n)&&Bt(n).position==="static";)n=Ga(n);return n&&(xt(n)==="html"||xt(n)==="body"&&Bt(n).position==="static")?t:n||$g(e)||t}function _o(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function Ns(e,t,n){return Tn(e,Dr(t,n))}function Dg(e,t,n){var s=Ns(e,t,n);return s>n?n:s}function Lc(){return{top:0,right:0,bottom:0,left:0}}function Rc(e){return Object.assign({},Lc(),e)}function Ic(e,t){return t.reduce(function(n,s){return n[s]=e,n},{})}var Lg=function(t,n){return t=typeof t=="function"?t(Object.assign({},n.rects,{placement:n.placement})):t,Rc(typeof t!="number"?t:Ic(t,ds))};function Rg(e){var t,n=e.state,s=e.name,r=e.options,o=n.elements.arrow,a=n.modifiersData.popperOffsets,l=Ot(n.placement),c=_o(l),d=[ze,st].indexOf(l)>=0,f=d?"height":"width";if(!(!o||!a)){var h=Lg(r.padding,n),p=mo(o),E=c==="y"?qe:ze,S=c==="y"?nt:st,C=n.rects.reference[f]+n.rects.reference[c]-a[c]-n.rects.popper[f],D=a[c]-n.rects.reference[c],P=Gs(o),V=P?c==="y"?P.clientHeight||0:P.clientWidth||0:0,k=C/2-D/2,M=h[E],b=V-p[f]-h[S],y=V/2-p[f]/2+k,O=Ns(M,y,b),T=c;n.modifiersData[s]=(t={},t[T]=O,t.centerOffset=O-y,t)}}function Ig(e){var t=e.state,n=e.options,s=n.element,r=s===void 0?"[data-popper-arrow]":s;r!=null&&(typeof r=="string"&&(r=t.elements.popper.querySelector(r),!r)||Dc(t.elements.popper,r)&&(t.elements.arrow=r))}const Pc={name:"arrow",enabled:!0,phase:"main",fn:Rg,effect:Ig,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function rs(e){return e.split("-")[1]}var Pg={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Mg(e,t){var n=e.x,s=e.y,r=t.devicePixelRatio||1;return{x:ns(n*r)/r||0,y:ns(s*r)/r||0}}function qa(e){var t,n=e.popper,s=e.popperRect,r=e.placement,o=e.variation,a=e.offsets,l=e.position,c=e.gpuAcceleration,d=e.adaptive,f=e.roundOffsets,h=e.isFixed,p=a.x,E=p===void 0?0:p,S=a.y,C=S===void 0?0:S,D=typeof f=="function"?f({x:E,y:C}):{x:E,y:C};E=D.x,C=D.y;var P=a.hasOwnProperty("x"),V=a.hasOwnProperty("y"),k=ze,M=qe,b=window;if(d){var y=Gs(n),O="clientHeight",T="clientWidth";if(y===rt(n)&&(y=on(n),Bt(y).position!=="static"&&l==="absolute"&&(O="scrollHeight",T="scrollWidth")),y=y,r===qe||(r===ze||r===st)&&o===ts){M=nt;var A=h&&y===b&&b.visualViewport?b.visualViewport.height:y[O];C-=A-s.height,C*=c?1:-1}if(r===ze||(r===qe||r===nt)&&o===ts){k=st;var w=h&&y===b&&b.visualViewport?b.visualViewport.width:y[T];E-=w-s.width,E*=c?1:-1}}var j=Object.assign({position:l},d&&Pg),B=f===!0?Mg({x:E,y:C},rt(n)):{x:E,y:C};if(E=B.x,C=B.y,c){var Q;return Object.assign({},j,(Q={},Q[M]=V?"0":"",Q[k]=P?"0":"",Q.transform=(b.devicePixelRatio||1)<=1?"translate("+E+"px, "+C+"px)":"translate3d("+E+"px, "+C+"px, 0)",Q))}return Object.assign({},j,(t={},t[M]=V?C+"px":"",t[k]=P?E+"px":"",t.transform="",t))}function kg(e){var t=e.state,n=e.options,s=n.gpuAcceleration,r=s===void 0?!0:s,o=n.adaptive,a=o===void 0?!0:o,l=n.roundOffsets,c=l===void 0?!0:l,d={placement:Ot(t.placement),variation:rs(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:r,isFixed:t.options.strategy==="fixed"};t.modifiersData.popperOffsets!=null&&(t.styles.popper=Object.assign({},t.styles.popper,qa(Object.assign({},d,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:a,roundOffsets:c})))),t.modifiersData.arrow!=null&&(t.styles.arrow=Object.assign({},t.styles.arrow,qa(Object.assign({},d,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:c})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})}const vo={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:kg,data:{}};var dr={passive:!0};function Vg(e){var t=e.state,n=e.instance,s=e.options,r=s.scroll,o=r===void 0?!0:r,a=s.resize,l=a===void 0?!0:a,c=rt(t.elements.popper),d=[].concat(t.scrollParents.reference,t.scrollParents.popper);return o&&d.forEach(function(f){f.addEventListener("scroll",n.update,dr)}),l&&c.addEventListener("resize",n.update,dr),function(){o&&d.forEach(function(f){f.removeEventListener("scroll",n.update,dr)}),l&&c.removeEventListener("resize",n.update,dr)}}const Eo={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:Vg,data:{}};var Fg={left:"right",right:"left",bottom:"top",top:"bottom"};function wr(e){return e.replace(/left|right|bottom|top/g,function(t){return Fg[t]})}var Hg={start:"end",end:"start"};function za(e){return e.replace(/start|end/g,function(t){return Hg[t]})}function yo(e){var t=rt(e),n=t.pageXOffset,s=t.pageYOffset;return{scrollLeft:n,scrollTop:s}}function bo(e){return ss(on(e)).left+yo(e).scrollLeft}function Bg(e,t){var n=rt(e),s=on(e),r=n.visualViewport,o=s.clientWidth,a=s.clientHeight,l=0,c=0;if(r){o=r.width,a=r.height;var d=$c();(d||!d&&t==="fixed")&&(l=r.offsetLeft,c=r.offsetTop)}return{width:o,height:a,x:l+bo(e),y:c}}function jg(e){var t,n=on(e),s=yo(e),r=(t=e.ownerDocument)==null?void 0:t.body,o=Tn(n.scrollWidth,n.clientWidth,r?r.scrollWidth:0,r?r.clientWidth:0),a=Tn(n.scrollHeight,n.clientHeight,r?r.scrollHeight:0,r?r.clientHeight:0),l=-s.scrollLeft+bo(e),c=-s.scrollTop;return Bt(r||n).direction==="rtl"&&(l+=Tn(n.clientWidth,r?r.clientWidth:0)-o),{width:o,height:a,x:l,y:c}}function Ao(e){var t=Bt(e),n=t.overflow,s=t.overflowX,r=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+r+s)}function Mc(e){return["html","body","#document"].indexOf(xt(e))>=0?e.ownerDocument.body:ct(e)&&Ao(e)?e:Mc(Wr(e))}function xs(e,t){var n;t===void 0&&(t=[]);var s=Mc(e),r=s===((n=e.ownerDocument)==null?void 0:n.body),o=rt(s),a=r?[o].concat(o.visualViewport||[],Ao(s)?s:[]):s,l=t.concat(a);return r?l:l.concat(xs(Wr(a)))}function Ki(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function Kg(e,t){var n=ss(e,!1,t==="fixed");return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}function Xa(e,t,n){return t===fo?Ki(Bg(e,n)):Nn(t)?Kg(t,n):Ki(jg(on(e)))}function Wg(e){var t=xs(Wr(e)),n=["absolute","fixed"].indexOf(Bt(e).position)>=0,s=n&&ct(e)?Gs(e):e;return Nn(s)?t.filter(function(r){return Nn(r)&&Dc(r,s)&&xt(r)!=="body"}):[]}function Ug(e,t,n,s){var r=t==="clippingParents"?Wg(e):[].concat(t),o=[].concat(r,[n]),a=o[0],l=o.reduce(function(c,d){var f=Xa(e,d,s);return c.top=Tn(f.top,c.top),c.right=Dr(f.right,c.right),c.bottom=Dr(f.bottom,c.bottom),c.left=Tn(f.left,c.left),c},Xa(e,a,s));return l.width=l.right-l.left,l.height=l.bottom-l.top,l.x=l.left,l.y=l.top,l}function kc(e){var t=e.reference,n=e.element,s=e.placement,r=s?Ot(s):null,o=s?rs(s):null,a=t.x+t.width/2-n.width/2,l=t.y+t.height/2-n.height/2,c;switch(r){case qe:c={x:a,y:t.y-n.height};break;case nt:c={x:a,y:t.y+t.height};break;case st:c={x:t.x+t.width,y:l};break;case ze:c={x:t.x-n.width,y:l};break;default:c={x:t.x,y:t.y}}var d=r?_o(r):null;if(d!=null){var f=d==="y"?"height":"width";switch(o){case On:c[d]=c[d]-(t[f]/2-n[f]/2);break;case ts:c[d]=c[d]+(t[f]/2-n[f]/2);break}}return c}function is(e,t){t===void 0&&(t={});var n=t,s=n.placement,r=s===void 0?e.placement:s,o=n.strategy,a=o===void 0?e.strategy:o,l=n.boundary,c=l===void 0?vc:l,d=n.rootBoundary,f=d===void 0?fo:d,h=n.elementContext,p=h===void 0?Wn:h,E=n.altBoundary,S=E===void 0?!1:E,C=n.padding,D=C===void 0?0:C,P=Rc(typeof D!="number"?D:Ic(D,ds)),V=p===Wn?Ec:Wn,k=e.rects.popper,M=e.elements[S?V:p],b=Ug(Nn(M)?M:M.contextElement||on(e.elements.popper),c,f,a),y=ss(e.elements.reference),O=kc({reference:y,element:k,strategy:"absolute",placement:r}),T=Ki(Object.assign({},k,O)),A=p===Wn?T:y,w={top:b.top-A.top+P.top,bottom:A.bottom-b.bottom+P.bottom,left:b.left-A.left+P.left,right:A.right-b.right+P.right},j=e.modifiersData.offset;if(p===Wn&&j){var B=j[r];Object.keys(w).forEach(function(Q){var oe=[st,nt].indexOf(Q)>=0?1:-1,he=[qe,nt].indexOf(Q)>=0?"y":"x";w[Q]+=B[he]*oe})}return w}function Yg(e,t){t===void 0&&(t={});var n=t,s=n.placement,r=n.boundary,o=n.rootBoundary,a=n.padding,l=n.flipVariations,c=n.allowedAutoPlacements,d=c===void 0?ho:c,f=rs(s),h=f?l?Bi:Bi.filter(function(S){return rs(S)===f}):ds,p=h.filter(function(S){return d.indexOf(S)>=0});p.length===0&&(p=h);var E=p.reduce(function(S,C){return S[C]=is(e,{placement:C,boundary:r,rootBoundary:o,padding:a})[Ot(C)],S},{});return Object.keys(E).sort(function(S,C){return E[S]-E[C]})}function Gg(e){if(Ot(e)===Kr)return[];var t=wr(e);return[za(e),t,za(t)]}function qg(e){var t=e.state,n=e.options,s=e.name;if(!t.modifiersData[s]._skip){for(var r=n.mainAxis,o=r===void 0?!0:r,a=n.altAxis,l=a===void 0?!0:a,c=n.fallbackPlacements,d=n.padding,f=n.boundary,h=n.rootBoundary,p=n.altBoundary,E=n.flipVariations,S=E===void 0?!0:E,C=n.allowedAutoPlacements,D=t.options.placement,P=Ot(D),V=P===D,k=c||(V||!S?[wr(D)]:Gg(D)),M=[D].concat(k).reduce(function(Be,Pe){return Be.concat(Ot(Pe)===Kr?Yg(t,{placement:Pe,boundary:f,rootBoundary:h,padding:d,flipVariations:S,allowedAutoPlacements:C}):Pe)},[]),b=t.rects.reference,y=t.rects.popper,O=new Map,T=!0,A=M[0],w=0;w=0,he=oe?"width":"height",te=is(t,{placement:j,boundary:f,rootBoundary:h,altBoundary:p,padding:d}),Z=oe?Q?st:ze:Q?nt:qe;b[he]>y[he]&&(Z=wr(Z));var ce=wr(Z),Ce=[];if(o&&Ce.push(te[B]<=0),l&&Ce.push(te[Z]<=0,te[ce]<=0),Ce.every(function(Be){return Be})){A=j,T=!1;break}O.set(j,Ce)}if(T)for(var He=S?3:1,De=function(Pe){var we=M.find(function(R){var q=O.get(R);if(q)return q.slice(0,Pe).every(function(z){return z})});if(we)return A=we,"break"},Ae=He;Ae>0;Ae--){var et=De(Ae);if(et==="break")break}t.placement!==A&&(t.modifiersData[s]._skip=!0,t.placement=A,t.reset=!0)}}const Vc={name:"flip",enabled:!0,phase:"main",fn:qg,requiresIfExists:["offset"],data:{_skip:!1}};function Qa(e,t,n){return n===void 0&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function Ja(e){return[qe,st,nt,ze].some(function(t){return e[t]>=0})}function zg(e){var t=e.state,n=e.name,s=t.rects.reference,r=t.rects.popper,o=t.modifiersData.preventOverflow,a=is(t,{elementContext:"reference"}),l=is(t,{altBoundary:!0}),c=Qa(a,s),d=Qa(l,r,o),f=Ja(c),h=Ja(d);t.modifiersData[n]={referenceClippingOffsets:c,popperEscapeOffsets:d,isReferenceHidden:f,hasPopperEscaped:h},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":f,"data-popper-escaped":h})}const Fc={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:zg};function Xg(e,t,n){var s=Ot(e),r=[ze,qe].indexOf(s)>=0?-1:1,o=typeof n=="function"?n(Object.assign({},t,{placement:e})):n,a=o[0],l=o[1];return a=a||0,l=(l||0)*r,[ze,st].indexOf(s)>=0?{x:l,y:a}:{x:a,y:l}}function Qg(e){var t=e.state,n=e.options,s=e.name,r=n.offset,o=r===void 0?[0,0]:r,a=ho.reduce(function(f,h){return f[h]=Xg(h,t.rects,o),f},{}),l=a[t.placement],c=l.x,d=l.y;t.modifiersData.popperOffsets!=null&&(t.modifiersData.popperOffsets.x+=c,t.modifiersData.popperOffsets.y+=d),t.modifiersData[s]=a}const Hc={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:Qg};function Jg(e){var t=e.state,n=e.name;t.modifiersData[n]=kc({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})}const To={name:"popperOffsets",enabled:!0,phase:"read",fn:Jg,data:{}};function Zg(e){return e==="x"?"y":"x"}function em(e){var t=e.state,n=e.options,s=e.name,r=n.mainAxis,o=r===void 0?!0:r,a=n.altAxis,l=a===void 0?!1:a,c=n.boundary,d=n.rootBoundary,f=n.altBoundary,h=n.padding,p=n.tether,E=p===void 0?!0:p,S=n.tetherOffset,C=S===void 0?0:S,D=is(t,{boundary:c,rootBoundary:d,padding:h,altBoundary:f}),P=Ot(t.placement),V=rs(t.placement),k=!V,M=_o(P),b=Zg(M),y=t.modifiersData.popperOffsets,O=t.rects.reference,T=t.rects.popper,A=typeof C=="function"?C(Object.assign({},t.rects,{placement:t.placement})):C,w=typeof A=="number"?{mainAxis:A,altAxis:A}:Object.assign({mainAxis:0,altAxis:0},A),j=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,B={x:0,y:0};if(y){if(o){var Q,oe=M==="y"?qe:ze,he=M==="y"?nt:st,te=M==="y"?"height":"width",Z=y[M],ce=Z+D[oe],Ce=Z-D[he],He=E?-T[te]/2:0,De=V===On?O[te]:T[te],Ae=V===On?-T[te]:-O[te],et=t.elements.arrow,Be=E&&et?mo(et):{width:0,height:0},Pe=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:Lc(),we=Pe[oe],R=Pe[he],q=Ns(0,O[te],Be[te]),z=k?O[te]/2-He-q-we-w.mainAxis:De-q-we-w.mainAxis,J=k?-O[te]/2+He+q+R+w.mainAxis:Ae+q+R+w.mainAxis,$=t.elements.arrow&&Gs(t.elements.arrow),ge=$?M==="y"?$.clientTop||0:$.clientLeft||0:0,g=(Q=j?.[M])!=null?Q:0,_=Z+z-g-ge,N=Z+J-g,H=Ns(E?Dr(ce,_):ce,Z,E?Tn(Ce,N):Ce);y[M]=H,B[M]=H-Z}if(l){var L,F=M==="x"?qe:ze,G=M==="x"?nt:st,x=y[b],W=b==="y"?"height":"width",K=x+D[F],se=x-D[G],X=[qe,ze].indexOf(P)!==-1,ee=(L=j?.[b])!=null?L:0,re=X?K:x-O[W]-T[W]-ee+w.altAxis,ae=X?x+O[W]+T[W]-ee-w.altAxis:se,de=E&&X?Dg(re,x,ae):Ns(E?re:K,x,E?ae:se);y[b]=de,B[b]=de-x}t.modifiersData[s]=B}}const Bc={name:"preventOverflow",enabled:!0,phase:"main",fn:em,requiresIfExists:["offset"]};function tm(e){return{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}}function nm(e){return e===rt(e)||!ct(e)?yo(e):tm(e)}function sm(e){var t=e.getBoundingClientRect(),n=ns(t.width)/e.offsetWidth||1,s=ns(t.height)/e.offsetHeight||1;return n!==1||s!==1}function rm(e,t,n){n===void 0&&(n=!1);var s=ct(t),r=ct(t)&&sm(t),o=on(t),a=ss(e,r,n),l={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(s||!s&&!n)&&((xt(t)!=="body"||Ao(o))&&(l=nm(t)),ct(t)?(c=ss(t,!0),c.x+=t.clientLeft,c.y+=t.clientTop):o&&(c.x=bo(o))),{x:a.left+l.scrollLeft-c.x,y:a.top+l.scrollTop-c.y,width:a.width,height:a.height}}function im(e){var t=new Map,n=new Set,s=[];e.forEach(function(o){t.set(o.name,o)});function r(o){n.add(o.name);var a=[].concat(o.requires||[],o.requiresIfExists||[]);a.forEach(function(l){if(!n.has(l)){var c=t.get(l);c&&r(c)}}),s.push(o)}return e.forEach(function(o){n.has(o.name)||r(o)}),s}function om(e){var t=im(e);return xc.reduce(function(n,s){return n.concat(t.filter(function(r){return r.phase===s}))},[])}function am(e){var t;return function(){return t||(t=new Promise(function(n){Promise.resolve().then(function(){t=void 0,n(e())})})),t}}function lm(e){var t=e.reduce(function(n,s){var r=n[s.name];return n[s.name]=r?Object.assign({},r,s,{options:Object.assign({},r.options,s.options),data:Object.assign({},r.data,s.data)}):s,n},{});return Object.keys(t).map(function(n){return t[n]})}var Za={placement:"bottom",modifiers:[],strategy:"absolute"};function el(){for(var e=arguments.length,t=new Array(e),n=0;nm[u]})}}return i.default=m,Object.freeze(i)}const r=s(n),o=new Map,a={set(m,i,u){o.has(m)||o.set(m,new Map);const v=o.get(m);if(!v.has(i)&&v.size!==0){console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(v.keys())[0]}.`);return}v.set(i,u)},get(m,i){return o.has(m)&&o.get(m).get(i)||null},remove(m,i){if(!o.has(m))return;const u=o.get(m);u.delete(i),u.size===0&&o.delete(m)}},l=1e6,c=1e3,d="transitionend",f=m=>(m&&window.CSS&&window.CSS.escape&&(m=m.replace(/#([^\s"#']+)/g,(i,u)=>`#${CSS.escape(u)}`)),m),h=m=>m==null?`${m}`:Object.prototype.toString.call(m).match(/\s([a-z]+)/i)[1].toLowerCase(),p=m=>{do m+=Math.floor(Math.random()*l);while(document.getElementById(m));return m},E=m=>{if(!m)return 0;let{transitionDuration:i,transitionDelay:u}=window.getComputedStyle(m);const v=Number.parseFloat(i),I=Number.parseFloat(u);return!v&&!I?0:(i=i.split(",")[0],u=u.split(",")[0],(Number.parseFloat(i)+Number.parseFloat(u))*c)},S=m=>{m.dispatchEvent(new Event(d))},C=m=>!m||typeof m!="object"?!1:(typeof m.jquery<"u"&&(m=m[0]),typeof m.nodeType<"u"),D=m=>C(m)?m.jquery?m[0]:m:typeof m=="string"&&m.length>0?document.querySelector(f(m)):null,P=m=>{if(!C(m)||m.getClientRects().length===0)return!1;const i=getComputedStyle(m).getPropertyValue("visibility")==="visible",u=m.closest("details:not([open])");if(!u)return i;if(u!==m){const v=m.closest("summary");if(v&&v.parentNode!==u||v===null)return!1}return i},V=m=>!m||m.nodeType!==Node.ELEMENT_NODE||m.classList.contains("disabled")?!0:typeof m.disabled<"u"?m.disabled:m.hasAttribute("disabled")&&m.getAttribute("disabled")!=="false",k=m=>{if(!document.documentElement.attachShadow)return null;if(typeof m.getRootNode=="function"){const i=m.getRootNode();return i instanceof ShadowRoot?i:null}return m instanceof ShadowRoot?m:m.parentNode?k(m.parentNode):null},M=()=>{},b=m=>{m.offsetHeight},y=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,O=[],T=m=>{document.readyState==="loading"?(O.length||document.addEventListener("DOMContentLoaded",()=>{for(const i of O)i()}),O.push(m)):m()},A=()=>document.documentElement.dir==="rtl",w=m=>{T(()=>{const i=y();if(i){const u=m.NAME,v=i.fn[u];i.fn[u]=m.jQueryInterface,i.fn[u].Constructor=m,i.fn[u].noConflict=()=>(i.fn[u]=v,m.jQueryInterface)}})},j=(m,i=[],u=m)=>typeof m=="function"?m(...i):u,B=(m,i,u=!0)=>{if(!u){j(m);return}const I=E(i)+5;let Y=!1;const U=({target:le})=>{le===i&&(Y=!0,i.removeEventListener(d,U),j(m))};i.addEventListener(d,U),setTimeout(()=>{Y||S(i)},I)},Q=(m,i,u,v)=>{const I=m.length;let Y=m.indexOf(i);return Y===-1?!u&&v?m[I-1]:m[0]:(Y+=u?1:-1,v&&(Y=(Y+I)%I),m[Math.max(0,Math.min(Y,I-1))])},oe=/[^.]*(?=\..*)\.|.*/,he=/\..*/,te=/::\d+$/,Z={};let ce=1;const Ce={mouseenter:"mouseover",mouseleave:"mouseout"},He=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function De(m,i){return i&&`${i}::${ce++}`||m.uidEvent||ce++}function Ae(m){const i=De(m);return m.uidEvent=i,Z[i]=Z[i]||{},Z[i]}function et(m,i){return function u(v){return ge(v,{delegateTarget:m}),u.oneOff&&$.off(m,v.type,i),i.apply(m,[v])}}function Be(m,i,u){return function v(I){const Y=m.querySelectorAll(i);for(let{target:U}=I;U&&U!==this;U=U.parentNode)for(const le of Y)if(le===U)return ge(I,{delegateTarget:U}),v.oneOff&&$.off(m,I.type,i,u),u.apply(U,[I])}}function Pe(m,i,u=null){return Object.values(m).find(v=>v.callable===i&&v.delegationSelector===u)}function we(m,i,u){const v=typeof i=="string",I=v?u:i||u;let Y=J(m);return He.has(Y)||(Y=m),[v,I,Y]}function R(m,i,u,v,I){if(typeof i!="string"||!m)return;let[Y,U,le]=we(i,u,v);i in Ce&&(U=(Tg=>function(Bn){if(!Bn.relatedTarget||Bn.relatedTarget!==Bn.delegateTarget&&!Bn.delegateTarget.contains(Bn.relatedTarget))return Tg.call(this,Bn)})(U));const Xe=Ae(m),ot=Xe[le]||(Xe[le]={}),$e=Pe(ot,U,Y?u:null);if($e){$e.oneOff=$e.oneOff&&I;return}const At=De(U,i.replace(oe,"")),gt=Y?Be(m,u,U):et(m,U);gt.delegationSelector=Y?u:null,gt.callable=U,gt.oneOff=I,gt.uidEvent=At,ot[At]=gt,m.addEventListener(le,gt,Y)}function q(m,i,u,v,I){const Y=Pe(i[u],v,I);Y&&(m.removeEventListener(u,Y,!!I),delete i[u][Y.uidEvent])}function z(m,i,u,v){const I=i[u]||{};for(const[Y,U]of Object.entries(I))Y.includes(v)&&q(m,i,u,U.callable,U.delegationSelector)}function J(m){return m=m.replace(he,""),Ce[m]||m}const $={on(m,i,u,v){R(m,i,u,v,!1)},one(m,i,u,v){R(m,i,u,v,!0)},off(m,i,u,v){if(typeof i!="string"||!m)return;const[I,Y,U]=we(i,u,v),le=U!==i,Xe=Ae(m),ot=Xe[U]||{},$e=i.startsWith(".");if(typeof Y<"u"){if(!Object.keys(ot).length)return;q(m,Xe,U,Y,I?u:null);return}if($e)for(const At of Object.keys(Xe))z(m,Xe,At,i.slice(1));for(const[At,gt]of Object.entries(ot)){const fr=At.replace(te,"");(!le||i.includes(fr))&&q(m,Xe,U,gt.callable,gt.delegationSelector)}},trigger(m,i,u){if(typeof i!="string"||!m)return null;const v=y(),I=J(i),Y=i!==I;let U=null,le=!0,Xe=!0,ot=!1;Y&&v&&(U=v.Event(i,u),v(m).trigger(U),le=!U.isPropagationStopped(),Xe=!U.isImmediatePropagationStopped(),ot=U.isDefaultPrevented());const $e=ge(new Event(i,{bubbles:le,cancelable:!0}),u);return ot&&$e.preventDefault(),Xe&&m.dispatchEvent($e),$e.defaultPrevented&&U&&U.preventDefault(),$e}};function ge(m,i={}){for(const[u,v]of Object.entries(i))try{m[u]=v}catch{Object.defineProperty(m,u,{configurable:!0,get(){return v}})}return m}function g(m){if(m==="true")return!0;if(m==="false")return!1;if(m===Number(m).toString())return Number(m);if(m===""||m==="null")return null;if(typeof m!="string")return m;try{return JSON.parse(decodeURIComponent(m))}catch{return m}}function _(m){return m.replace(/[A-Z]/g,i=>`-${i.toLowerCase()}`)}const N={setDataAttribute(m,i,u){m.setAttribute(`data-bs-${_(i)}`,u)},removeDataAttribute(m,i){m.removeAttribute(`data-bs-${_(i)}`)},getDataAttributes(m){if(!m)return{};const i={},u=Object.keys(m.dataset).filter(v=>v.startsWith("bs")&&!v.startsWith("bsConfig"));for(const v of u){let I=v.replace(/^bs/,"");I=I.charAt(0).toLowerCase()+I.slice(1,I.length),i[I]=g(m.dataset[v])}return i},getDataAttribute(m,i){return g(m.getAttribute(`data-bs-${_(i)}`))}};class H{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(i){return i=this._mergeConfigObj(i),i=this._configAfterMerge(i),this._typeCheckConfig(i),i}_configAfterMerge(i){return i}_mergeConfigObj(i,u){const v=C(u)?N.getDataAttribute(u,"config"):{};return{...this.constructor.Default,...typeof v=="object"?v:{},...C(u)?N.getDataAttributes(u):{},...typeof i=="object"?i:{}}}_typeCheckConfig(i,u=this.constructor.DefaultType){for(const[v,I]of Object.entries(u)){const Y=i[v],U=C(Y)?"element":h(Y);if(!new RegExp(I).test(U))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${v}" provided type "${U}" but expected type "${I}".`)}}}const L="5.3.3";class F extends H{constructor(i,u){super(),i=D(i),i&&(this._element=i,this._config=this._getConfig(u),a.set(this._element,this.constructor.DATA_KEY,this))}dispose(){a.remove(this._element,this.constructor.DATA_KEY),$.off(this._element,this.constructor.EVENT_KEY);for(const i of Object.getOwnPropertyNames(this))this[i]=null}_queueCallback(i,u,v=!0){B(i,u,v)}_getConfig(i){return i=this._mergeConfigObj(i,this._element),i=this._configAfterMerge(i),this._typeCheckConfig(i),i}static getInstance(i){return a.get(D(i),this.DATA_KEY)}static getOrCreateInstance(i,u={}){return this.getInstance(i)||new this(i,typeof u=="object"?u:null)}static get VERSION(){return L}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(i){return`${i}${this.EVENT_KEY}`}}const G=m=>{let i=m.getAttribute("data-bs-target");if(!i||i==="#"){let u=m.getAttribute("href");if(!u||!u.includes("#")&&!u.startsWith("."))return null;u.includes("#")&&!u.startsWith("#")&&(u=`#${u.split("#")[1]}`),i=u&&u!=="#"?u.trim():null}return i?i.split(",").map(u=>f(u)).join(","):null},x={find(m,i=document.documentElement){return[].concat(...Element.prototype.querySelectorAll.call(i,m))},findOne(m,i=document.documentElement){return Element.prototype.querySelector.call(i,m)},children(m,i){return[].concat(...m.children).filter(u=>u.matches(i))},parents(m,i){const u=[];let v=m.parentNode.closest(i);for(;v;)u.push(v),v=v.parentNode.closest(i);return u},prev(m,i){let u=m.previousElementSibling;for(;u;){if(u.matches(i))return[u];u=u.previousElementSibling}return[]},next(m,i){let u=m.nextElementSibling;for(;u;){if(u.matches(i))return[u];u=u.nextElementSibling}return[]},focusableChildren(m){const i=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map(u=>`${u}:not([tabindex^="-"])`).join(",");return this.find(i,m).filter(u=>!V(u)&&P(u))},getSelectorFromElement(m){const i=G(m);return i&&x.findOne(i)?i:null},getElementFromSelector(m){const i=G(m);return i?x.findOne(i):null},getMultipleElementsFromSelector(m){const i=G(m);return i?x.find(i):[]}},W=(m,i="hide")=>{const u=`click.dismiss${m.EVENT_KEY}`,v=m.NAME;$.on(document,u,`[data-bs-dismiss="${v}"]`,function(I){if(["A","AREA"].includes(this.tagName)&&I.preventDefault(),V(this))return;const Y=x.getElementFromSelector(this)||this.closest(`.${v}`);m.getOrCreateInstance(Y)[i]()})},K="alert",X=".bs.alert",ee=`close${X}`,re=`closed${X}`,ae="fade",de="show";class ue extends F{static get NAME(){return K}close(){if($.trigger(this._element,ee).defaultPrevented)return;this._element.classList.remove(de);const u=this._element.classList.contains(ae);this._queueCallback(()=>this._destroyElement(),this._element,u)}_destroyElement(){this._element.remove(),$.trigger(this._element,re),this.dispose()}static jQueryInterface(i){return this.each(function(){const u=ue.getOrCreateInstance(this);if(typeof i=="string"){if(u[i]===void 0||i.startsWith("_")||i==="constructor")throw new TypeError(`No method named "${i}"`);u[i](this)}})}}W(ue,"close"),w(ue);const je="button",tt=".bs.button",Ke=".data-api",fn="active",_s='[data-bs-toggle="button"]',We=`click${tt}${Ke}`;class Me extends F{static get NAME(){return je}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle(fn))}static jQueryInterface(i){return this.each(function(){const u=Me.getOrCreateInstance(this);i==="toggle"&&u[i]()})}}$.on(document,We,_s,m=>{m.preventDefault();const i=m.target.closest(_s);Me.getOrCreateInstance(i).toggle()}),w(Me);const Zs="swipe",Rn=".bs.swipe",qf=`touchstart${Rn}`,zf=`touchmove${Rn}`,Xf=`touchend${Rn}`,Qf=`pointerdown${Rn}`,Jf=`pointerup${Rn}`,Zf="touch",ed="pen",td="pointer-event",nd=40,sd={endCallback:null,leftCallback:null,rightCallback:null},rd={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class er extends H{constructor(i,u){super(),this._element=i,!(!i||!er.isSupported())&&(this._config=this._getConfig(u),this._deltaX=0,this._supportPointerEvents=!!window.PointerEvent,this._initEvents())}static get Default(){return sd}static get DefaultType(){return rd}static get NAME(){return Zs}dispose(){$.off(this._element,Rn)}_start(i){if(!this._supportPointerEvents){this._deltaX=i.touches[0].clientX;return}this._eventIsPointerPenTouch(i)&&(this._deltaX=i.clientX)}_end(i){this._eventIsPointerPenTouch(i)&&(this._deltaX=i.clientX-this._deltaX),this._handleSwipe(),j(this._config.endCallback)}_move(i){this._deltaX=i.touches&&i.touches.length>1?0:i.touches[0].clientX-this._deltaX}_handleSwipe(){const i=Math.abs(this._deltaX);if(i<=nd)return;const u=i/this._deltaX;this._deltaX=0,u&&j(u>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?($.on(this._element,Qf,i=>this._start(i)),$.on(this._element,Jf,i=>this._end(i)),this._element.classList.add(td)):($.on(this._element,qf,i=>this._start(i)),$.on(this._element,zf,i=>this._move(i)),$.on(this._element,Xf,i=>this._end(i)))}_eventIsPointerPenTouch(i){return this._supportPointerEvents&&(i.pointerType===ed||i.pointerType===Zf)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const id="carousel",Kt=".bs.carousel",oa=".data-api",od="ArrowLeft",ad="ArrowRight",ld=500,vs="next",In="prev",Pn="left",tr="right",cd=`slide${Kt}`,ui=`slid${Kt}`,ud=`keydown${Kt}`,fd=`mouseenter${Kt}`,dd=`mouseleave${Kt}`,hd=`dragstart${Kt}`,pd=`load${Kt}${oa}`,gd=`click${Kt}${oa}`,aa="carousel",nr="active",md="slide",_d="carousel-item-end",vd="carousel-item-start",Ed="carousel-item-next",yd="carousel-item-prev",la=".active",ca=".carousel-item",bd=la+ca,Ad=".carousel-item img",Td=".carousel-indicators",wd="[data-bs-slide], [data-bs-slide-to]",Cd='[data-bs-ride="carousel"]',Sd={[od]:tr,[ad]:Pn},Od={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Nd={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class Mn extends F{constructor(i,u){super(i,u),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=x.findOne(Td,this._element),this._addEventListeners(),this._config.ride===aa&&this.cycle()}static get Default(){return Od}static get DefaultType(){return Nd}static get NAME(){return id}next(){this._slide(vs)}nextWhenVisible(){!document.hidden&&P(this._element)&&this.next()}prev(){this._slide(In)}pause(){this._isSliding&&S(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval(()=>this.nextWhenVisible(),this._config.interval)}_maybeEnableCycle(){if(this._config.ride){if(this._isSliding){$.one(this._element,ui,()=>this.cycle());return}this.cycle()}}to(i){const u=this._getItems();if(i>u.length-1||i<0)return;if(this._isSliding){$.one(this._element,ui,()=>this.to(i));return}const v=this._getItemIndex(this._getActive());if(v===i)return;const I=i>v?vs:In;this._slide(I,u[i])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(i){return i.defaultInterval=i.interval,i}_addEventListeners(){this._config.keyboard&&$.on(this._element,ud,i=>this._keydown(i)),this._config.pause==="hover"&&($.on(this._element,fd,()=>this.pause()),$.on(this._element,dd,()=>this._maybeEnableCycle())),this._config.touch&&er.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const v of x.find(Ad,this._element))$.on(v,hd,I=>I.preventDefault());const u={leftCallback:()=>this._slide(this._directionToOrder(Pn)),rightCallback:()=>this._slide(this._directionToOrder(tr)),endCallback:()=>{this._config.pause==="hover"&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout(()=>this._maybeEnableCycle(),ld+this._config.interval))}};this._swipeHelper=new er(this._element,u)}_keydown(i){if(/input|textarea/i.test(i.target.tagName))return;const u=Sd[i.key];u&&(i.preventDefault(),this._slide(this._directionToOrder(u)))}_getItemIndex(i){return this._getItems().indexOf(i)}_setActiveIndicatorElement(i){if(!this._indicatorsElement)return;const u=x.findOne(la,this._indicatorsElement);u.classList.remove(nr),u.removeAttribute("aria-current");const v=x.findOne(`[data-bs-slide-to="${i}"]`,this._indicatorsElement);v&&(v.classList.add(nr),v.setAttribute("aria-current","true"))}_updateInterval(){const i=this._activeElement||this._getActive();if(!i)return;const u=Number.parseInt(i.getAttribute("data-bs-interval"),10);this._config.interval=u||this._config.defaultInterval}_slide(i,u=null){if(this._isSliding)return;const v=this._getActive(),I=i===vs,Y=u||Q(this._getItems(),v,I,this._config.wrap);if(Y===v)return;const U=this._getItemIndex(Y),le=fr=>$.trigger(this._element,fr,{relatedTarget:Y,direction:this._orderToDirection(i),from:this._getItemIndex(v),to:U});if(le(cd).defaultPrevented||!v||!Y)return;const ot=!!this._interval;this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(U),this._activeElement=Y;const $e=I?vd:_d,At=I?Ed:yd;Y.classList.add(At),b(Y),v.classList.add($e),Y.classList.add($e);const gt=()=>{Y.classList.remove($e,At),Y.classList.add(nr),v.classList.remove(nr,At,$e),this._isSliding=!1,le(ui)};this._queueCallback(gt,v,this._isAnimated()),ot&&this.cycle()}_isAnimated(){return this._element.classList.contains(md)}_getActive(){return x.findOne(bd,this._element)}_getItems(){return x.find(ca,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(i){return A()?i===Pn?In:vs:i===Pn?vs:In}_orderToDirection(i){return A()?i===In?Pn:tr:i===In?tr:Pn}static jQueryInterface(i){return this.each(function(){const u=Mn.getOrCreateInstance(this,i);if(typeof i=="number"){u.to(i);return}if(typeof i=="string"){if(u[i]===void 0||i.startsWith("_")||i==="constructor")throw new TypeError(`No method named "${i}"`);u[i]()}})}}$.on(document,gd,wd,function(m){const i=x.getElementFromSelector(this);if(!i||!i.classList.contains(aa))return;m.preventDefault();const u=Mn.getOrCreateInstance(i),v=this.getAttribute("data-bs-slide-to");if(v){u.to(v),u._maybeEnableCycle();return}if(N.getDataAttribute(this,"slide")==="next"){u.next(),u._maybeEnableCycle();return}u.prev(),u._maybeEnableCycle()}),$.on(window,pd,()=>{const m=x.find(Cd);for(const i of m)Mn.getOrCreateInstance(i)}),w(Mn);const xd="collapse",Es=".bs.collapse",$d=".data-api",Dd=`show${Es}`,Ld=`shown${Es}`,Rd=`hide${Es}`,Id=`hidden${Es}`,Pd=`click${Es}${$d}`,fi="show",kn="collapse",sr="collapsing",Md="collapsed",kd=`:scope .${kn} .${kn}`,Vd="collapse-horizontal",Fd="width",Hd="height",Bd=".collapse.show, .collapse.collapsing",di='[data-bs-toggle="collapse"]',jd={parent:null,toggle:!0},Kd={parent:"(null|element)",toggle:"boolean"};class Vn extends F{constructor(i,u){super(i,u),this._isTransitioning=!1,this._triggerArray=[];const v=x.find(di);for(const I of v){const Y=x.getSelectorFromElement(I),U=x.find(Y).filter(le=>le===this._element);Y!==null&&U.length&&this._triggerArray.push(I)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return jd}static get DefaultType(){return Kd}static get NAME(){return xd}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let i=[];if(this._config.parent&&(i=this._getFirstLevelChildren(Bd).filter(le=>le!==this._element).map(le=>Vn.getOrCreateInstance(le,{toggle:!1}))),i.length&&i[0]._isTransitioning||$.trigger(this._element,Dd).defaultPrevented)return;for(const le of i)le.hide();const v=this._getDimension();this._element.classList.remove(kn),this._element.classList.add(sr),this._element.style[v]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const I=()=>{this._isTransitioning=!1,this._element.classList.remove(sr),this._element.classList.add(kn,fi),this._element.style[v]="",$.trigger(this._element,Ld)},U=`scroll${v[0].toUpperCase()+v.slice(1)}`;this._queueCallback(I,this._element,!0),this._element.style[v]=`${this._element[U]}px`}hide(){if(this._isTransitioning||!this._isShown()||$.trigger(this._element,Rd).defaultPrevented)return;const u=this._getDimension();this._element.style[u]=`${this._element.getBoundingClientRect()[u]}px`,b(this._element),this._element.classList.add(sr),this._element.classList.remove(kn,fi);for(const I of this._triggerArray){const Y=x.getElementFromSelector(I);Y&&!this._isShown(Y)&&this._addAriaAndCollapsedClass([I],!1)}this._isTransitioning=!0;const v=()=>{this._isTransitioning=!1,this._element.classList.remove(sr),this._element.classList.add(kn),$.trigger(this._element,Id)};this._element.style[u]="",this._queueCallback(v,this._element,!0)}_isShown(i=this._element){return i.classList.contains(fi)}_configAfterMerge(i){return i.toggle=!!i.toggle,i.parent=D(i.parent),i}_getDimension(){return this._element.classList.contains(Vd)?Fd:Hd}_initializeChildren(){if(!this._config.parent)return;const i=this._getFirstLevelChildren(di);for(const u of i){const v=x.getElementFromSelector(u);v&&this._addAriaAndCollapsedClass([u],this._isShown(v))}}_getFirstLevelChildren(i){const u=x.find(kd,this._config.parent);return x.find(i,this._config.parent).filter(v=>!u.includes(v))}_addAriaAndCollapsedClass(i,u){if(i.length)for(const v of i)v.classList.toggle(Md,!u),v.setAttribute("aria-expanded",u)}static jQueryInterface(i){const u={};return typeof i=="string"&&/show|hide/.test(i)&&(u.toggle=!1),this.each(function(){const v=Vn.getOrCreateInstance(this,u);if(typeof i=="string"){if(typeof v[i]>"u")throw new TypeError(`No method named "${i}"`);v[i]()}})}}$.on(document,Pd,di,function(m){(m.target.tagName==="A"||m.delegateTarget&&m.delegateTarget.tagName==="A")&&m.preventDefault();for(const i of x.getMultipleElementsFromSelector(this))Vn.getOrCreateInstance(i,{toggle:!1}).toggle()}),w(Vn);const ua="dropdown",dn=".bs.dropdown",hi=".data-api",Wd="Escape",fa="Tab",Ud="ArrowUp",da="ArrowDown",Yd=2,Gd=`hide${dn}`,qd=`hidden${dn}`,zd=`show${dn}`,Xd=`shown${dn}`,ha=`click${dn}${hi}`,pa=`keydown${dn}${hi}`,Qd=`keyup${dn}${hi}`,Fn="show",Jd="dropup",Zd="dropend",eh="dropstart",th="dropup-center",nh="dropdown-center",hn='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',sh=`${hn}.${Fn}`,rr=".dropdown-menu",rh=".navbar",ih=".navbar-nav",oh=".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",ah=A()?"top-end":"top-start",lh=A()?"top-start":"top-end",ch=A()?"bottom-end":"bottom-start",uh=A()?"bottom-start":"bottom-end",fh=A()?"left-start":"right-start",dh=A()?"right-start":"left-start",hh="top",ph="bottom",gh={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},mh={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class pt extends F{constructor(i,u){super(i,u),this._popper=null,this._parent=this._element.parentNode,this._menu=x.next(this._element,rr)[0]||x.prev(this._element,rr)[0]||x.findOne(rr,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return gh}static get DefaultType(){return mh}static get NAME(){return ua}toggle(){return this._isShown()?this.hide():this.show()}show(){if(V(this._element)||this._isShown())return;const i={relatedTarget:this._element};if(!$.trigger(this._element,zd,i).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(ih))for(const v of[].concat(...document.body.children))$.on(v,"mouseover",M);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Fn),this._element.classList.add(Fn),$.trigger(this._element,Xd,i)}}hide(){if(V(this._element)||!this._isShown())return;const i={relatedTarget:this._element};this._completeHide(i)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(i){if(!$.trigger(this._element,Gd,i).defaultPrevented){if("ontouchstart"in document.documentElement)for(const v of[].concat(...document.body.children))$.off(v,"mouseover",M);this._popper&&this._popper.destroy(),this._menu.classList.remove(Fn),this._element.classList.remove(Fn),this._element.setAttribute("aria-expanded","false"),N.removeDataAttribute(this._menu,"popper"),$.trigger(this._element,qd,i)}}_getConfig(i){if(i=super._getConfig(i),typeof i.reference=="object"&&!C(i.reference)&&typeof i.reference.getBoundingClientRect!="function")throw new TypeError(`${ua.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return i}_createPopper(){if(typeof r>"u")throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let i=this._element;this._config.reference==="parent"?i=this._parent:C(this._config.reference)?i=D(this._config.reference):typeof this._config.reference=="object"&&(i=this._config.reference);const u=this._getPopperConfig();this._popper=r.createPopper(i,this._menu,u)}_isShown(){return this._menu.classList.contains(Fn)}_getPlacement(){const i=this._parent;if(i.classList.contains(Zd))return fh;if(i.classList.contains(eh))return dh;if(i.classList.contains(th))return hh;if(i.classList.contains(nh))return ph;const u=getComputedStyle(this._menu).getPropertyValue("--bs-position").trim()==="end";return i.classList.contains(Jd)?u?lh:ah:u?uh:ch}_detectNavbar(){return this._element.closest(rh)!==null}_getOffset(){const{offset:i}=this._config;return typeof i=="string"?i.split(",").map(u=>Number.parseInt(u,10)):typeof i=="function"?u=>i(u,this._element):i}_getPopperConfig(){const i={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||this._config.display==="static")&&(N.setDataAttribute(this._menu,"popper","static"),i.modifiers=[{name:"applyStyles",enabled:!1}]),{...i,...j(this._config.popperConfig,[i])}}_selectMenuItem({key:i,target:u}){const v=x.find(oh,this._menu).filter(I=>P(I));v.length&&Q(v,u,i===da,!v.includes(u)).focus()}static jQueryInterface(i){return this.each(function(){const u=pt.getOrCreateInstance(this,i);if(typeof i=="string"){if(typeof u[i]>"u")throw new TypeError(`No method named "${i}"`);u[i]()}})}static clearMenus(i){if(i.button===Yd||i.type==="keyup"&&i.key!==fa)return;const u=x.find(sh);for(const v of u){const I=pt.getInstance(v);if(!I||I._config.autoClose===!1)continue;const Y=i.composedPath(),U=Y.includes(I._menu);if(Y.includes(I._element)||I._config.autoClose==="inside"&&!U||I._config.autoClose==="outside"&&U||I._menu.contains(i.target)&&(i.type==="keyup"&&i.key===fa||/input|select|option|textarea|form/i.test(i.target.tagName)))continue;const le={relatedTarget:I._element};i.type==="click"&&(le.clickEvent=i),I._completeHide(le)}}static dataApiKeydownHandler(i){const u=/input|textarea/i.test(i.target.tagName),v=i.key===Wd,I=[Ud,da].includes(i.key);if(!I&&!v||u&&!v)return;i.preventDefault();const Y=this.matches(hn)?this:x.prev(this,hn)[0]||x.next(this,hn)[0]||x.findOne(hn,i.delegateTarget.parentNode),U=pt.getOrCreateInstance(Y);if(I){i.stopPropagation(),U.show(),U._selectMenuItem(i);return}U._isShown()&&(i.stopPropagation(),U.hide(),Y.focus())}}$.on(document,pa,hn,pt.dataApiKeydownHandler),$.on(document,pa,rr,pt.dataApiKeydownHandler),$.on(document,ha,pt.clearMenus),$.on(document,Qd,pt.clearMenus),$.on(document,ha,hn,function(m){m.preventDefault(),pt.getOrCreateInstance(this).toggle()}),w(pt);const ga="backdrop",_h="fade",ma="show",_a=`mousedown.bs.${ga}`,vh={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Eh={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class va extends H{constructor(i){super(),this._config=this._getConfig(i),this._isAppended=!1,this._element=null}static get Default(){return vh}static get DefaultType(){return Eh}static get NAME(){return ga}show(i){if(!this._config.isVisible){j(i);return}this._append();const u=this._getElement();this._config.isAnimated&&b(u),u.classList.add(ma),this._emulateAnimation(()=>{j(i)})}hide(i){if(!this._config.isVisible){j(i);return}this._getElement().classList.remove(ma),this._emulateAnimation(()=>{this.dispose(),j(i)})}dispose(){this._isAppended&&($.off(this._element,_a),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const i=document.createElement("div");i.className=this._config.className,this._config.isAnimated&&i.classList.add(_h),this._element=i}return this._element}_configAfterMerge(i){return i.rootElement=D(i.rootElement),i}_append(){if(this._isAppended)return;const i=this._getElement();this._config.rootElement.append(i),$.on(i,_a,()=>{j(this._config.clickCallback)}),this._isAppended=!0}_emulateAnimation(i){B(i,this._getElement(),this._config.isAnimated)}}const yh="focustrap",ir=".bs.focustrap",bh=`focusin${ir}`,Ah=`keydown.tab${ir}`,Th="Tab",wh="forward",Ea="backward",Ch={autofocus:!0,trapElement:null},Sh={autofocus:"boolean",trapElement:"element"};class ya extends H{constructor(i){super(),this._config=this._getConfig(i),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Ch}static get DefaultType(){return Sh}static get NAME(){return yh}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),$.off(document,ir),$.on(document,bh,i=>this._handleFocusin(i)),$.on(document,Ah,i=>this._handleKeydown(i)),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,$.off(document,ir))}_handleFocusin(i){const{trapElement:u}=this._config;if(i.target===document||i.target===u||u.contains(i.target))return;const v=x.focusableChildren(u);v.length===0?u.focus():this._lastTabNavDirection===Ea?v[v.length-1].focus():v[0].focus()}_handleKeydown(i){i.key===Th&&(this._lastTabNavDirection=i.shiftKey?Ea:wh)}}const ba=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Aa=".sticky-top",or="padding-right",Ta="margin-right";class pi{constructor(){this._element=document.body}getWidth(){const i=document.documentElement.clientWidth;return Math.abs(window.innerWidth-i)}hide(){const i=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,or,u=>u+i),this._setElementAttributes(ba,or,u=>u+i),this._setElementAttributes(Aa,Ta,u=>u-i)}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,or),this._resetElementAttributes(ba,or),this._resetElementAttributes(Aa,Ta)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(i,u,v){const I=this.getWidth(),Y=U=>{if(U!==this._element&&window.innerWidth>U.clientWidth+I)return;this._saveInitialAttribute(U,u);const le=window.getComputedStyle(U).getPropertyValue(u);U.style.setProperty(u,`${v(Number.parseFloat(le))}px`)};this._applyManipulationCallback(i,Y)}_saveInitialAttribute(i,u){const v=i.style.getPropertyValue(u);v&&N.setDataAttribute(i,u,v)}_resetElementAttributes(i,u){const v=I=>{const Y=N.getDataAttribute(I,u);if(Y===null){I.style.removeProperty(u);return}N.removeDataAttribute(I,u),I.style.setProperty(u,Y)};this._applyManipulationCallback(i,v)}_applyManipulationCallback(i,u){if(C(i)){u(i);return}for(const v of x.find(i,this._element))u(v)}}const Oh="modal",it=".bs.modal",Nh=".data-api",xh="Escape",$h=`hide${it}`,Dh=`hidePrevented${it}`,wa=`hidden${it}`,Ca=`show${it}`,Lh=`shown${it}`,Rh=`resize${it}`,Ih=`click.dismiss${it}`,Ph=`mousedown.dismiss${it}`,Mh=`keydown.dismiss${it}`,kh=`click${it}${Nh}`,Sa="modal-open",Vh="fade",Oa="show",gi="modal-static",Fh=".modal.show",Hh=".modal-dialog",Bh=".modal-body",jh='[data-bs-toggle="modal"]',Kh={backdrop:!0,focus:!0,keyboard:!0},Wh={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class pn extends F{constructor(i,u){super(i,u),this._dialog=x.findOne(Hh,this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new pi,this._addEventListeners()}static get Default(){return Kh}static get DefaultType(){return Wh}static get NAME(){return Oh}toggle(i){return this._isShown?this.hide():this.show(i)}show(i){this._isShown||this._isTransitioning||$.trigger(this._element,Ca,{relatedTarget:i}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Sa),this._adjustDialog(),this._backdrop.show(()=>this._showElement(i)))}hide(){!this._isShown||this._isTransitioning||$.trigger(this._element,$h).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Oa),this._queueCallback(()=>this._hideModal(),this._element,this._isAnimated()))}dispose(){$.off(window,it),$.off(this._dialog,it),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new va({isVisible:!!this._config.backdrop,isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new ya({trapElement:this._element})}_showElement(i){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const u=x.findOne(Bh,this._dialog);u&&(u.scrollTop=0),b(this._element),this._element.classList.add(Oa);const v=()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,$.trigger(this._element,Lh,{relatedTarget:i})};this._queueCallback(v,this._dialog,this._isAnimated())}_addEventListeners(){$.on(this._element,Mh,i=>{if(i.key===xh){if(this._config.keyboard){this.hide();return}this._triggerBackdropTransition()}}),$.on(window,Rh,()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()}),$.on(this._element,Ph,i=>{$.one(this._element,Ih,u=>{if(!(this._element!==i.target||this._element!==u.target)){if(this._config.backdrop==="static"){this._triggerBackdropTransition();return}this._config.backdrop&&this.hide()}})})}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide(()=>{document.body.classList.remove(Sa),this._resetAdjustments(),this._scrollBar.reset(),$.trigger(this._element,wa)})}_isAnimated(){return this._element.classList.contains(Vh)}_triggerBackdropTransition(){if($.trigger(this._element,Dh).defaultPrevented)return;const u=this._element.scrollHeight>document.documentElement.clientHeight,v=this._element.style.overflowY;v==="hidden"||this._element.classList.contains(gi)||(u||(this._element.style.overflowY="hidden"),this._element.classList.add(gi),this._queueCallback(()=>{this._element.classList.remove(gi),this._queueCallback(()=>{this._element.style.overflowY=v},this._dialog)},this._dialog),this._element.focus())}_adjustDialog(){const i=this._element.scrollHeight>document.documentElement.clientHeight,u=this._scrollBar.getWidth(),v=u>0;if(v&&!i){const I=A()?"paddingLeft":"paddingRight";this._element.style[I]=`${u}px`}if(!v&&i){const I=A()?"paddingRight":"paddingLeft";this._element.style[I]=`${u}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(i,u){return this.each(function(){const v=pn.getOrCreateInstance(this,i);if(typeof i=="string"){if(typeof v[i]>"u")throw new TypeError(`No method named "${i}"`);v[i](u)}})}}$.on(document,kh,jh,function(m){const i=x.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&m.preventDefault(),$.one(i,Ca,I=>{I.defaultPrevented||$.one(i,wa,()=>{P(this)&&this.focus()})});const u=x.findOne(Fh);u&&pn.getInstance(u).hide(),pn.getOrCreateInstance(i).toggle(this)}),W(pn),w(pn);const Uh="offcanvas",Dt=".bs.offcanvas",Na=".data-api",Yh=`load${Dt}${Na}`,Gh="Escape",xa="show",$a="showing",Da="hiding",qh="offcanvas-backdrop",La=".offcanvas.show",zh=`show${Dt}`,Xh=`shown${Dt}`,Qh=`hide${Dt}`,Ra=`hidePrevented${Dt}`,Ia=`hidden${Dt}`,Jh=`resize${Dt}`,Zh=`click${Dt}${Na}`,ep=`keydown.dismiss${Dt}`,tp='[data-bs-toggle="offcanvas"]',np={backdrop:!0,keyboard:!0,scroll:!1},sp={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Lt extends F{constructor(i,u){super(i,u),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return np}static get DefaultType(){return sp}static get NAME(){return Uh}toggle(i){return this._isShown?this.hide():this.show(i)}show(i){if(this._isShown||$.trigger(this._element,zh,{relatedTarget:i}).defaultPrevented)return;this._isShown=!0,this._backdrop.show(),this._config.scroll||new pi().hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add($a);const v=()=>{(!this._config.scroll||this._config.backdrop)&&this._focustrap.activate(),this._element.classList.add(xa),this._element.classList.remove($a),$.trigger(this._element,Xh,{relatedTarget:i})};this._queueCallback(v,this._element,!0)}hide(){if(!this._isShown||$.trigger(this._element,Qh).defaultPrevented)return;this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Da),this._backdrop.hide();const u=()=>{this._element.classList.remove(xa,Da),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||new pi().reset(),$.trigger(this._element,Ia)};this._queueCallback(u,this._element,!0)}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const i=()=>{if(this._config.backdrop==="static"){$.trigger(this._element,Ra);return}this.hide()},u=!!this._config.backdrop;return new va({className:qh,isVisible:u,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:u?i:null})}_initializeFocusTrap(){return new ya({trapElement:this._element})}_addEventListeners(){$.on(this._element,ep,i=>{if(i.key===Gh){if(this._config.keyboard){this.hide();return}$.trigger(this._element,Ra)}})}static jQueryInterface(i){return this.each(function(){const u=Lt.getOrCreateInstance(this,i);if(typeof i=="string"){if(u[i]===void 0||i.startsWith("_")||i==="constructor")throw new TypeError(`No method named "${i}"`);u[i](this)}})}}$.on(document,Zh,tp,function(m){const i=x.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&m.preventDefault(),V(this))return;$.one(i,Ia,()=>{P(this)&&this.focus()});const u=x.findOne(La);u&&u!==i&&Lt.getInstance(u).hide(),Lt.getOrCreateInstance(i).toggle(this)}),$.on(window,Yh,()=>{for(const m of x.find(La))Lt.getOrCreateInstance(m).show()}),$.on(window,Jh,()=>{for(const m of x.find("[aria-modal][class*=show][class*=offcanvas-]"))getComputedStyle(m).position!=="fixed"&&Lt.getOrCreateInstance(m).hide()}),W(Lt),w(Lt);const Pa={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},rp=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),ip=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,op=(m,i)=>{const u=m.nodeName.toLowerCase();return i.includes(u)?rp.has(u)?!!ip.test(m.nodeValue):!0:i.filter(v=>v instanceof RegExp).some(v=>v.test(u))};function ap(m,i,u){if(!m.length)return m;if(u&&typeof u=="function")return u(m);const I=new window.DOMParser().parseFromString(m,"text/html"),Y=[].concat(...I.body.querySelectorAll("*"));for(const U of Y){const le=U.nodeName.toLowerCase();if(!Object.keys(i).includes(le)){U.remove();continue}const Xe=[].concat(...U.attributes),ot=[].concat(i["*"]||[],i[le]||[]);for(const $e of Xe)op($e,ot)||U.removeAttribute($e.nodeName)}return I.body.innerHTML}const lp="TemplateFactory",cp={allowList:Pa,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:""},up={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},fp={entry:"(string|element|function|null)",selector:"(string|element)"};class dp extends H{constructor(i){super(),this._config=this._getConfig(i)}static get Default(){return cp}static get DefaultType(){return up}static get NAME(){return lp}getContent(){return Object.values(this._config.content).map(i=>this._resolvePossibleFunction(i)).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(i){return this._checkContent(i),this._config.content={...this._config.content,...i},this}toHtml(){const i=document.createElement("div");i.innerHTML=this._maybeSanitize(this._config.template);for(const[I,Y]of Object.entries(this._config.content))this._setContent(i,Y,I);const u=i.children[0],v=this._resolvePossibleFunction(this._config.extraClass);return v&&u.classList.add(...v.split(" ")),u}_typeCheckConfig(i){super._typeCheckConfig(i),this._checkContent(i.content)}_checkContent(i){for(const[u,v]of Object.entries(i))super._typeCheckConfig({selector:u,entry:v},fp)}_setContent(i,u,v){const I=x.findOne(v,i);if(I){if(u=this._resolvePossibleFunction(u),!u){I.remove();return}if(C(u)){this._putElementInTemplate(D(u),I);return}if(this._config.html){I.innerHTML=this._maybeSanitize(u);return}I.textContent=u}}_maybeSanitize(i){return this._config.sanitize?ap(i,this._config.allowList,this._config.sanitizeFn):i}_resolvePossibleFunction(i){return j(i,[this])}_putElementInTemplate(i,u){if(this._config.html){u.innerHTML="",u.append(i);return}u.textContent=i.textContent}}const hp="tooltip",pp=new Set(["sanitize","allowList","sanitizeFn"]),mi="fade",gp="modal",ar="show",mp=".tooltip-inner",Ma=`.${gp}`,ka="hide.bs.modal",ys="hover",_i="focus",_p="click",vp="manual",Ep="hide",yp="hidden",bp="show",Ap="shown",Tp="inserted",wp="click",Cp="focusin",Sp="focusout",Op="mouseenter",Np="mouseleave",xp={AUTO:"auto",TOP:"top",RIGHT:A()?"left":"right",BOTTOM:"bottom",LEFT:A()?"right":"left"},$p={allowList:Pa,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'
',title:"",trigger:"hover focus"},Dp={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class gn extends F{constructor(i,u){if(typeof r>"u")throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(i,u),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return $p}static get DefaultType(){return Dp}static get NAME(){return hp}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){if(this._isEnabled){if(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()){this._leave();return}this._enter()}}dispose(){clearTimeout(this._timeout),$.off(this._element.closest(Ma),ka,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if(this._element.style.display==="none")throw new Error("Please use show on visible elements");if(!(this._isWithContent()&&this._isEnabled))return;const i=$.trigger(this._element,this.constructor.eventName(bp)),v=(k(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(i.defaultPrevented||!v)return;this._disposePopper();const I=this._getTipElement();this._element.setAttribute("aria-describedby",I.getAttribute("id"));const{container:Y}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(Y.append(I),$.trigger(this._element,this.constructor.eventName(Tp))),this._popper=this._createPopper(I),I.classList.add(ar),"ontouchstart"in document.documentElement)for(const le of[].concat(...document.body.children))$.on(le,"mouseover",M);const U=()=>{$.trigger(this._element,this.constructor.eventName(Ap)),this._isHovered===!1&&this._leave(),this._isHovered=!1};this._queueCallback(U,this.tip,this._isAnimated())}hide(){if(!this._isShown()||$.trigger(this._element,this.constructor.eventName(Ep)).defaultPrevented)return;if(this._getTipElement().classList.remove(ar),"ontouchstart"in document.documentElement)for(const I of[].concat(...document.body.children))$.off(I,"mouseover",M);this._activeTrigger[_p]=!1,this._activeTrigger[_i]=!1,this._activeTrigger[ys]=!1,this._isHovered=null;const v=()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),$.trigger(this._element,this.constructor.eventName(yp)))};this._queueCallback(v,this.tip,this._isAnimated())}update(){this._popper&&this._popper.update()}_isWithContent(){return!!this._getTitle()}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(i){const u=this._getTemplateFactory(i).toHtml();if(!u)return null;u.classList.remove(mi,ar),u.classList.add(`bs-${this.constructor.NAME}-auto`);const v=p(this.constructor.NAME).toString();return u.setAttribute("id",v),this._isAnimated()&&u.classList.add(mi),u}setContent(i){this._newContent=i,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(i){return this._templateFactory?this._templateFactory.changeContent(i):this._templateFactory=new dp({...this._config,content:i,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[mp]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(i){return this.constructor.getOrCreateInstance(i.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(mi)}_isShown(){return this.tip&&this.tip.classList.contains(ar)}_createPopper(i){const u=j(this._config.placement,[this,i,this._element]),v=xp[u.toUpperCase()];return r.createPopper(this._element,i,this._getPopperConfig(v))}_getOffset(){const{offset:i}=this._config;return typeof i=="string"?i.split(",").map(u=>Number.parseInt(u,10)):typeof i=="function"?u=>i(u,this._element):i}_resolvePossibleFunction(i){return j(i,[this._element])}_getPopperConfig(i){const u={placement:i,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:v=>{this._getTipElement().setAttribute("data-popper-placement",v.state.placement)}}]};return{...u,...j(this._config.popperConfig,[u])}}_setListeners(){const i=this._config.trigger.split(" ");for(const u of i)if(u==="click")$.on(this._element,this.constructor.eventName(wp),this._config.selector,v=>{this._initializeOnDelegatedTarget(v).toggle()});else if(u!==vp){const v=u===ys?this.constructor.eventName(Op):this.constructor.eventName(Cp),I=u===ys?this.constructor.eventName(Np):this.constructor.eventName(Sp);$.on(this._element,v,this._config.selector,Y=>{const U=this._initializeOnDelegatedTarget(Y);U._activeTrigger[Y.type==="focusin"?_i:ys]=!0,U._enter()}),$.on(this._element,I,this._config.selector,Y=>{const U=this._initializeOnDelegatedTarget(Y);U._activeTrigger[Y.type==="focusout"?_i:ys]=U._element.contains(Y.relatedTarget),U._leave()})}this._hideModalHandler=()=>{this._element&&this.hide()},$.on(this._element.closest(Ma),ka,this._hideModalHandler)}_fixTitle(){const i=this._element.getAttribute("title");i&&(!this._element.getAttribute("aria-label")&&!this._element.textContent.trim()&&this._element.setAttribute("aria-label",i),this._element.setAttribute("data-bs-original-title",i),this._element.removeAttribute("title"))}_enter(){if(this._isShown()||this._isHovered){this._isHovered=!0;return}this._isHovered=!0,this._setTimeout(()=>{this._isHovered&&this.show()},this._config.delay.show)}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout(()=>{this._isHovered||this.hide()},this._config.delay.hide))}_setTimeout(i,u){clearTimeout(this._timeout),this._timeout=setTimeout(i,u)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(i){const u=N.getDataAttributes(this._element);for(const v of Object.keys(u))pp.has(v)&&delete u[v];return i={...u,...typeof i=="object"&&i?i:{}},i=this._mergeConfigObj(i),i=this._configAfterMerge(i),this._typeCheckConfig(i),i}_configAfterMerge(i){return i.container=i.container===!1?document.body:D(i.container),typeof i.delay=="number"&&(i.delay={show:i.delay,hide:i.delay}),typeof i.title=="number"&&(i.title=i.title.toString()),typeof i.content=="number"&&(i.content=i.content.toString()),i}_getDelegateConfig(){const i={};for(const[u,v]of Object.entries(this._config))this.constructor.Default[u]!==v&&(i[u]=v);return i.selector=!1,i.trigger="manual",i}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(i){return this.each(function(){const u=gn.getOrCreateInstance(this,i);if(typeof i=="string"){if(typeof u[i]>"u")throw new TypeError(`No method named "${i}"`);u[i]()}})}}w(gn);const Lp="popover",Rp=".popover-header",Ip=".popover-body",Pp={...gn.Default,content:"",offset:[0,8],placement:"right",template:'