mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-04 00:06:18 +00:00
Compare commits
2 Commits
v4.3.1-dev
...
cleanup-da
Author | SHA1 | Date | |
---|---|---|---|
|
ed896b6545 | ||
|
6bebe89202 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,7 +6,6 @@ __pycache__
|
|||||||
src/test.py
|
src/test.py
|
||||||
*.db
|
*.db
|
||||||
src/wg-dashboard.ini
|
src/wg-dashboard.ini
|
||||||
src/ssl-tls.ini
|
|
||||||
src/static/pic.xd
|
src/static/pic.xd
|
||||||
*.conf
|
*.conf
|
||||||
private_key.txt
|
private_key.txt
|
||||||
@@ -20,6 +19,7 @@ node_modules/**
|
|||||||
*/proxy.js
|
*/proxy.js
|
||||||
src/static/app/proxy.js
|
src/static/app/proxy.js
|
||||||
.secrets
|
.secrets
|
||||||
|
*.pid
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
@@ -104,7 +104,6 @@ ARG wg_port="51820"
|
|||||||
ENV TZ="Europe/Amsterdam" \
|
ENV TZ="Europe/Amsterdam" \
|
||||||
global_dns="9.9.9.9" \
|
global_dns="9.9.9.9" \
|
||||||
wgd_port="10086" \
|
wgd_port="10086" \
|
||||||
log_level="INFO" \
|
|
||||||
public_ip="" \
|
public_ip="" \
|
||||||
WGDASH=/opt/wgdashboard
|
WGDASH=/opt/wgdashboard
|
||||||
|
|
||||||
@@ -130,15 +129,6 @@ SaveConfig = true\n\
|
|||||||
DNS = ${global_dns}" > /configs/wg0.conf.template \
|
DNS = ${global_dns}" > /configs/wg0.conf.template \
|
||||||
&& chmod 600 /configs/wg0.conf.template
|
&& chmod 600 /configs/wg0.conf.template
|
||||||
|
|
||||||
# Replacement of paths with relative ones for namespace handling when using a reverse proxy
|
|
||||||
RUN set -ex && \
|
|
||||||
find ${WGDASH}/src/static/dist -type f \( -name "*.html" -o -name "*.js" \) \
|
|
||||||
-exec sed -i 's|/static/dist|./static/dist|g' {} + && \
|
|
||||||
find ${WGDASH}/src/static/dist -type f -name "*.css" \
|
|
||||||
-exec sed -i 's|/static/dist/WGDashboardClient/assets/|./|g' {} + && \
|
|
||||||
find ${WGDASH}/src/static/dist -type f -name "*.css" \
|
|
||||||
-exec sed -i 's|/static/dist/WGDashboardAdmin/assets/|./|g' {} +
|
|
||||||
|
|
||||||
# Set a healthcheck to determine the container its health
|
# Set a healthcheck to determine the container its health
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
|
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
|
||||||
|
@@ -102,7 +102,6 @@ Updating the WGDashboard container should be through 'The Docker Way' - by pulli
|
|||||||
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
|
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
|
||||||
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. |
|
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. |
|
||||||
| `wgd_port` | Any port that is allowed for the process | `10086` | `443` | This port is used to set the WGDashboard web port. |
|
| `wgd_port` | Any port that is allowed for the process | `10086` | `443` | This port is used to set the WGDashboard web port. |
|
||||||
| `log_level` | `DEBUG`, `INFO`, `WARNING`, `ERROR` and `CRITICAL` | `INFO` | `WARNING` | Sets the severity of the logs being displayed. |
|
|
||||||
| `username` | Any non‐empty string | `-` | `admin` | Username for the WGDashboard web interface account. |
|
| `username` | Any non‐empty string | `-` | `admin` | Username for the WGDashboard web interface account. |
|
||||||
| `password` | Any non‐empty string | `-` | `s3cr3tP@ss` | Password for the WGDashboard web interface account (stored hashed). |
|
| `password` | Any non‐empty string | `-` | `s3cr3tP@ss` | Password for the WGDashboard web interface account (stored hashed). |
|
||||||
| `enable_totp` | `true`, `false` | `true` | `false` | Enable TOTP‐based two‐factor authentication for the account. |
|
| `enable_totp` | `true`, `false` | `true` | `false` | Enable TOTP‐based two‐factor authentication for the account. |
|
||||||
|
@@ -12,8 +12,7 @@ services:
|
|||||||
# Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard.
|
# Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard.
|
||||||
# By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character)
|
# By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character)
|
||||||
# Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means.
|
# Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means.
|
||||||
environment:
|
#environment:
|
||||||
- log_level=WARNING
|
|
||||||
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
|
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
|
||||||
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
|
#- 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.
|
#- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
|
||||||
|
@@ -13,15 +13,15 @@ hash_password() {
|
|||||||
set_ini() {
|
set_ini() {
|
||||||
local section="$1" key="$2" value="$3"
|
local section="$1" key="$2" value="$3"
|
||||||
local current_value
|
local current_value
|
||||||
|
|
||||||
# Add section if it doesn't exist
|
# Add section if it doesn't exist
|
||||||
grep -q "^\[${section}\]" "$config_file" \
|
grep -q "^\[${section}\]" "$config_file" \
|
||||||
|| printf "\n[%s]\n" "${section}" >> "$config_file"
|
|| printf "\n[%s]\n" "${section}" >> "$config_file"
|
||||||
|
|
||||||
# Check current value if key exists
|
# Check current value if key exists
|
||||||
if grep -q "^[[:space:]]*${key}[[:space:]]*=" "$config_file"; then
|
if grep -q "^[[:space:]]*${key}[[:space:]]*=" "$config_file"; then
|
||||||
current_value=$(grep "^[[:space:]]*${key}[[:space:]]*=" "$config_file" | cut -d= -f2- | xargs)
|
current_value=$(grep "^[[:space:]]*${key}[[:space:]]*=" "$config_file" | cut -d= -f2- | xargs)
|
||||||
|
|
||||||
# Don't display actual value if it's a password field
|
# Don't display actual value if it's a password field
|
||||||
if [[ "$key" == *"password"* ]]; then
|
if [[ "$key" == *"password"* ]]; then
|
||||||
if [ "$current_value" = "$value" ]; then
|
if [ "$current_value" = "$value" ]; then
|
||||||
@@ -40,7 +40,7 @@ set_ini() {
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
sed -i "/^\[${section}\]/a ${key} = ${value}" "$config_file"
|
sed -i "/^\[${section}\]/a ${key} = ${value}" "$config_file"
|
||||||
|
|
||||||
# Don't display actual value if it's a password field
|
# Don't display actual value if it's a password field
|
||||||
if [[ "$key" == *"password"* ]]; then
|
if [[ "$key" == *"password"* ]]; then
|
||||||
echo "- Added new setting $key (value hidden)"
|
echo "- Added new setting $key (value hidden)"
|
||||||
@@ -61,112 +61,111 @@ echo "Starting the WGDashboard Docker container."
|
|||||||
|
|
||||||
ensure_installation() {
|
ensure_installation() {
|
||||||
echo "Quick-installing..."
|
echo "Quick-installing..."
|
||||||
|
|
||||||
# Make the wgd.sh script executable.
|
# Make the wgd.sh script executable.
|
||||||
chmod +x "${WGDASH}"/src/wgd.sh
|
chmod +x "${WGDASH}"/src/wgd.sh
|
||||||
cd "${WGDASH}"/src || exit
|
cd "${WGDASH}"/src || exit
|
||||||
|
|
||||||
# Github issue: https://github.com/donaldzou/WGDashboard/issues/723
|
# Github issue: https://github.com/donaldzou/WGDashboard/issues/723
|
||||||
echo "Checking for stale pids..."
|
echo "Checking for stale pids..."
|
||||||
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
|
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
|
||||||
echo "Found stale pid, removing..."
|
echo "Found stale pid, removing..."
|
||||||
rm ${WGDASH}/src/gunicorn.pid
|
rm ${WGDASH}/src/gunicorn.pid
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Removing clear shell command from the wgd.sh script to enhance docker logging.
|
# Removing clear shell command from the wgd.sh script to enhance docker logging.
|
||||||
echo "Removing clear command from wgd.sh for better Docker logging."
|
echo "Removing clear command from wgd.sh for better Docker logging."
|
||||||
sed -i '/clear/d' ./wgd.sh
|
sed -i '/clear/d' ./wgd.sh
|
||||||
|
|
||||||
# Create required directories and links
|
# Create required directories and links
|
||||||
if [ ! -d "/data/db" ]; then
|
if [ ! -d "/data/db" ]; then
|
||||||
echo "Creating database dir"
|
echo "Creating database dir"
|
||||||
mkdir -p /data/db
|
mkdir -p /data/db
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d "${WGDASH}/src/db" ]; then
|
if [ ! -d "${WGDASH}/src/db" ]; then
|
||||||
ln -s /data/db "${WGDASH}/src/db"
|
ln -s /data/db "${WGDASH}/src/db"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "${config_file}" ]; then
|
if [ ! -f "${config_file}" ]; then
|
||||||
echo "Creating wg-dashboard.ini file"
|
echo "Creating wg-dashboard.ini file"
|
||||||
touch "${config_file}"
|
touch "${config_file}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "${WGDASH}/src/wg-dashboard.ini" ]; then
|
if [ ! -f "${WGDASH}/src/wg-dashboard.ini" ]; then
|
||||||
ln -s "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
|
ln -s "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the Python virtual environment.
|
# Create the Python virtual environment.
|
||||||
. "${WGDASH}/src/venv/bin/activate"
|
. "${WGDASH}/src/venv/bin/activate"
|
||||||
|
|
||||||
# Use the bash interpreter to install WGDashboard according to the wgd.sh script.
|
# Use the bash interpreter to install WGDashboard according to the wgd.sh script.
|
||||||
/bin/bash ./wgd.sh install
|
/bin/bash ./wgd.sh install
|
||||||
|
|
||||||
echo "Looks like the installation succeeded. Moving on."
|
echo "Looks like the installation succeeded. Moving on."
|
||||||
|
|
||||||
# Setup WireGuard if needed
|
# Setup WireGuard if needed
|
||||||
if [ -z "$(ls -A /etc/wireguard)" ]; then
|
if [ ! -f "/etc/wireguard/wg0.conf" ]; then
|
||||||
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
||||||
|
|
||||||
echo "Setting a secure private key."
|
echo "Setting a secure private key."
|
||||||
local privateKey
|
local privateKey
|
||||||
privateKey=$(wg genkey)
|
privateKey=$(wg genkey)
|
||||||
sed -i "s|^PrivateKey *=.*$|PrivateKey = ${privateKey}|g" /etc/wireguard/wg0.conf
|
sed -i "s|^PrivateKey *=.*$|PrivateKey = ${privateKey}|g" /etc/wireguard/wg0.conf
|
||||||
|
|
||||||
echo "Done setting template."
|
echo "Done setting template."
|
||||||
else
|
else
|
||||||
echo "Existing Wireguard configuration file found in /etc/wireguard."
|
echo "Existing wg0 configuration file found, using that."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
set_envvars() {
|
set_envvars() {
|
||||||
printf "\n------------- SETTING ENVIRONMENT VARIABLES ----------------\n"
|
printf "\n------------- SETTING ENVIRONMENT VARIABLES ----------------\n"
|
||||||
|
|
||||||
# Check if config file is empty
|
# Check if config file is empty
|
||||||
if [ ! -s "${config_file}" ]; then
|
if [ ! -s "${config_file}" ]; then
|
||||||
echo "Config file is empty. Creating initial structure."
|
echo "Config file is empty. Creating initial structure."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Checking basic configuration:"
|
echo "Checking basic configuration:"
|
||||||
set_ini Peers peer_global_dns "${global_dns}"
|
set_ini Peers peer_global_dns "${global_dns}"
|
||||||
|
|
||||||
if [ -z "${public_ip}" ]; then
|
if [ -z "${public_ip}" ]; then
|
||||||
public_ip=$(curl -s ifconfig.me)
|
public_ip=$(curl -s ifconfig.me)
|
||||||
echo "Automatically detected public IP: ${public_ip}"
|
echo "Automatically detected public IP: ${public_ip}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set_ini Peers remote_endpoint "${public_ip}"
|
set_ini Peers remote_endpoint "${public_ip}"
|
||||||
set_ini Server app_port "${wgd_port}"
|
set_ini Server app_port "${wgd_port}"
|
||||||
set_ini Server log_level "${log_level}"
|
|
||||||
|
|
||||||
# Account settings - process all parameters
|
# Account settings - process all parameters
|
||||||
[[ -n "$username" ]] && echo "Configuring user account:"
|
[[ -n "$username" ]] && echo "Configuring user account:"
|
||||||
# Basic account variables
|
# Basic account variables
|
||||||
[[ -n "$username" ]] && set_ini Account username "${username}"
|
[[ -n "$username" ]] && set_ini Account username "${username}"
|
||||||
|
|
||||||
if [[ -n "$password" ]]; then
|
if [[ -n "$password" ]]; then
|
||||||
echo "- Setting password"
|
echo "- Setting password"
|
||||||
set_ini Account password "$(hash_password "${password}")"
|
set_ini Account password "$(hash_password "${password}")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Additional account variables
|
# Additional account variables
|
||||||
[[ -n "$enable_totp" ]] && set_ini Account enable_totp "${enable_totp}"
|
[[ -n "$enable_totp" ]] && set_ini Account enable_totp "${enable_totp}"
|
||||||
[[ -n "$totp_verified" ]] && set_ini Account totp_verified "${totp_verified}"
|
[[ -n "$totp_verified" ]] && set_ini Account totp_verified "${totp_verified}"
|
||||||
[[ -n "$totp_key" ]] && set_ini Account totp_key "${totp_key}"
|
[[ -n "$totp_key" ]] && set_ini Account totp_key "${totp_key}"
|
||||||
|
|
||||||
# Welcome session
|
# Welcome session
|
||||||
[[ -n "$welcome_session" ]] && set_ini Other welcome_session "${welcome_session}"
|
[[ -n "$welcome_session" ]] && set_ini Other welcome_session "${welcome_session}"
|
||||||
# If username and password are set but welcome_session isn't, disable it
|
# If username and password are set but welcome_session isn't, disable it
|
||||||
if [[ -n "$username" && -n "$password" && -z "$welcome_session" ]]; then
|
if [[ -n "$username" && -n "$password" && -z "$welcome_session" ]]; then
|
||||||
set_ini Other welcome_session "false"
|
set_ini Other welcome_session "false"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Autostart WireGuard
|
# Autostart WireGuard
|
||||||
if [[ -n "$wg_autostart" ]]; then
|
if [[ -n "$wg_autostart" ]]; then
|
||||||
echo "Configuring WireGuard autostart:"
|
echo "Configuring WireGuard autostart:"
|
||||||
set_ini WireGuardConfiguration autostart "${wg_autostart}"
|
set_ini WireGuardConfiguration autostart "${wg_autostart}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Email (check if any settings need to be configured)
|
# Email (check if any settings need to be configured)
|
||||||
email_vars=("email_server" "email_port" "email_encryption" "email_username" "email_password" "email_from" "email_template")
|
email_vars=("email_server" "email_port" "email_encryption" "email_username" "email_password" "email_from" "email_template")
|
||||||
for var in "${email_vars[@]}"; do
|
for var in "${email_vars[@]}"; do
|
||||||
@@ -175,12 +174,12 @@ set_envvars() {
|
|||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Email (iterate through all possible fields)
|
# Email (iterate through all possible fields)
|
||||||
email_fields=("server:email_server" "port:email_port" "encryption:email_encryption"
|
email_fields=("server:email_server" "port:email_port" "encryption:email_encryption"
|
||||||
"username:email_username" "email_password:email_password"
|
"username:email_username" "email_password:email_password"
|
||||||
"send_from:email_from" "email_template:email_template")
|
"send_from:email_from" "email_template:email_template")
|
||||||
|
|
||||||
for field_pair in "${email_fields[@]}"; do
|
for field_pair in "${email_fields[@]}"; do
|
||||||
IFS=: read -r field var <<< "$field_pair"
|
IFS=: read -r field var <<< "$field_pair"
|
||||||
[[ -n "${!var}" ]] && set_ini Email "$field" "${!var}"
|
[[ -n "${!var}" ]] && set_ini Email "$field" "${!var}"
|
||||||
@@ -190,7 +189,7 @@ set_envvars() {
|
|||||||
# Start service and monitor logs
|
# Start service and monitor logs
|
||||||
start_and_monitor() {
|
start_and_monitor() {
|
||||||
printf "\n---------------------- STARTING CORE -----------------------\n"
|
printf "\n---------------------- STARTING CORE -----------------------\n"
|
||||||
|
|
||||||
# Due to some instances complaining about this, making sure its there every time.
|
# Due to some instances complaining about this, making sure its there every time.
|
||||||
mkdir -p /dev/net
|
mkdir -p /dev/net
|
||||||
mknod /dev/net/tun c 10 200
|
mknod /dev/net/tun c 10 200
|
||||||
@@ -199,15 +198,15 @@ start_and_monitor() {
|
|||||||
# Actually starting WGDashboard
|
# Actually starting WGDashboard
|
||||||
echo "Activating Python venv and executing the WireGuard Dashboard service."
|
echo "Activating Python venv and executing the WireGuard Dashboard service."
|
||||||
bash ./wgd.sh start
|
bash ./wgd.sh start
|
||||||
|
|
||||||
# Wait a second before continuing, to give the python program some time to get ready.
|
# Wait a second before continuing, to give the python program some time to get ready.
|
||||||
sleep 1
|
sleep 1
|
||||||
echo -e "\nEnsuring container continuation."
|
echo -e "\nEnsuring container continuation."
|
||||||
|
|
||||||
# Find and monitor log file
|
# Find and monitor log file
|
||||||
local logdir="${WGDASH}/src/log"
|
local logdir="${WGDASH}/src/log"
|
||||||
latestErrLog=$(find "$logdir" -name "error_*.log" -type f -print | sort -r | head -n 1)
|
latestErrLog=$(find "$logdir" -name "error_*.log" -type f -print | sort -r | head -n 1)
|
||||||
|
|
||||||
# Only tail the logs if they are found
|
# Only tail the logs if they are found
|
||||||
if [ -n "$latestErrLog" ]; then
|
if [ -n "$latestErrLog" ]; then
|
||||||
tail -f "$latestErrLog" &
|
tail -f "$latestErrLog" &
|
||||||
@@ -222,4 +221,4 @@ start_and_monitor() {
|
|||||||
# Main execution flow
|
# Main execution flow
|
||||||
ensure_installation
|
ensure_installation
|
||||||
set_envvars
|
set_envvars
|
||||||
start_and_monitor
|
start_and_monitor
|
741
src/dashboard.py
741
src/dashboard.py
@@ -1,27 +1,45 @@
|
|||||||
|
# --- Standard library imports ---
|
||||||
|
import configparser
|
||||||
|
import hashlib
|
||||||
|
import ipaddress
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import random, shutil, sqlite3, configparser, hashlib, ipaddress, json, os, secrets, subprocess
|
import os
|
||||||
import time, re, uuid, bcrypt, psutil, pyotp, threading
|
import random
|
||||||
|
import re
|
||||||
|
import secrets
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
import uuid
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from itertools import islice
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
|
# --- Third-party imports ---
|
||||||
|
import bcrypt
|
||||||
|
import psutil
|
||||||
|
import pyotp
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from jinja2 import Template
|
from flask import Flask, request, render_template, session, send_file, Response
|
||||||
from flask import Flask, request, render_template, session, send_file
|
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from icmplib import ping, traceroute
|
|
||||||
from flask.json.provider import DefaultJSONProvider
|
from flask.json.provider import DefaultJSONProvider
|
||||||
from itertools import islice
|
from icmplib import ping, traceroute
|
||||||
|
from jinja2 import Template
|
||||||
|
from packaging import version
|
||||||
from sqlalchemy import RowMapping
|
from sqlalchemy import RowMapping
|
||||||
|
|
||||||
|
# --- Local module imports ---
|
||||||
|
from client import createClientBlueprint
|
||||||
from modules.Utilities import (
|
from modules.Utilities import (
|
||||||
RegexMatch, StringToBoolean,
|
RegexMatch, StringToBoolean,
|
||||||
ValidateIPAddressesWithRange, ValidateDNSAddress,
|
ValidateIPAddressesWithRange, ValidateDNSAddress,
|
||||||
GenerateWireguardPublicKey, GenerateWireguardPrivateKey
|
GenerateWireguardPublicKey, GenerateWireguardPrivateKey
|
||||||
)
|
)
|
||||||
from packaging import version
|
|
||||||
from modules.Email import EmailSender
|
from modules.Email import EmailSender
|
||||||
from modules.DashboardLogger import DashboardLogger
|
from modules.DashboardLogger import DashboardLogger
|
||||||
from modules.PeerJob import PeerJob
|
from modules.PeerJob import PeerJob
|
||||||
@@ -31,16 +49,15 @@ from modules.PeerJobs import PeerJobs
|
|||||||
from modules.DashboardConfig import DashboardConfig
|
from modules.DashboardConfig import DashboardConfig
|
||||||
from modules.WireguardConfiguration import WireguardConfiguration
|
from modules.WireguardConfiguration import WireguardConfiguration
|
||||||
from modules.AmneziaWireguardConfiguration import AmneziaWireguardConfiguration
|
from modules.AmneziaWireguardConfiguration import AmneziaWireguardConfiguration
|
||||||
|
|
||||||
from client import createClientBlueprint
|
|
||||||
|
|
||||||
from logging.config import dictConfig
|
|
||||||
|
|
||||||
from modules.DashboardClients import DashboardClients
|
from modules.DashboardClients import DashboardClients
|
||||||
from modules.DashboardPlugins import DashboardPlugins
|
from modules.DashboardPlugins import DashboardPlugins
|
||||||
from modules.DashboardWebHooks import DashboardWebHooks
|
from modules.DashboardWebHooks import DashboardWebHooks
|
||||||
from modules.NewConfigurationTemplates import NewConfigurationTemplates
|
from modules.NewConfigurationTemplates import NewConfigurationTemplates
|
||||||
|
|
||||||
|
# --- Logging configuration ---
|
||||||
|
from logging.config import dictConfig
|
||||||
|
|
||||||
|
|
||||||
class CustomJsonEncoder(DefaultJSONProvider):
|
class CustomJsonEncoder(DefaultJSONProvider):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
super().__init__(app)
|
super().__init__(app)
|
||||||
@@ -55,7 +72,6 @@ class CustomJsonEncoder(DefaultJSONProvider):
|
|||||||
return super().default(self)
|
return super().default(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Response Object
|
Response Object
|
||||||
'''
|
'''
|
||||||
@@ -72,6 +88,7 @@ def ResponseObject(status=True, message=None, data=None, status_code = 200) -> F
|
|||||||
'''
|
'''
|
||||||
Flask App
|
Flask App
|
||||||
'''
|
'''
|
||||||
|
app = Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin"))
|
||||||
|
|
||||||
def peerInformationBackgroundThread():
|
def peerInformationBackgroundThread():
|
||||||
global WireguardConfigurations
|
global WireguardConfigurations
|
||||||
@@ -119,8 +136,7 @@ def peerJobScheduleBackgroundThread():
|
|||||||
def gunicornConfig():
|
def gunicornConfig():
|
||||||
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
|
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
|
||||||
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
|
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
|
||||||
|
return app_ip, app_port
|
||||||
return app_ip, app_port, log_level
|
|
||||||
|
|
||||||
def ProtocolsEnabled() -> list[str]:
|
def ProtocolsEnabled() -> list[str]:
|
||||||
from shutil import which
|
from shutil import which
|
||||||
@@ -172,7 +188,16 @@ def startThreads():
|
|||||||
scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread, daemon=True)
|
scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread, daemon=True)
|
||||||
scheduleJobThread.start()
|
scheduleJobThread.start()
|
||||||
|
|
||||||
app = Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin"))
|
dictConfig({
|
||||||
|
'version': 1,
|
||||||
|
'formatters': {'default': {
|
||||||
|
'format': '[%(asctime)s] [%(levelname)s] in [%(module)s] %(message)s',
|
||||||
|
}},
|
||||||
|
'root': {
|
||||||
|
'level': 'INFO'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
WireguardConfigurations: dict[str, WireguardConfiguration] = {}
|
WireguardConfigurations: dict[str, WireguardConfiguration] = {}
|
||||||
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
||||||
@@ -180,82 +205,127 @@ CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
|||||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
|
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
|
||||||
app.secret_key = secrets.token_urlsafe(32)
|
app.secret_key = secrets.token_urlsafe(32)
|
||||||
app.json = CustomJsonEncoder(app)
|
app.json = CustomJsonEncoder(app)
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
SystemStatus = SystemStatus()
|
SystemStatus = SystemStatus()
|
||||||
DashboardConfig = DashboardConfig()
|
DashboardConfig = DashboardConfig()
|
||||||
EmailSender = EmailSender(DashboardConfig)
|
EmailSender = EmailSender(DashboardConfig)
|
||||||
AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations)
|
AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations)
|
||||||
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations)
|
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations)
|
||||||
DashboardLogger: DashboardLogger = DashboardLogger()
|
DashboardLogger = DashboardLogger()
|
||||||
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
||||||
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
||||||
NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates()
|
NewConfigurationTemplates = NewConfigurationTemplates()
|
||||||
|
|
||||||
InitWireguardConfigurationsList(startup=True)
|
InitWireguardConfigurationsList(startup=True)
|
||||||
|
DashboardClients = DashboardClients(WireguardConfigurations)
|
||||||
DashboardClients: DashboardClients = DashboardClients(WireguardConfigurations)
|
app.register_blueprint(createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients))
|
||||||
app.register_blueprint(
|
|
||||||
createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients)
|
|
||||||
)
|
|
||||||
|
|
||||||
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
|
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
|
||||||
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
|
|
||||||
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
|
|
||||||
_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
|
|
||||||
_, log_level = DashboardConfig.GetConfig("Server", "log_level")
|
|
||||||
|
|
||||||
cors = CORS(app, resources={rf"{APP_PREFIX}/api/*": {
|
cors = CORS(app, resources={rf"{APP_PREFIX}/api/*": {
|
||||||
"origins": "*",
|
"origins": "*",
|
||||||
"methods": "DELETE, POST, GET, OPTIONS",
|
"methods": "DELETE, POST, GET, OPTIONS",
|
||||||
"allow_headers": ["Content-Type", "wg-dashboard-apikey"]
|
"allow_headers": ["Content-Type", "wg-dashboard-apikey"]
|
||||||
}})
|
}})
|
||||||
|
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
|
||||||
|
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
|
||||||
|
_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
|
||||||
|
|
||||||
app.logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
|
|
||||||
|
|
||||||
dictConfig({
|
|
||||||
'version': 1,
|
|
||||||
'disable_existing_loggers': False,
|
|
||||||
'formatters': {
|
|
||||||
'default': {
|
|
||||||
'format': '[%(asctime)s] [%(levelname)s] in [%(module)s] %(message)s',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'handlers': {
|
|
||||||
'wsgi': {
|
|
||||||
'class': 'logging.StreamHandler',
|
|
||||||
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
|
||||||
'formatter': 'default',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
API Routes
|
API Routes
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def _enforce_session_auth():
|
||||||
|
"""Enforce session authentication for non-API key access."""
|
||||||
|
white_list = [
|
||||||
|
'/static/',
|
||||||
|
'validateAuthentication',
|
||||||
|
'authenticate',
|
||||||
|
'getDashboardConfiguration',
|
||||||
|
'getDashboardTheme',
|
||||||
|
'getDashboardVersion',
|
||||||
|
'sharePeer/get',
|
||||||
|
'isTotpEnabled',
|
||||||
|
'locale',
|
||||||
|
'/fileDownload',
|
||||||
|
'/client'
|
||||||
|
]
|
||||||
|
|
||||||
|
path_ok = (
|
||||||
|
("username" in session and session.get("role") == "admin")
|
||||||
|
or (f"{APP_PREFIX}/" == request.path or f"{APP_PREFIX}" == request.path)
|
||||||
|
or not all(sub not in request.path for sub in white_list)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not path_ok:
|
||||||
|
response = Flask.make_response(app, {
|
||||||
|
"status": False,
|
||||||
|
"message": "Unauthorized access.",
|
||||||
|
"data": None
|
||||||
|
})
|
||||||
|
response.content_type = "application/json"
|
||||||
|
response.status_code = 401
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def _login_with_token(key):
|
||||||
|
auth_token = hashlib.sha256(f"{key}{datetime.now()}".encode()).hexdigest()
|
||||||
|
session.update({'role': 'admin', 'username': auth_token})
|
||||||
|
session.permanent = True
|
||||||
|
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
||||||
|
resp.set_cookie("authToken", auth_token)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
def _login_with_credentials(data):
|
||||||
|
username = data.get('username')
|
||||||
|
password = data.get('password')
|
||||||
|
totp_code = data.get('totp')
|
||||||
|
|
||||||
|
valid_password = bcrypt.checkpw(password.encode("utf-8"),
|
||||||
|
DashboardConfig.GetConfig("Account", "password")[1].encode("utf-8"))
|
||||||
|
totp_enabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]
|
||||||
|
totp_valid = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now() == totp_code if totp_enabled else True
|
||||||
|
|
||||||
|
if username == DashboardConfig.GetConfig("Account", "username")[1] and valid_password and totp_valid:
|
||||||
|
auth_token = hashlib.sha256(f"{username}{datetime.now()}".encode()).hexdigest()
|
||||||
|
session.update({'role': 'admin', 'username': auth_token})
|
||||||
|
session.permanent = True
|
||||||
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login success: {username}")
|
||||||
|
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
||||||
|
resp.set_cookie("authToken", auth_token)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login failed: {username}")
|
||||||
|
msg = "Sorry, your username, password or OTP is incorrect." if totp_enabled else "Sorry, your username or password is incorrect."
|
||||||
|
return ResponseObject(False, msg)
|
||||||
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def auth_req():
|
def auth_req():
|
||||||
|
# Skip preflight requests
|
||||||
if request.method.lower() == 'options':
|
if request.method.lower() == 'options':
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
|
|
||||||
|
# Logging
|
||||||
if "api" in request.path:
|
if "api" in request.path:
|
||||||
if str(request.method) == "GET":
|
log_message = str(request.args) if request.method.upper() == "GET" else f"Request Args: {str(request.args)} Body:{str(request.get_json())}"
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=str(request.args))
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=log_message)
|
||||||
elif str(request.method) == "POST":
|
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Request Args: {str(request.args)} Body:{str(request.get_json())}")
|
authentication_required = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
||||||
|
headers = request.headers
|
||||||
|
|
||||||
authenticationRequired = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
if authentication_required:
|
||||||
d = request.headers
|
api_key = headers.get('wg-dashboard-apikey')
|
||||||
if authenticationRequired:
|
api_key_enabled = DashboardConfig.GetConfig("Server", "dashboard_api_key")[1]
|
||||||
apiKey = d.get('wg-dashboard-apikey')
|
|
||||||
apiKeyEnabled = DashboardConfig.GetConfig("Server", "dashboard_api_key")[1]
|
# API key authentication
|
||||||
if apiKey is not None and len(apiKey) > 0 and apiKeyEnabled:
|
if api_key and api_key_enabled:
|
||||||
apiKeyExist = len(list(filter(lambda x : x.Key == apiKey, DashboardConfig.DashboardAPIKeys))) == 1
|
api_key_exists = any(k.Key == api_key for k in DashboardConfig.DashboardAPIKeys)
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"API Key Access: {('true' if apiKeyExist else 'false')} - Key: {apiKey}")
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"API Key Access: {api_key_exists} - Key: {api_key}")
|
||||||
if not apiKeyExist:
|
|
||||||
|
if not api_key_exists:
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
response = Flask.make_response(app, {
|
response = Flask.make_response(app, {
|
||||||
"status": False,
|
"status": False,
|
||||||
@@ -265,84 +335,49 @@ def auth_req():
|
|||||||
response.content_type = "application/json"
|
response.content_type = "application/json"
|
||||||
response.status_code = 401
|
response.status_code = 401
|
||||||
return response
|
return response
|
||||||
|
|
||||||
DashboardConfig.APIAccessed = True
|
DashboardConfig.APIAccessed = True
|
||||||
else:
|
else:
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
whiteList = [
|
_enforce_session_auth()
|
||||||
'/static/', 'validateAuthentication', 'authenticate', 'getDashboardConfiguration',
|
|
||||||
'getDashboardTheme', 'getDashboardVersion', 'sharePeer/get', 'isTotpEnabled', 'locale',
|
|
||||||
'/fileDownload',
|
|
||||||
'/client'
|
|
||||||
]
|
|
||||||
|
|
||||||
if (("username" not in session or session.get("role") != "admin")
|
|
||||||
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 len(list(filter(lambda x : x not in request.path, whiteList))) == len(whiteList)
|
|
||||||
):
|
|
||||||
response = Flask.make_response(app, {
|
|
||||||
"status": False,
|
|
||||||
"message": "Unauthorized access.",
|
|
||||||
"data": None
|
|
||||||
})
|
|
||||||
response.content_type = "application/json"
|
|
||||||
response.status_code = 401
|
|
||||||
return response
|
|
||||||
|
|
||||||
@app.route(f'{APP_PREFIX}/api/handshake', methods=["GET", "OPTIONS"])
|
@app.route(f'{APP_PREFIX}/api/handshake', methods=["GET", "OPTIONS"])
|
||||||
def API_Handshake():
|
def API_Handshake():
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
|
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
|
||||||
def API_ValidateAuthentication():
|
def API_ValidateAuthentication():
|
||||||
token = request.cookies.get("authToken")
|
token = request.cookies.get("authToken")
|
||||||
if DashboardConfig.GetConfig("Server", "auth_req")[1]:
|
auth_required = 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.")
|
if auth_required and (not token or "username" not in session or session["username"] != token):
|
||||||
|
return ResponseObject(False, "Invalid authentication.",
|
||||||
|
status_code=401)
|
||||||
|
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/requireAuthentication')
|
@app.get(f'{APP_PREFIX}/api/requireAuthentication')
|
||||||
def API_RequireAuthentication():
|
def API_RequireAuthentication():
|
||||||
return ResponseObject(data=DashboardConfig.GetConfig("Server", "auth_req")[1])
|
return ResponseObject(data=DashboardConfig.GetConfig("Server", "auth_req")[1])
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/authenticate')
|
@app.post(f'{APP_PREFIX}/api/authenticate')
|
||||||
def API_AuthenticateLogin():
|
def API_AuthenticateLogin():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if not DashboardConfig.GetConfig("Server", "auth_req")[1]:
|
auth_req = 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['role'] = 'admin'
|
|
||||||
session['username'] = authToken
|
|
||||||
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
|
||||||
resp.set_cookie("authToken", authToken)
|
|
||||||
session.permanent = True
|
|
||||||
return resp
|
|
||||||
valid = bcrypt.checkpw(data['password'].encode("utf-8"),
|
|
||||||
DashboardConfig.GetConfig("Account", "password")[1].encode("utf-8"))
|
|
||||||
totpEnabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]
|
|
||||||
totpValid = False
|
|
||||||
if totpEnabled:
|
|
||||||
totpValid = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now() == data['totp']
|
|
||||||
|
|
||||||
if (valid
|
if not auth_req:
|
||||||
and data['username'] == DashboardConfig.GetConfig("Account", "username")[1]
|
return ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
||||||
and ((totpEnabled and totpValid) or not totpEnabled)
|
|
||||||
):
|
# API key login
|
||||||
authToken = hashlib.sha256(f"{data['username']}{datetime.now()}".encode()).hexdigest()
|
if DashboardConfig.APIAccessed:
|
||||||
session['role'] = 'admin'
|
return _login_with_token(request.headers.get('wg-dashboard-apikey'))
|
||||||
session['username'] = authToken
|
|
||||||
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
# User login
|
||||||
resp.set_cookie("authToken", authToken)
|
return _login_with_credentials(data)
|
||||||
session.permanent = True
|
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login success: {data['username']}")
|
|
||||||
return resp
|
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login failed: {data['username']}")
|
|
||||||
if totpEnabled:
|
|
||||||
return ResponseObject(False, "Sorry, your username, password or OTP is incorrect.")
|
|
||||||
else:
|
|
||||||
return ResponseObject(False, "Sorry, your username or password is incorrect.")
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/signout')
|
@app.get(f'{APP_PREFIX}/api/signout')
|
||||||
def API_SignOut():
|
def API_SignOut():
|
||||||
@@ -354,7 +389,7 @@ def API_SignOut():
|
|||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurations')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurations')
|
||||||
def API_getWireguardConfigurations():
|
def API_getWireguardConfigurations():
|
||||||
InitWireguardConfigurationsList()
|
InitWireguardConfigurationsList()
|
||||||
return ResponseObject(data=[wc for wc in WireguardConfigurations.values()])
|
return ResponseObject(data=list(WireguardConfigurations.values()))
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/newConfigurationTemplates')
|
@app.get(f'{APP_PREFIX}/api/newConfigurationTemplates')
|
||||||
def API_NewConfigurationTemplates():
|
def API_NewConfigurationTemplates():
|
||||||
@@ -367,193 +402,255 @@ def API_NewConfigurationTemplates_CreateTemplate():
|
|||||||
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/updateTemplate')
|
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/updateTemplate')
|
||||||
def API_NewConfigurationTemplates_UpdateTemplate():
|
def API_NewConfigurationTemplates_UpdateTemplate():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
template = data.get('Template', None)
|
template = data.get('Template')
|
||||||
|
|
||||||
if not template:
|
if not template:
|
||||||
return ResponseObject(False, "Please provide template")
|
return ResponseObject(False, "Please provide template",
|
||||||
|
status_code=400)
|
||||||
|
|
||||||
status, msg = NewConfigurationTemplates.UpdateTemplate(template)
|
status, msg = NewConfigurationTemplates.UpdateTemplate(template)
|
||||||
|
|
||||||
return ResponseObject(status, msg)
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/deleteTemplate')
|
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/deleteTemplate')
|
||||||
def API_NewConfigurationTemplates_DeleteTemplate():
|
def API_NewConfigurationTemplates_DeleteTemplate():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
template = data.get('Template', None)
|
template = data.get('Template')
|
||||||
if not template:
|
|
||||||
return ResponseObject(False, "Please provide template")
|
|
||||||
|
|
||||||
|
if not template:
|
||||||
|
return ResponseObject(False, "Please provide template",
|
||||||
|
status_code=400)
|
||||||
|
|
||||||
status, msg = NewConfigurationTemplates.DeleteTemplate(template)
|
status, msg = NewConfigurationTemplates.DeleteTemplate(template)
|
||||||
|
|
||||||
return ResponseObject(status, msg)
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/addWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/addWireguardConfiguration')
|
||||||
def API_addWireguardConfiguration():
|
def API_addWireguardConfiguration():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
requiredKeys = [
|
protocol = data.get("Protocol")
|
||||||
"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
|
required_keys = {"ConfigurationName", "Address", "ListenPort", "PrivateKey", "Protocol"}
|
||||||
for i in WireguardConfigurations.values():
|
if not required_keys.issubset(data.keys()):
|
||||||
if i.Name == data['ConfigurationName']:
|
return ResponseObject(False, "Please provide all required parameters.", status_code=400)
|
||||||
return ResponseObject(False,
|
|
||||||
f"Already have a configuration with the name \"{data['ConfigurationName']}\"",
|
|
||||||
"ConfigurationName")
|
|
||||||
|
|
||||||
if str(i.ListenPort) == str(data["ListenPort"]):
|
if protocol not in ProtocolsEnabled():
|
||||||
return ResponseObject(False,
|
return ResponseObject(False, "Please provide a valid protocol: wg / awg.", status_code=400)
|
||||||
f"Already have a configuration with the port \"{data['ListenPort']}\"",
|
|
||||||
"ListenPort")
|
|
||||||
|
|
||||||
if i.Address == data["Address"]:
|
for cfg in WireguardConfigurations.values():
|
||||||
return ResponseObject(False,
|
duplicates = {
|
||||||
f"Already have a configuration with the address \"{data['Address']}\"",
|
"ConfigurationName": cfg.Name == data['ConfigurationName'],
|
||||||
"Address")
|
"ListenPort": str(cfg.ListenPort) == str(data["ListenPort"]),
|
||||||
|
"Address": cfg.Address == data["Address"]
|
||||||
if "Backup" in data.keys():
|
|
||||||
path = {
|
|
||||||
"wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
|
||||||
"awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
|
|
||||||
}
|
}
|
||||||
|
for key, is_duplicate in duplicates.items():
|
||||||
if (os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"])) and
|
if is_duplicate:
|
||||||
os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
return ResponseObject(
|
||||||
protocol = "wg"
|
False,
|
||||||
elif (os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"])) and
|
f"Already have a configuration with the {key.lower()} \"{data[key]}\"",
|
||||||
os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
key,
|
||||||
protocol = "awg"
|
status_code=400
|
||||||
else:
|
)
|
||||||
return ResponseObject(False, "Backup does not exist")
|
|
||||||
|
paths = {
|
||||||
|
"wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||||
|
"awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if "Backup" in data:
|
||||||
|
backup_file = data["Backup"]
|
||||||
|
protocol_detected = None
|
||||||
|
for proto, base_path in paths.items():
|
||||||
|
conf_path = os.path.join(base_path, 'WGDashboard_Backup', backup_file)
|
||||||
|
sql_path = os.path.join(base_path, 'WGDashboard_Backup', backup_file.replace('.conf', '.sql'))
|
||||||
|
if os.path.exists(conf_path) and os.path.exists(sql_path):
|
||||||
|
protocol_detected = proto
|
||||||
|
break
|
||||||
|
|
||||||
|
if not protocol_detected:
|
||||||
|
return ResponseObject(False, "Backup does not exist", status_code=400)
|
||||||
|
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(path[protocol], 'WGDashboard_Backup', data["Backup"]),
|
os.path.join(paths[protocol_detected], 'WGDashboard_Backup', backup_file),
|
||||||
os.path.join(path[protocol], f'{data["ConfigurationName"]}.conf')
|
os.path.join(paths[protocol_detected], f'{data["ConfigurationName"]}.conf')
|
||||||
)
|
)
|
||||||
WireguardConfigurations[data['ConfigurationName']] = (
|
protocol = protocol_detected # Use backup protocol
|
||||||
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data, name=data['ConfigurationName'])) if protocol == 'wg' else (
|
|
||||||
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data, name=data['ConfigurationName']))
|
|
||||||
else:
|
else:
|
||||||
WireguardConfigurations[data['ConfigurationName']] = (
|
conf_path = os.path.join(paths[protocol], f'{data["ConfigurationName"]}.conf')
|
||||||
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) if data.get('Protocol') == 'wg' else (
|
if not os.path.exists(conf_path):
|
||||||
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data))
|
with open(conf_path, 'w') as f:
|
||||||
|
f.write(
|
||||||
|
f"[Interface]\n"
|
||||||
|
f"Address = {data['Address']}\n"
|
||||||
|
f"ListenPort = {data['ListenPort']}\n"
|
||||||
|
f"PrivateKey = {data['PrivateKey']}\n"
|
||||||
|
)
|
||||||
|
os.chmod(conf_path, 0o600) # secure file permissions
|
||||||
|
|
||||||
|
ConfigClass = WireguardConfiguration if protocol == "wg" else AmneziaWireguardConfiguration
|
||||||
|
|
||||||
|
WireguardConfigurations[data['ConfigurationName']] = ConfigClass(
|
||||||
|
DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks,
|
||||||
|
data=data, name=data['ConfigurationName']
|
||||||
|
)
|
||||||
|
|
||||||
return ResponseObject()
|
return ResponseObject()
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration')
|
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration')
|
||||||
def API_toggleWireguardConfiguration():
|
def API_toggleWireguardConfiguration():
|
||||||
configurationName = request.args.get('configurationName')
|
configuration_name = request.args.get('configurationName')
|
||||||
if configurationName is None or len(
|
|
||||||
configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
|
if not configuration_name or configuration_name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
|
return ResponseObject(False, "Please provide a valid configuration name",
|
||||||
toggleStatus, msg = WireguardConfigurations[configurationName].toggleConfiguration()
|
status_code=404)
|
||||||
return ResponseObject(toggleStatus, msg, WireguardConfigurations[configurationName].Status)
|
|
||||||
|
target_configuration = WireguardConfigurations[configuration_name]
|
||||||
|
status, msg = target_configuration.toggleConfiguration()
|
||||||
|
configuration_status = target_configuration.Status
|
||||||
|
|
||||||
|
return ResponseObject(status, msg, configuration_status)
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updateWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/updateWireguardConfiguration')
|
||||||
def API_updateWireguardConfiguration():
|
def API_updateWireguardConfiguration():
|
||||||
data = request.get_json()
|
data = request.get_json() or {}
|
||||||
requiredKeys = ["Name"]
|
|
||||||
for i in requiredKeys:
|
|
||||||
if i not in data.keys():
|
|
||||||
return ResponseObject(False, "Please provide these following field: " + ", ".join(requiredKeys))
|
|
||||||
name = data.get("Name")
|
name = data.get("Name")
|
||||||
if name not in WireguardConfigurations.keys():
|
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
|
||||||
|
|
||||||
status, msg = WireguardConfigurations[name].updateConfigurationSettings(data)
|
if not name:
|
||||||
|
return ResponseObject(False, "Please provide the field: Name",
|
||||||
|
status_code=400)
|
||||||
|
|
||||||
return ResponseObject(status, message=msg, data=WireguardConfigurations[name])
|
if name not in WireguardConfigurations:
|
||||||
|
return ResponseObject(False, "Configuration does not exist",
|
||||||
|
status_code=404)
|
||||||
|
|
||||||
|
target_configuration = WireguardConfigurations[name]
|
||||||
|
status, msg = target_configuration.updateConfigurationSettings(data)
|
||||||
|
|
||||||
|
return ResponseObject(status, msg, target_configuration)
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationInfo')
|
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationInfo')
|
||||||
def API_updateWireguardConfigurationInfo():
|
def API_updateWireguardConfigurationInfo():
|
||||||
data = request.get_json()
|
data = request.get_json() or {}
|
||||||
name = data.get('Name')
|
name = data.get('Name')
|
||||||
key = data.get('Key')
|
key = data.get('Key')
|
||||||
value = data.get('Value')
|
value = data.get('Value')
|
||||||
if not all([data, key, name]):
|
|
||||||
return ResponseObject(status=False, message="Please provide configuration name, key and value")
|
if not all([name, key, value]): # Required values
|
||||||
if name not in WireguardConfigurations.keys():
|
return ResponseObject(False, "Please provide configuration name, key, and value")
|
||||||
|
|
||||||
|
if name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
|
||||||
status, msg, key = WireguardConfigurations[name].updateConfigurationInfo(key, value)
|
target_configuration = WireguardConfigurations[name]
|
||||||
|
status, msg, key = target_configuration.updateConfigurationInfo(key, value)
|
||||||
return ResponseObject(status=status, message=msg, data=key)
|
|
||||||
|
return ResponseObject(status, msg, key)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRawFile')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRawFile')
|
||||||
def API_GetWireguardConfigurationRawFile():
|
def API_getWireguardConfigurationRawFile():
|
||||||
configurationName = request.args.get('configurationName')
|
configuration_name = request.args.get('configurationName')
|
||||||
if configurationName is None or len(
|
|
||||||
configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
|
if not configuration_name or configuration_name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
|
return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
|
||||||
|
|
||||||
|
config = WireguardConfigurations[configuration_name]
|
||||||
|
|
||||||
return ResponseObject(data={
|
return ResponseObject(data={
|
||||||
"path": WireguardConfigurations[configurationName].configPath,
|
"path": config.configPath,
|
||||||
"content": WireguardConfigurations[configurationName].getRawConfigurationFile()
|
"content": config.getRawConfigurationFile()
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationRawFile')
|
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationRawFile')
|
||||||
def API_UpdateWireguardConfigurationRawFile():
|
def API_UpdateWireguardConfigurationRawFile():
|
||||||
data = request.get_json()
|
data = request.get_json() or {}
|
||||||
configurationName = data.get('configurationName')
|
configuration_name = data.get('configurationName')
|
||||||
rawConfiguration = data.get('rawConfiguration')
|
raw_configuration = data.get('rawConfiguration')
|
||||||
if configurationName is None or len(
|
|
||||||
configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
|
if not configuration_name or configuration_name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Please provide a valid configuration name")
|
return ResponseObject(False, "Please provide a valid configuration name")
|
||||||
if rawConfiguration is None or len(rawConfiguration) == 0:
|
|
||||||
|
if not raw_configuration:
|
||||||
return ResponseObject(False, "Please provide content")
|
return ResponseObject(False, "Please provide content")
|
||||||
|
|
||||||
status, err = WireguardConfigurations[configurationName].updateRawConfigurationFile(rawConfiguration)
|
config = WireguardConfigurations[configuration_name]
|
||||||
|
status, err = config.updateRawConfigurationFile(raw_configuration)
|
||||||
|
|
||||||
return ResponseObject(status=status, message=err)
|
return ResponseObject(status, err)
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfiguration')
|
||||||
def API_deleteWireguardConfiguration():
|
def API_deleteWireguardConfiguration():
|
||||||
data = request.get_json()
|
data = request.get_json() or {}
|
||||||
if "ConfigurationName" not in data.keys() or data.get("ConfigurationName") is None or data.get("ConfigurationName") not in WireguardConfigurations.keys():
|
configuration_name = data.get("ConfigurationName")
|
||||||
|
|
||||||
|
if not configuration_name or configuration_name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Please provide the configuration name you want to delete", status_code=404)
|
return ResponseObject(False, "Please provide the configuration name you want to delete", status_code=404)
|
||||||
rp = WireguardConfigurations.pop(data.get("ConfigurationName"))
|
|
||||||
|
|
||||||
|
rp = WireguardConfigurations.pop(configuration_name)
|
||||||
status = rp.deleteConfiguration()
|
status = rp.deleteConfiguration()
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
WireguardConfigurations[data.get("ConfigurationName")] = rp
|
WireguardConfigurations[configuration_name] = rp
|
||||||
|
|
||||||
return ResponseObject(status)
|
return ResponseObject(status)
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/renameWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/renameWireguardConfiguration')
|
||||||
def API_renameWireguardConfiguration():
|
def API_renameWireguardConfiguration():
|
||||||
data = request.get_json()
|
data = request.get_json() or {}
|
||||||
keys = ["ConfigurationName", "NewConfigurationName"]
|
|
||||||
for k in keys:
|
old_name = data.get("ConfigurationName")
|
||||||
if (k not in data.keys() or data.get(k) is None or len(data.get(k)) == 0 or
|
new_name = data.get("NewConfigurationName")
|
||||||
(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)
|
if not old_name or old_name not in WireguardConfigurations:
|
||||||
|
return ResponseObject(False, "Please provide a valid configuration name to rename", status_code=404)
|
||||||
|
|
||||||
if data.get("NewConfigurationName") in WireguardConfigurations.keys():
|
if not new_name:
|
||||||
return ResponseObject(False, "Configuration name already exist", status_code=400)
|
return ResponseObject(False, "Please provide a new configuration name", status=400)
|
||||||
|
|
||||||
rc = WireguardConfigurations.pop(data.get("ConfigurationName"))
|
if new_name in WireguardConfigurations:
|
||||||
|
return ResponseObject(False, "The configuration name already exists", status_code=400)
|
||||||
|
|
||||||
status, message = rc.renameConfiguration(data.get("NewConfigurationName"))
|
rc = WireguardConfigurations.pop(old_name)
|
||||||
|
status, message = rc.renameConfiguration(new_name)
|
||||||
|
|
||||||
if status:
|
if status:
|
||||||
WireguardConfigurations[data.get("NewConfigurationName")] = (WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")) if rc.Protocol == 'wg' else AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")))
|
if rc.Protocol == 'wg':
|
||||||
|
ConfigClass = WireguardConfiguration
|
||||||
|
else:
|
||||||
|
ConfigClass = AmneziaWireguardConfiguration
|
||||||
|
|
||||||
|
WireguardConfigurations[new_name] = ConfigClass(
|
||||||
|
DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, new_name
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
WireguardConfigurations[data.get("ConfigurationName")] = rc
|
WireguardConfigurations[old_name] = rc
|
||||||
|
|
||||||
return ResponseObject(status, message)
|
return ResponseObject(status, message)
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRealtimeTraffic')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRealtimeTraffic')
|
||||||
def API_getWireguardConfigurationRealtimeTraffic():
|
def API_getWireguardConfigurationRealtimeTraffic():
|
||||||
configurationName = request.args.get('configurationName')
|
configuration_name = requests.args.get('configurationName')
|
||||||
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
|
||||||
|
if not configuration_name or configuration_name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
return ResponseObject(data=WireguardConfigurations[configurationName].getRealtimeTrafficUsage())
|
|
||||||
|
rt_traffic_usage = WireguardConfigurations[configuration_name]
|
||||||
|
return ResponseObject(data=rt_traffic_usage)
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
|
||||||
def API_getWireguardConfigurationBackup():
|
def API_getWireguardConfigurationBackup():
|
||||||
configurationName = request.args.get('configurationName')
|
configuration_name = request.args.get('configurationName')
|
||||||
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
|
||||||
|
if not configuration_name or configuration_name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
return ResponseObject(data=WireguardConfigurations[configurationName].getBackups())
|
|
||||||
|
target_configuration = WireguardConfigurations[configuration_name]
|
||||||
|
return ResponseObject(data=target_configuration.getBackups())
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getAllWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/getAllWireguardConfigurationBackup')
|
||||||
def API_getAllWireguardConfigurationBackup():
|
def API_getAllWireguardConfigurationBackup():
|
||||||
@@ -561,47 +658,86 @@ def API_getAllWireguardConfigurationBackup():
|
|||||||
"ExistingConfigurations": {},
|
"ExistingConfigurations": {},
|
||||||
"NonExistingConfigurations": {}
|
"NonExistingConfigurations": {}
|
||||||
}
|
}
|
||||||
existingConfiguration = WireguardConfigurations.keys()
|
|
||||||
for i in existingConfiguration:
|
existing_configurations = WireguardConfigurations.keys()
|
||||||
b = WireguardConfigurations[i].getBackups(True)
|
|
||||||
if len(b) > 0:
|
for single_conf in existing_configurations:
|
||||||
data['ExistingConfigurations'][i] = WireguardConfigurations[i].getBackups(True)
|
backups = WireguardConfigurations[single_conf].getBackups(True)
|
||||||
|
if len(backups) > 0:
|
||||||
|
data['ExistingConfigurations'][single_conf] = WireguardConfigurations[single_conf].getBackups(True)
|
||||||
|
|
||||||
for protocol in ProtocolsEnabled():
|
for protocol in ProtocolsEnabled():
|
||||||
directory = os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup')
|
config_path_info = DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")
|
||||||
if os.path.exists(directory):
|
configuration_path = config_path_info[1]
|
||||||
files = [(file, os.path.getctime(os.path.join(directory, file)))
|
backup_directory = os.path.join(configuration_path, 'WGDashboard_Backup')
|
||||||
for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))]
|
|
||||||
files.sort(key=lambda x: x[1], reverse=True)
|
if not os.path.exists(backup_directory):
|
||||||
|
continue
|
||||||
for f, ct in files:
|
|
||||||
if RegexMatch(r"^(.*)_(.*)\.(conf)$", f):
|
backup_files = []
|
||||||
s = re.search(r"^(.*)_(.*)\.(conf)$", f)
|
for file_name in os.listdir(backup_directory):
|
||||||
name = s.group(1)
|
full_file_path = os.path.join(backup_directory, file_name)
|
||||||
if name not in existingConfiguration:
|
if os.path.isfile(full_file_path):
|
||||||
if name not in data['NonExistingConfigurations'].keys():
|
creation_time = os.path.getctime(full_file_path)
|
||||||
data['NonExistingConfigurations'][name] = []
|
backup_files.append((file_name, creation_time))
|
||||||
|
|
||||||
date = s.group(2)
|
backup_files.sort(key=lambda file_info: file_info[1], reverse=True)
|
||||||
d = {
|
|
||||||
"protocol": protocol,
|
for file_name, creation_time in backup_files:
|
||||||
"filename": f,
|
pattern = r"^(.*)_(.*)\.conf$"
|
||||||
"backupDate": date,
|
match_result = re.match(pattern, file_name)
|
||||||
"content": open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
|
|
||||||
}
|
if not match_result:
|
||||||
if f.replace(".conf", ".sql") in list(os.listdir(directory)):
|
continue
|
||||||
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()
|
configuration_name = match_result.group(1)
|
||||||
data['NonExistingConfigurations'][name].append(d)
|
backup_date = match_result.group(2)
|
||||||
|
|
||||||
|
if configuration_name in existing_configurations:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if 'NonExistingConfigurations' not in data:
|
||||||
|
data['NonExistingConfigurations'] = {}
|
||||||
|
|
||||||
|
if configuration_name not in data['NonExistingConfigurations']:
|
||||||
|
data['NonExistingConfigurations'][configuration_name] = []
|
||||||
|
|
||||||
|
configuration_file_path = os.path.join(backup_directory, file_name)
|
||||||
|
with open(configuration_file_path, 'r') as configuration_file:
|
||||||
|
configuration_content = configuration_file.read()
|
||||||
|
|
||||||
|
backup_data = {
|
||||||
|
"protocol": protocol,
|
||||||
|
"filename": file_name,
|
||||||
|
"backupDate": backup_date,
|
||||||
|
"content": configuration_content
|
||||||
|
}
|
||||||
|
|
||||||
|
sql_file_name = file_name.replace(".conf", ".sql")
|
||||||
|
sql_file_path = os.path.join(backup_directory, sql_file_name)
|
||||||
|
|
||||||
|
if os.path.isfile(sql_file_path):
|
||||||
|
with open(sql_file_path, 'r') as sql_file:
|
||||||
|
sql_content = sql_file.read()
|
||||||
|
|
||||||
|
backup_data["database"] = True
|
||||||
|
backup_data["databaseContent"] = sql_content
|
||||||
|
|
||||||
|
data['NonExistingConfigurations'][configuration_name].append(backup_data)
|
||||||
|
|
||||||
return ResponseObject(data=data)
|
return ResponseObject(data=data)
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup')
|
||||||
def API_createWireguardConfigurationBackup():
|
def API_createWireguardConfigurationBackup():
|
||||||
configurationName = request.args.get('configurationName')
|
configuration_name = request.args.get('configurationName')
|
||||||
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
|
||||||
|
if not configuration_name or configuration_name not in WireguardConfigurations:
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
return ResponseObject(status=WireguardConfigurations[configurationName].backupConfigurationFile()[0],
|
|
||||||
data=WireguardConfigurations[configurationName].getBackups())
|
conf_backup_file = WireguardConfigurations[configuration_name].backupConfigurationFile()[0]
|
||||||
|
conf_backups = WireguardConfigurations[configuration_name].getBackups()
|
||||||
|
|
||||||
|
return ResponseObject(status=conf_backup_file,data=conf_backups)
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfigurationBackup')
|
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfigurationBackup')
|
||||||
def API_deleteWireguardConfigurationBackup():
|
def API_deleteWireguardConfigurationBackup():
|
||||||
@@ -633,18 +769,19 @@ def API_downloadWireguardConfigurationBackup():
|
|||||||
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
||||||
def API_restoreWireguardConfigurationBackup():
|
def API_restoreWireguardConfigurationBackup():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
configuration_name = data['ConfigurationName']
|
||||||
|
backup_file_name = data['BackupFileName']
|
||||||
|
|
||||||
if ("ConfigurationName" not in data.keys() or
|
if ("ConfigurationName" not in data.keys() or
|
||||||
"BackupFileName" not in data.keys() or
|
"BackupFileName" not in data.keys() or
|
||||||
len(data['ConfigurationName']) == 0 or
|
len(data['ConfigurationName']) == 0 or
|
||||||
len(data['BackupFileName']) == 0):
|
len(data['BackupFileName']) == 0):
|
||||||
return ResponseObject(False,
|
return ResponseObject(False,"Please provide ConfigurationName and BackupFileName in body", status_code=400)
|
||||||
"Please provide ConfigurationName and BackupFileName in body", status_code=400)
|
|
||||||
configurationName = data['ConfigurationName']
|
if configuration_name not in WireguardConfigurations.keys():
|
||||||
backupFileName = data['BackupFileName']
|
|
||||||
if configurationName not in WireguardConfigurations.keys():
|
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
|
||||||
status = WireguardConfigurations[configurationName].restoreBackup(backupFileName)
|
status = WireguardConfigurations[configuration_name].restoreBackup(backup_file_name)
|
||||||
return ResponseObject(status=status, message=(None if status else 'Restore backup failed'))
|
return ResponseObject(status=status, message=(None if status else 'Restore backup failed'))
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getDashboardConfiguration')
|
@app.get(f'{APP_PREFIX}/api/getDashboardConfiguration')
|
||||||
@@ -1386,31 +1523,6 @@ def API_Welcome_Finish():
|
|||||||
DashboardConfig.SetConfig("Other", "welcome_session", False)
|
DashboardConfig.SetConfig("Other", "welcome_session", False)
|
||||||
return ResponseObject()
|
return ResponseObject()
|
||||||
|
|
||||||
class Locale:
|
|
||||||
def __init__(self):
|
|
||||||
self.localePath = './static/locales/'
|
|
||||||
self.activeLanguages = {}
|
|
||||||
with open(os.path.join(f"{self.localePath}supported_locales.json"), "r") as f:
|
|
||||||
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":
|
|
||||||
return None
|
|
||||||
if os.path.exists(os.path.join(f"{self.localePath}{currentLanguage}.json")):
|
|
||||||
with open(os.path.join(f"{self.localePath}{currentLanguage}.json"), "r") as f:
|
|
||||||
return dict(json.loads(''.join(f.readlines())))
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def updateLanguage(self, lang_id):
|
|
||||||
if not os.path.exists(os.path.join(f"{self.localePath}{lang_id}.json")):
|
|
||||||
DashboardConfig.SetConfig("Server", "dashboard_language", "en-US")
|
|
||||||
else:
|
|
||||||
DashboardConfig.SetConfig("Server", "dashboard_language", lang_id)
|
|
||||||
|
|
||||||
Locale = Locale()
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/locale')
|
@app.get(f'{APP_PREFIX}/api/locale')
|
||||||
def API_Locale_CurrentLang():
|
def API_Locale_CurrentLang():
|
||||||
return ResponseObject(data=Locale.getLanguage())
|
return ResponseObject(data=Locale.getLanguage())
|
||||||
@@ -1495,6 +1607,31 @@ def API_SystemStatus():
|
|||||||
def API_ProtocolsEnabled():
|
def API_ProtocolsEnabled():
|
||||||
return ResponseObject(data=ProtocolsEnabled())
|
return ResponseObject(data=ProtocolsEnabled())
|
||||||
|
|
||||||
|
class Locale:
|
||||||
|
def __init__(self):
|
||||||
|
self.localePath = './static/locales/'
|
||||||
|
self.activeLanguages = {}
|
||||||
|
with open(os.path.join(f"{self.localePath}supported_locales.json"), "r") as f:
|
||||||
|
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":
|
||||||
|
return None
|
||||||
|
if os.path.exists(os.path.join(f"{self.localePath}{currentLanguage}.json")):
|
||||||
|
with open(os.path.join(f"{self.localePath}{currentLanguage}.json"), "r") as f:
|
||||||
|
return dict(json.loads(''.join(f.readlines())))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def updateLanguage(self, lang_id):
|
||||||
|
if not os.path.exists(os.path.join(f"{self.localePath}{lang_id}.json")):
|
||||||
|
DashboardConfig.SetConfig("Server", "dashboard_language", "en-US")
|
||||||
|
else:
|
||||||
|
DashboardConfig.SetConfig("Server", "dashboard_language", lang_id)
|
||||||
|
|
||||||
|
Locale = Locale()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
OIDC Controller
|
OIDC Controller
|
||||||
'''
|
'''
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import dashboard
|
import dashboard
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger, Dash
|
global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger, Dash
|
||||||
app_host, app_port, log_level = dashboard.gunicornConfig()
|
app_host, app_port = dashboard.gunicornConfig()
|
||||||
date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S')
|
date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S')
|
||||||
|
|
||||||
def post_worker_init(worker):
|
def post_worker_init(worker):
|
||||||
@@ -16,7 +16,7 @@ daemon = True
|
|||||||
pidfile = './gunicorn.pid'
|
pidfile = './gunicorn.pid'
|
||||||
wsgi_app = "dashboard:app"
|
wsgi_app = "dashboard:app"
|
||||||
accesslog = f"./log/access_{date}.log"
|
accesslog = f"./log/access_{date}.log"
|
||||||
loglevel = f"{log_level}"
|
loglevel = "info"
|
||||||
capture_output = True
|
capture_output = True
|
||||||
errorlog = f"./log/error_{date}.log"
|
errorlog = f"./log/error_{date}.log"
|
||||||
pythonpath = "., ./modules"
|
pythonpath = "., ./modules"
|
||||||
|
@@ -174,7 +174,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
|
|||||||
|
|
||||||
def getPeers(self):
|
def getPeers(self):
|
||||||
self.Peers.clear()
|
self.Peers.clear()
|
||||||
current_app.logger.debug(f"Refreshing {self.Name} peer list")
|
current_app.logger.info(f"Refreshing {self.Name} peer list")
|
||||||
|
|
||||||
if self.configurationFileChanged():
|
if self.configurationFileChanged():
|
||||||
with open(self.configPath, 'r') as configFile:
|
with open(self.configPath, 'r') as configFile:
|
||||||
@@ -183,7 +183,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
|
|||||||
content = configFile.read().split('\n')
|
content = configFile.read().split('\n')
|
||||||
try:
|
try:
|
||||||
if "[Peer]" not in content:
|
if "[Peer]" not in content:
|
||||||
current_app.logger.debug(f"{self.Name} config has no [Peer] section")
|
current_app.logger.info(f"{self.Name} config has no [Peer] section")
|
||||||
return
|
return
|
||||||
|
|
||||||
peerStarts = content.index("[Peer]")
|
peerStarts = content.index("[Peer]")
|
||||||
|
@@ -1,47 +1,25 @@
|
|||||||
# ConnectionString.py
|
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
from sqlalchemy_utils import database_exists, create_database
|
from sqlalchemy_utils import database_exists, create_database
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
default_db = "wgdashboard"
|
def ConnectionString(database) -> str:
|
||||||
default_log_db = "wgdashboard_log"
|
|
||||||
default_job_db = "wgdashboard_job"
|
|
||||||
sqlite_path = "db"
|
|
||||||
if os.path.exists(sqlite_path):
|
|
||||||
os.makedirs(sqlite_path, exist_ok=True)
|
|
||||||
|
|
||||||
def ConnectionString(database_name: str) -> str:
|
|
||||||
"""
|
|
||||||
Returns a SQLAlchemy-compatible connection string for the chosen database.
|
|
||||||
Creates the database if it doesn't exist.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Read and parse the INI file once at startup
|
|
||||||
parser = configparser.ConfigParser(strict=False)
|
parser = configparser.ConfigParser(strict=False)
|
||||||
parser.read("wg-dashboard.ini")
|
parser.read_file(open('wg-dashboard.ini', "r+"))
|
||||||
|
sqlitePath = os.path.join("db")
|
||||||
db_type = parser.get("Database", "type")
|
if not os.path.isdir(sqlitePath):
|
||||||
db_prefix = parser.get("Database", "prefix")
|
os.mkdir(sqlitePath)
|
||||||
database_name = f"{db_prefix}{database_name}"
|
if parser.get("Database", "type") == "postgresql":
|
||||||
|
cn = f'postgresql+psycopg://{parser.get("Database", "username")}:{parser.get("Database", "password")}@{parser.get("Database", "host")}/{database}'
|
||||||
if db_type == "postgresql":
|
elif parser.get("Database", "type") == "mysql":
|
||||||
username = parser.get("Database", "username")
|
cn = f'mysql+pymysql://{parser.get("Database", "username")}:{parser.get("Database", "password")}@{parser.get("Database", "host")}/{database}'
|
||||||
password = parser.get("Database", "password")
|
|
||||||
host = parser.get("Database", "host")
|
|
||||||
cn = f"postgresql+psycopg://{username}:{password}@{host}/{database_name}"
|
|
||||||
elif db_type == "mysql":
|
|
||||||
username = parser.get("Database", "username")
|
|
||||||
password = parser.get("Database", "password")
|
|
||||||
host = parser.get("Database", "host")
|
|
||||||
cn = f"mysql+pymysql://{username}:{password}@{host}/{database_name}"
|
|
||||||
else:
|
else:
|
||||||
cn = f'sqlite:///{os.path.join(sqlite_path, f"{database_name}.db")}'
|
cn = f'sqlite:///{os.path.join(sqlitePath, f"{database}.db")}'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not database_exists(cn):
|
if not database_exists(cn):
|
||||||
create_database(cn)
|
create_database(cn)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.critical("Database error. Terminating...", e)
|
current_app.logger.error("Database error. Terminating...", e)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
return cn
|
return cn
|
@@ -8,7 +8,7 @@ import pyotp
|
|||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
from .DashboardClientsPeerAssignment import DashboardClientsPeerAssignment
|
from .DashboardClientsPeerAssignment import DashboardClientsPeerAssignment
|
||||||
from .DashboardClientsTOTP import DashboardClientsTOTP
|
from .DashboardClientsTOTP import DashboardClientsTOTP
|
||||||
from .DashboardOIDC import DashboardOIDC
|
from .DashboardOIDC import DashboardOIDC
|
||||||
@@ -20,7 +20,7 @@ from flask import session
|
|||||||
class DashboardClients:
|
class DashboardClients:
|
||||||
def __init__(self, wireguardConfigurations):
|
def __init__(self, wireguardConfigurations):
|
||||||
self.logger = DashboardLogger()
|
self.logger = DashboardLogger()
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.OIDC = DashboardOIDC("Client")
|
self.OIDC = DashboardOIDC("Client")
|
||||||
|
|
||||||
@@ -32,10 +32,10 @@ class DashboardClients:
|
|||||||
db.Column('TotpKey', db.String(500)),
|
db.Column('TotpKey', db.String(500)),
|
||||||
db.Column('TotpKeyVerified', db.Integer),
|
db.Column('TotpKeyVerified', db.Integer),
|
||||||
db.Column('CreatedDate',
|
db.Column('CreatedDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP),
|
||||||
server_default=db.func.now()),
|
server_default=db.func.now()),
|
||||||
db.Column('DeletedDate',
|
db.Column('DeletedDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP)),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP)),
|
||||||
extend_existing=True,
|
extend_existing=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,10 +46,10 @@ class DashboardClients:
|
|||||||
db.Column('ProviderIssuer', db.String(500), nullable=False, index=True),
|
db.Column('ProviderIssuer', db.String(500), nullable=False, index=True),
|
||||||
db.Column('ProviderSubject', db.String(500), nullable=False, index=True),
|
db.Column('ProviderSubject', db.String(500), nullable=False, index=True),
|
||||||
db.Column('CreatedDate',
|
db.Column('CreatedDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP),
|
||||||
server_default=db.func.now()),
|
server_default=db.func.now()),
|
||||||
db.Column('DeletedDate',
|
db.Column('DeletedDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP)),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP)),
|
||||||
extend_existing=True,
|
extend_existing=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,10 +65,10 @@ class DashboardClients:
|
|||||||
db.Column('ResetToken', db.String(255), nullable=False, primary_key=True),
|
db.Column('ResetToken', db.String(255), nullable=False, primary_key=True),
|
||||||
db.Column('ClientID', db.String(255), nullable=False),
|
db.Column('ClientID', db.String(255), nullable=False),
|
||||||
db.Column('CreatedDate',
|
db.Column('CreatedDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP),
|
||||||
server_default=db.func.now()),
|
server_default=db.func.now()),
|
||||||
db.Column('ExpiryDate',
|
db.Column('ExpiryDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP)),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP)),
|
||||||
extend_existing=True
|
extend_existing=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
from .DashboardLogger import DashboardLogger
|
from .DashboardLogger import DashboardLogger
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from .WireguardConfiguration import WireguardConfiguration
|
from .WireguardConfiguration import WireguardConfiguration
|
||||||
@@ -31,7 +31,7 @@ class Assignment:
|
|||||||
class DashboardClientsPeerAssignment:
|
class DashboardClientsPeerAssignment:
|
||||||
def __init__(self, wireguardConfigurations: dict[str, WireguardConfiguration]):
|
def __init__(self, wireguardConfigurations: dict[str, WireguardConfiguration]):
|
||||||
self.logger = DashboardLogger()
|
self.logger = DashboardLogger()
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.wireguardConfigurations = wireguardConfigurations
|
self.wireguardConfigurations = wireguardConfigurations
|
||||||
self.dashboardClientsPeerAssignmentTable = db.Table(
|
self.dashboardClientsPeerAssignmentTable = db.Table(
|
||||||
@@ -41,10 +41,10 @@ class DashboardClientsPeerAssignment:
|
|||||||
db.Column('ConfigurationName', db.String(255)),
|
db.Column('ConfigurationName', db.String(255)),
|
||||||
db.Column('PeerID', db.String(500)),
|
db.Column('PeerID', db.String(500)),
|
||||||
db.Column('AssignedDate',
|
db.Column('AssignedDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP),
|
||||||
server_default=db.func.now()),
|
server_default=db.func.now()),
|
||||||
db.Column('UnassignedDate',
|
db.Column('UnassignedDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP)),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP)),
|
||||||
extend_existing=True
|
extend_existing=True
|
||||||
)
|
)
|
||||||
self.metadata.create_all(self.engine)
|
self.metadata.create_all(self.engine)
|
||||||
|
@@ -3,19 +3,19 @@ import hashlib
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
|
|
||||||
|
|
||||||
class DashboardClientsTOTP:
|
class DashboardClientsTOTP:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.dashboardClientsTOTPTable = db.Table(
|
self.dashboardClientsTOTPTable = db.Table(
|
||||||
'DashboardClientsTOTPTokens', self.metadata,
|
'DashboardClientsTOTPTokens', self.metadata,
|
||||||
db.Column("Token", db.String(500), primary_key=True, index=True),
|
db.Column("Token", db.String(500), primary_key=True, index=True),
|
||||||
db.Column("ClientID", db.String(500), index=True),
|
db.Column("ClientID", db.String(500), index=True),
|
||||||
db.Column(
|
db.Column(
|
||||||
"ExpireTime", (db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP)
|
"ExpireTime", (db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.metadata.create_all(self.engine)
|
self.metadata.create_all(self.engine)
|
||||||
|
@@ -7,7 +7,7 @@ import sqlalchemy as db
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
from .Utilities import (
|
from .Utilities import (
|
||||||
GetRemoteEndpoint, ValidateDNSAddress
|
GetRemoteEndpoint, ValidateDNSAddress
|
||||||
)
|
)
|
||||||
@@ -47,8 +47,7 @@ class DashboardConfig:
|
|||||||
"dashboard_sort": "status",
|
"dashboard_sort": "status",
|
||||||
"dashboard_theme": "dark",
|
"dashboard_theme": "dark",
|
||||||
"dashboard_api_key": "false",
|
"dashboard_api_key": "false",
|
||||||
"dashboard_language": "en-US",
|
"dashboard_language": "en-US"
|
||||||
"log_level": "INFO"
|
|
||||||
},
|
},
|
||||||
"Peers": {
|
"Peers": {
|
||||||
"peer_global_DNS": "1.1.1.1",
|
"peer_global_DNS": "1.1.1.1",
|
||||||
@@ -66,8 +65,7 @@ class DashboardConfig:
|
|||||||
"host": "",
|
"host": "",
|
||||||
"port": "",
|
"port": "",
|
||||||
"username": "",
|
"username": "",
|
||||||
"password": "",
|
"password": ""
|
||||||
"prefix": ""
|
|
||||||
},
|
},
|
||||||
"Email":{
|
"Email":{
|
||||||
"server": "",
|
"server": "",
|
||||||
@@ -97,14 +95,28 @@ class DashboardConfig:
|
|||||||
if not exist:
|
if not exist:
|
||||||
self.SetConfig(section, key, value, True)
|
self.SetConfig(section, key, value, True)
|
||||||
|
|
||||||
self.SetConfig("Server", "version", DashboardConfig.DashboardVersion)
|
self.engine = db.create_engine(ConnectionString('wgdashboard'))
|
||||||
self.SaveConfig()
|
|
||||||
|
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
|
||||||
self.dbMetadata = db.MetaData()
|
self.dbMetadata = db.MetaData()
|
||||||
self.__createAPIKeyTable()
|
self.__createAPIKeyTable()
|
||||||
self.DashboardAPIKeys = self.__getAPIKeys()
|
self.DashboardAPIKeys = self.__getAPIKeys()
|
||||||
self.APIAccessed = False
|
self.APIAccessed = False
|
||||||
|
self.SetConfig("Server", "version", DashboardConfig.DashboardVersion)
|
||||||
|
|
||||||
|
def getConnectionString(self, database) -> str or None:
|
||||||
|
sqlitePath = os.path.join(DashboardConfig.ConfigurationPath, "db")
|
||||||
|
|
||||||
|
if not os.path.isdir(sqlitePath):
|
||||||
|
os.mkdir(sqlitePath)
|
||||||
|
|
||||||
|
if self.GetConfig("Database", "type")[1] == "postgresql":
|
||||||
|
cn = f'postgresql+psycopg2://{self.GetConfig("Database", "username")[1]}:{self.GetConfig("Database", "password")[1]}@{self.GetConfig("Database", "host")[1]}/{database}'
|
||||||
|
elif self.GetConfig("Database", "type")[1] == "mysql":
|
||||||
|
cn = f'mysql+mysqldb://{self.GetConfig("Database", "username")[1]}:{self.GetConfig("Database", "password")[1]}@{self.GetConfig("Database", "host")[1]}/{database}'
|
||||||
|
else:
|
||||||
|
cn = f'sqlite:///{os.path.join(sqlitePath, f"{database}.db")}'
|
||||||
|
if not database_exists(cn):
|
||||||
|
create_database(cn)
|
||||||
|
return cn
|
||||||
|
|
||||||
def __createAPIKeyTable(self):
|
def __createAPIKeyTable(self):
|
||||||
self.apiKeyTable = db.Table('DashboardAPIKeys', self.dbMetadata,
|
self.apiKeyTable = db.Table('DashboardAPIKeys', self.dbMetadata,
|
||||||
|
@@ -4,18 +4,18 @@ Dashboard Logger Class
|
|||||||
import uuid
|
import uuid
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from .ConnectionString import ConnectionString, default_db, default_log_db
|
from .ConnectionString import ConnectionString
|
||||||
|
|
||||||
|
|
||||||
class DashboardLogger:
|
class DashboardLogger:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.engine = db.create_engine(ConnectionString(default_log_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard_log"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.dashboardLoggerTable = db.Table('DashboardLog', self.metadata,
|
self.dashboardLoggerTable = db.Table('DashboardLog', self.metadata,
|
||||||
|
|
||||||
db.Column('LogID', db.String(255), nullable=False, primary_key=True),
|
db.Column('LogID', db.String(255), nullable=False, primary_key=True),
|
||||||
db.Column('LogDate',
|
db.Column('LogDate',
|
||||||
(db.DATETIME if 'sqlite:///' in ConnectionString(default_db) else db.TIMESTAMP),
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP),
|
||||||
server_default=db.func.now()),
|
server_default=db.func.now()),
|
||||||
db.Column('URL', db.String(255)),
|
db.Column('URL', db.String(255)),
|
||||||
db.Column('IP', db.String(255)),
|
db.Column('IP', db.String(255)),
|
||||||
|
@@ -8,7 +8,7 @@ from datetime import datetime, timedelta
|
|||||||
import requests
|
import requests
|
||||||
from pydantic import BaseModel, field_serializer
|
from pydantic import BaseModel, field_serializer
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
WebHookActions = ['peer_created', 'peer_deleted', 'peer_updated']
|
WebHookActions = ['peer_created', 'peer_deleted', 'peer_updated']
|
||||||
@@ -40,7 +40,7 @@ class WebHookSessionLogs(BaseModel):
|
|||||||
|
|
||||||
class DashboardWebHooks:
|
class DashboardWebHooks:
|
||||||
def __init__(self, DashboardConfig):
|
def __init__(self, DashboardConfig):
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.webHooksTable = db.Table(
|
self.webHooksTable = db.Table(
|
||||||
'DashboardWebHooks', self.metadata,
|
'DashboardWebHooks', self.metadata,
|
||||||
@@ -201,7 +201,7 @@ class DashboardWebHooks:
|
|||||||
|
|
||||||
class WebHookSession:
|
class WebHookSession:
|
||||||
def __init__(self, webHook: WebHook, data: dict[str, str]):
|
def __init__(self, webHook: WebHook, data: dict[str, str]):
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.webHookSessionsTable = db.Table('DashboardWebHookSessions', self.metadata, autoload_with=self.engine)
|
self.webHookSessionsTable = db.Table('DashboardWebHookSessions', self.metadata, autoload_with=self.engine)
|
||||||
self.webHook = webHook
|
self.webHook = webHook
|
||||||
|
@@ -2,7 +2,7 @@ import uuid
|
|||||||
|
|
||||||
from pydantic import BaseModel, field_serializer
|
from pydantic import BaseModel, field_serializer
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
|
|
||||||
|
|
||||||
class NewConfigurationTemplate(BaseModel):
|
class NewConfigurationTemplate(BaseModel):
|
||||||
@@ -14,7 +14,7 @@ class NewConfigurationTemplate(BaseModel):
|
|||||||
|
|
||||||
class NewConfigurationTemplates:
|
class NewConfigurationTemplates:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.templatesTable = db.Table(
|
self.templatesTable = db.Table(
|
||||||
'NewConfigurationTemplates', self.metadata,
|
'NewConfigurationTemplates', self.metadata,
|
||||||
|
@@ -4,12 +4,12 @@ Peer Job Logger
|
|||||||
import uuid
|
import uuid
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from .ConnectionString import ConnectionString, default_log_db
|
from .ConnectionString import ConnectionString
|
||||||
from .Log import Log
|
from .Log import Log
|
||||||
|
|
||||||
class PeerJobLogger:
|
class PeerJobLogger:
|
||||||
def __init__(self, AllPeerJobs, DashboardConfig):
|
def __init__(self, AllPeerJobs, DashboardConfig):
|
||||||
self.engine = db.create_engine(ConnectionString(default_log_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard_log"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.jobLogTable = db.Table('JobLog', self.metadata,
|
self.jobLogTable = db.Table('JobLog', self.metadata,
|
||||||
db.Column('LogID', db.String(255), nullable=False, primary_key=True),
|
db.Column('LogID', db.String(255), nullable=False, primary_key=True),
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Peer Jobs
|
Peer Jobs
|
||||||
"""
|
"""
|
||||||
from .ConnectionString import ConnectionString, default_job_db
|
from .ConnectionString import ConnectionString
|
||||||
from .PeerJob import PeerJob
|
from .PeerJob import PeerJob
|
||||||
from .PeerJobLogger import PeerJobLogger
|
from .PeerJobLogger import PeerJobLogger
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
@@ -11,7 +11,7 @@ from flask import current_app
|
|||||||
class PeerJobs:
|
class PeerJobs:
|
||||||
def __init__(self, DashboardConfig, WireguardConfigurations):
|
def __init__(self, DashboardConfig, WireguardConfigurations):
|
||||||
self.Jobs: list[PeerJob] = []
|
self.Jobs: list[PeerJob] = []
|
||||||
self.engine = db.create_engine(ConnectionString(default_job_db))
|
self.engine = db.create_engine(ConnectionString('wgdashboard_job'))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.peerJobTable = db.Table('PeerJobs', self.metadata,
|
self.peerJobTable = db.Table('PeerJobs', self.metadata,
|
||||||
db.Column('JobID', db.String(255), nullable=False, primary_key=True),
|
db.Column('JobID', db.String(255), nullable=False, primary_key=True),
|
||||||
@@ -141,7 +141,7 @@ class PeerJobs:
|
|||||||
|
|
||||||
|
|
||||||
def runJob(self):
|
def runJob(self):
|
||||||
current_app.logger.debug("Running scheduled jobs")
|
current_app.logger.info("Running scheduled jobs")
|
||||||
needToDelete = []
|
needToDelete = []
|
||||||
self.__getJobs()
|
self.__getJobs()
|
||||||
for job in self.Jobs:
|
for job in self.Jobs:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
from .PeerShareLink import PeerShareLink
|
from .PeerShareLink import PeerShareLink
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -10,7 +10,7 @@ Peer Share Links
|
|||||||
class PeerShareLinks:
|
class PeerShareLinks:
|
||||||
def __init__(self, DashboardConfig, WireguardConfigurations):
|
def __init__(self, DashboardConfig, WireguardConfigurations):
|
||||||
self.Links: list[PeerShareLink] = []
|
self.Links: list[PeerShareLink] = []
|
||||||
self.engine = db.create_engine(ConnectionString(default_db))
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata = db.MetaData()
|
self.metadata = db.MetaData()
|
||||||
self.peerShareLinksTable = db.Table(
|
self.peerShareLinksTable = db.Table(
|
||||||
'PeerShareLinks', self.metadata,
|
'PeerShareLinks', self.metadata,
|
||||||
|
@@ -10,7 +10,7 @@ from datetime import datetime, timedelta
|
|||||||
from itertools import islice
|
from itertools import islice
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from .ConnectionString import ConnectionString, default_db
|
from .ConnectionString import ConnectionString
|
||||||
from .DashboardConfig import DashboardConfig
|
from .DashboardConfig import DashboardConfig
|
||||||
from .Peer import Peer
|
from .Peer import Peer
|
||||||
from .PeerJobs import PeerJobs
|
from .PeerJobs import PeerJobs
|
||||||
@@ -64,7 +64,7 @@ class WireguardConfiguration:
|
|||||||
self.AllPeerShareLinks = AllPeerShareLinks
|
self.AllPeerShareLinks = AllPeerShareLinks
|
||||||
self.DashboardWebHooks = DashboardWebHooks
|
self.DashboardWebHooks = DashboardWebHooks
|
||||||
self.configPath = os.path.join(self.__getProtocolPath(), f'{self.Name}.conf')
|
self.configPath = os.path.join(self.__getProtocolPath(), f'{self.Name}.conf')
|
||||||
self.engine: sqlalchemy.Engine = sqlalchemy.create_engine(ConnectionString(default_db))
|
self.engine: sqlalchemy.Engine = sqlalchemy.create_engine(ConnectionString("wgdashboard"))
|
||||||
self.metadata: sqlalchemy.MetaData = sqlalchemy.MetaData()
|
self.metadata: sqlalchemy.MetaData = sqlalchemy.MetaData()
|
||||||
self.dbType = self.DashboardConfig.GetConfig("Database", "type")[1]
|
self.dbType = self.DashboardConfig.GetConfig("Database", "type")[1]
|
||||||
|
|
||||||
@@ -396,7 +396,7 @@ class WireguardConfiguration:
|
|||||||
|
|
||||||
def getPeers(self):
|
def getPeers(self):
|
||||||
tmpList = []
|
tmpList = []
|
||||||
current_app.logger.debug(f"Refreshing {self.Name} peer list")
|
current_app.logger.info(f"Refreshing {self.Name} peer list")
|
||||||
|
|
||||||
if self.configurationFileChanged():
|
if self.configurationFileChanged():
|
||||||
with open(self.configPath, 'r') as configFile:
|
with open(self.configPath, 'r') as configFile:
|
||||||
@@ -405,7 +405,7 @@ class WireguardConfiguration:
|
|||||||
content = configFile.read().split('\n')
|
content = configFile.read().split('\n')
|
||||||
try:
|
try:
|
||||||
if "[Peer]" not in content:
|
if "[Peer]" not in content:
|
||||||
current_app.logger.debug(f"{self.Name} config has no [Peer] section")
|
current_app.logger.info(f"{self.Name} config has no [Peer] section")
|
||||||
return
|
return
|
||||||
|
|
||||||
peerStarts = content.index("[Peer]")
|
peerStarts = content.index("[Peer]")
|
||||||
|
474
src/static/app/package-lock.json
generated
474
src/static/app/package-lock.json
generated
@@ -23,14 +23,14 @@
|
|||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"is-cidr": "^5.0.3",
|
"is-cidr": "^5.0.3",
|
||||||
"npm": "^11.6.0",
|
"npm": "^10.5.0",
|
||||||
"ol": "^10.2.1",
|
"ol": "^10.2.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.5.0",
|
"pinia-plugin-persistedstate": "^4.5.0",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qrcodejs": "^1.0.0",
|
"qrcodejs": "^1.0.0",
|
||||||
"simple-code-editor": "^2.0.9",
|
"simple-code-editor": "^2.0.9",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^11.1.0",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.17",
|
||||||
"vue-chartjs": "^5.3.0",
|
"vue-chartjs": "^5.3.0",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.2.5"
|
||||||
@@ -2578,15 +2578,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cidr-regex": {
|
"node_modules/cidr-regex": {
|
||||||
"version": "5.0.0",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-5.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/cidr-regex/-/cidr-regex-4.1.3.tgz",
|
||||||
"integrity": "sha512-9FT511D25oLAQYkfKLqWUMzoitgITToOqNThDAM8ujXaeXDulDPffJQflag918J8DN8mUPXRpS9J3U5GlIHGSQ==",
|
"integrity": "sha512-86M1y3ZeQvpZkZejQCcS+IaSWjlDUC+ORP0peScQ4uEUFCZ8bEQVz7NlJHqysoUb6w3zCjx4Mq/8/2RHhMwHYw==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ip-regex": "^5.0.0"
|
"ip-regex": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cidr-tools": {
|
"node_modules/cidr-tools": {
|
||||||
@@ -4045,7 +4045,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/ip-regex": {
|
"node_modules/ip-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/ip-regex/-/ip-regex-5.0.0.tgz",
|
||||||
"integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==",
|
"integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4068,15 +4068,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-cidr": {
|
"node_modules/is-cidr": {
|
||||||
"version": "6.0.0",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-6.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/is-cidr/-/is-cidr-5.1.1.tgz",
|
||||||
"integrity": "sha512-LM62mX6QmYvLL7c0AZ2rnqGUAHcgkNwre56e8rrAdRLjUmwqrOrqGj6E/iVSrL7xxZfGQUR0gBVx9pW5CLIbig==",
|
"integrity": "sha512-AwzRMjtJNTPOgm7xuYZ71715z99t+4yRnSnSzgK5err5+heYi4zMuvmpUadaJ28+KCXCQo8CjUrKQZRWSPmqTQ==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cidr-regex": "^5.0.0"
|
"cidr-regex": "^4.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-expression": {
|
"node_modules/is-expression": {
|
||||||
@@ -4706,9 +4706,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm": {
|
"node_modules/npm": {
|
||||||
"version": "11.6.0",
|
"version": "10.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/npm/-/npm-11.6.0.tgz",
|
"resolved": "https://registry.npmmirror.com/npm/-/npm-10.9.3.tgz",
|
||||||
"integrity": "sha512-d/P7DbvYgYNde9Ehfeq99+13/E7E82PfZPw8uYZASr9sQ3ZhBBCA9cXSJRA1COfJ6jDLJ0K36UJnXQWhCvLXuQ==",
|
"integrity": "sha512-6Eh1u5Q+kIVXeA8e7l2c/HpnFFcwrkt37xDMujD5be1gloWa9p6j3Fsv3mByXXmqJHy+2cElRMML8opNT7xIJQ==",
|
||||||
"bundleDependencies": [
|
"bundleDependencies": [
|
||||||
"@isaacs/string-locale-compare",
|
"@isaacs/string-locale-compare",
|
||||||
"@npmcli/arborist",
|
"@npmcli/arborist",
|
||||||
@@ -4739,6 +4739,7 @@
|
|||||||
"libnpmdiff",
|
"libnpmdiff",
|
||||||
"libnpmexec",
|
"libnpmexec",
|
||||||
"libnpmfund",
|
"libnpmfund",
|
||||||
|
"libnpmhook",
|
||||||
"libnpmorg",
|
"libnpmorg",
|
||||||
"libnpmpack",
|
"libnpmpack",
|
||||||
"libnpmpublish",
|
"libnpmpublish",
|
||||||
@@ -4775,7 +4776,8 @@
|
|||||||
"tiny-relative-date",
|
"tiny-relative-date",
|
||||||
"treeverse",
|
"treeverse",
|
||||||
"validate-npm-package-name",
|
"validate-npm-package-name",
|
||||||
"which"
|
"which",
|
||||||
|
"write-file-atomic"
|
||||||
],
|
],
|
||||||
"license": "Artistic-2.0",
|
"license": "Artistic-2.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
@@ -4787,8 +4789,8 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/string-locale-compare": "^1.1.0",
|
"@isaacs/string-locale-compare": "^1.1.0",
|
||||||
"@npmcli/arborist": "^9.1.4",
|
"@npmcli/arborist": "^8.0.1",
|
||||||
"@npmcli/config": "^10.4.0",
|
"@npmcli/config": "^9.0.0",
|
||||||
"@npmcli/fs": "^4.0.0",
|
"@npmcli/fs": "^4.0.0",
|
||||||
"@npmcli/map-workspaces": "^4.0.2",
|
"@npmcli/map-workspaces": "^4.0.2",
|
||||||
"@npmcli/package-json": "^6.2.0",
|
"@npmcli/package-json": "^6.2.0",
|
||||||
@@ -4800,7 +4802,7 @@
|
|||||||
"archy": "~1.0.0",
|
"archy": "~1.0.0",
|
||||||
"cacache": "^19.0.1",
|
"cacache": "^19.0.1",
|
||||||
"chalk": "^5.4.1",
|
"chalk": "^5.4.1",
|
||||||
"ci-info": "^4.3.0",
|
"ci-info": "^4.2.0",
|
||||||
"cli-columns": "^4.0.0",
|
"cli-columns": "^4.0.0",
|
||||||
"fastest-levenshtein": "^1.0.16",
|
"fastest-levenshtein": "^1.0.16",
|
||||||
"fs-minipass": "^3.0.3",
|
"fs-minipass": "^3.0.3",
|
||||||
@@ -4808,19 +4810,20 @@
|
|||||||
"graceful-fs": "^4.2.11",
|
"graceful-fs": "^4.2.11",
|
||||||
"hosted-git-info": "^8.1.0",
|
"hosted-git-info": "^8.1.0",
|
||||||
"ini": "^5.0.0",
|
"ini": "^5.0.0",
|
||||||
"init-package-json": "^8.2.1",
|
"init-package-json": "^7.0.2",
|
||||||
"is-cidr": "^5.1.1",
|
"is-cidr": "^5.1.1",
|
||||||
"json-parse-even-better-errors": "^4.0.0",
|
"json-parse-even-better-errors": "^4.0.0",
|
||||||
"libnpmaccess": "^10.0.1",
|
"libnpmaccess": "^9.0.0",
|
||||||
"libnpmdiff": "^8.0.7",
|
"libnpmdiff": "^7.0.1",
|
||||||
"libnpmexec": "^10.1.6",
|
"libnpmexec": "^9.0.1",
|
||||||
"libnpmfund": "^7.0.7",
|
"libnpmfund": "^6.0.1",
|
||||||
"libnpmorg": "^8.0.0",
|
"libnpmhook": "^11.0.0",
|
||||||
"libnpmpack": "^9.0.7",
|
"libnpmorg": "^7.0.0",
|
||||||
"libnpmpublish": "^11.1.0",
|
"libnpmpack": "^8.0.1",
|
||||||
"libnpmsearch": "^9.0.0",
|
"libnpmpublish": "^10.0.1",
|
||||||
"libnpmteam": "^8.0.1",
|
"libnpmsearch": "^8.0.0",
|
||||||
"libnpmversion": "^8.0.1",
|
"libnpmteam": "^7.0.0",
|
||||||
|
"libnpmversion": "^7.0.0",
|
||||||
"make-fetch-happen": "^14.0.3",
|
"make-fetch-happen": "^14.0.3",
|
||||||
"minimatch": "^9.0.5",
|
"minimatch": "^9.0.5",
|
||||||
"minipass": "^7.1.1",
|
"minipass": "^7.1.1",
|
||||||
@@ -4828,7 +4831,7 @@
|
|||||||
"ms": "^2.1.2",
|
"ms": "^2.1.2",
|
||||||
"node-gyp": "^11.2.0",
|
"node-gyp": "^11.2.0",
|
||||||
"nopt": "^8.1.0",
|
"nopt": "^8.1.0",
|
||||||
"normalize-package-data": "^7.0.1",
|
"normalize-package-data": "^7.0.0",
|
||||||
"npm-audit-report": "^6.0.0",
|
"npm-audit-report": "^6.0.0",
|
||||||
"npm-install-checks": "^7.1.1",
|
"npm-install-checks": "^7.1.1",
|
||||||
"npm-package-arg": "^12.0.2",
|
"npm-package-arg": "^12.0.2",
|
||||||
@@ -4837,7 +4840,7 @@
|
|||||||
"npm-registry-fetch": "^18.0.2",
|
"npm-registry-fetch": "^18.0.2",
|
||||||
"npm-user-validate": "^3.0.0",
|
"npm-user-validate": "^3.0.0",
|
||||||
"p-map": "^7.0.3",
|
"p-map": "^7.0.3",
|
||||||
"pacote": "^21.0.0",
|
"pacote": "^19.0.1",
|
||||||
"parse-conflict-json": "^4.0.0",
|
"parse-conflict-json": "^4.0.0",
|
||||||
"proc-log": "^5.0.0",
|
"proc-log": "^5.0.0",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
@@ -4845,20 +4848,21 @@
|
|||||||
"semver": "^7.7.2",
|
"semver": "^7.7.2",
|
||||||
"spdx-expression-parse": "^4.0.0",
|
"spdx-expression-parse": "^4.0.0",
|
||||||
"ssri": "^12.0.0",
|
"ssri": "^12.0.0",
|
||||||
"supports-color": "^10.0.0",
|
"supports-color": "^9.4.0",
|
||||||
"tar": "^6.2.1",
|
"tar": "^6.2.1",
|
||||||
"text-table": "~0.2.0",
|
"text-table": "~0.2.0",
|
||||||
"tiny-relative-date": "^1.3.0",
|
"tiny-relative-date": "^1.3.0",
|
||||||
"treeverse": "^3.0.0",
|
"treeverse": "^3.0.0",
|
||||||
"validate-npm-package-name": "^6.0.2",
|
"validate-npm-package-name": "^6.0.1",
|
||||||
"which": "^5.0.0"
|
"which": "^5.0.0",
|
||||||
|
"write-file-atomic": "^6.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"npm": "bin/npm-cli.js",
|
"npm": "bin/npm-cli.js",
|
||||||
"npx": "bin/npx-cli.js"
|
"npx": "bin/npx-cli.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@isaacs/cliui": {
|
"node_modules/npm/node_modules/@isaacs/cliui": {
|
||||||
@@ -4955,7 +4959,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@npmcli/arborist": {
|
"node_modules/npm/node_modules/@npmcli/arborist": {
|
||||||
"version": "9.1.4",
|
"version": "8.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4963,7 +4967,7 @@
|
|||||||
"@npmcli/fs": "^4.0.0",
|
"@npmcli/fs": "^4.0.0",
|
||||||
"@npmcli/installed-package-contents": "^3.0.0",
|
"@npmcli/installed-package-contents": "^3.0.0",
|
||||||
"@npmcli/map-workspaces": "^4.0.1",
|
"@npmcli/map-workspaces": "^4.0.1",
|
||||||
"@npmcli/metavuln-calculator": "^9.0.0",
|
"@npmcli/metavuln-calculator": "^8.0.0",
|
||||||
"@npmcli/name-from-folder": "^3.0.0",
|
"@npmcli/name-from-folder": "^3.0.0",
|
||||||
"@npmcli/node-gyp": "^4.0.0",
|
"@npmcli/node-gyp": "^4.0.0",
|
||||||
"@npmcli/package-json": "^6.0.1",
|
"@npmcli/package-json": "^6.0.1",
|
||||||
@@ -4974,6 +4978,7 @@
|
|||||||
"cacache": "^19.0.1",
|
"cacache": "^19.0.1",
|
||||||
"common-ancestor-path": "^1.0.1",
|
"common-ancestor-path": "^1.0.1",
|
||||||
"hosted-git-info": "^8.0.0",
|
"hosted-git-info": "^8.0.0",
|
||||||
|
"json-parse-even-better-errors": "^4.0.0",
|
||||||
"json-stringify-nice": "^1.1.4",
|
"json-stringify-nice": "^1.1.4",
|
||||||
"lru-cache": "^10.2.2",
|
"lru-cache": "^10.2.2",
|
||||||
"minimatch": "^9.0.4",
|
"minimatch": "^9.0.4",
|
||||||
@@ -4982,7 +4987,7 @@
|
|||||||
"npm-package-arg": "^12.0.0",
|
"npm-package-arg": "^12.0.0",
|
||||||
"npm-pick-manifest": "^10.0.0",
|
"npm-pick-manifest": "^10.0.0",
|
||||||
"npm-registry-fetch": "^18.0.1",
|
"npm-registry-fetch": "^18.0.1",
|
||||||
"pacote": "^21.0.0",
|
"pacote": "^19.0.0",
|
||||||
"parse-conflict-json": "^4.0.0",
|
"parse-conflict-json": "^4.0.0",
|
||||||
"proc-log": "^5.0.0",
|
"proc-log": "^5.0.0",
|
||||||
"proggy": "^3.0.0",
|
"proggy": "^3.0.0",
|
||||||
@@ -4992,17 +4997,17 @@
|
|||||||
"semver": "^7.3.7",
|
"semver": "^7.3.7",
|
||||||
"ssri": "^12.0.0",
|
"ssri": "^12.0.0",
|
||||||
"treeverse": "^3.0.0",
|
"treeverse": "^3.0.0",
|
||||||
"walk-up-path": "^4.0.0"
|
"walk-up-path": "^3.0.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"arborist": "bin/index.js"
|
"arborist": "bin/index.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@npmcli/config": {
|
"node_modules/npm/node_modules/@npmcli/config": {
|
||||||
"version": "10.4.0",
|
"version": "9.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5010,13 +5015,13 @@
|
|||||||
"@npmcli/package-json": "^6.0.1",
|
"@npmcli/package-json": "^6.0.1",
|
||||||
"ci-info": "^4.0.0",
|
"ci-info": "^4.0.0",
|
||||||
"ini": "^5.0.0",
|
"ini": "^5.0.0",
|
||||||
"nopt": "^8.1.0",
|
"nopt": "^8.0.0",
|
||||||
"proc-log": "^5.0.0",
|
"proc-log": "^5.0.0",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"walk-up-path": "^4.0.0"
|
"walk-up-path": "^3.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@npmcli/fs": {
|
"node_modules/npm/node_modules/@npmcli/fs": {
|
||||||
@@ -5078,18 +5083,48 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@npmcli/metavuln-calculator": {
|
"node_modules/npm/node_modules/@npmcli/metavuln-calculator": {
|
||||||
"version": "9.0.1",
|
"version": "8.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cacache": "^19.0.0",
|
"cacache": "^19.0.0",
|
||||||
"json-parse-even-better-errors": "^4.0.0",
|
"json-parse-even-better-errors": "^4.0.0",
|
||||||
"pacote": "^21.0.0",
|
"pacote": "^20.0.0",
|
||||||
"proc-log": "^5.0.0",
|
"proc-log": "^5.0.0",
|
||||||
"semver": "^7.3.5"
|
"semver": "^7.3.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": {
|
||||||
|
"version": "20.0.0",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@npmcli/git": "^6.0.0",
|
||||||
|
"@npmcli/installed-package-contents": "^3.0.0",
|
||||||
|
"@npmcli/package-json": "^6.0.0",
|
||||||
|
"@npmcli/promise-spawn": "^8.0.0",
|
||||||
|
"@npmcli/run-script": "^9.0.0",
|
||||||
|
"cacache": "^19.0.0",
|
||||||
|
"fs-minipass": "^3.0.0",
|
||||||
|
"minipass": "^7.0.2",
|
||||||
|
"npm-package-arg": "^12.0.0",
|
||||||
|
"npm-packlist": "^9.0.0",
|
||||||
|
"npm-pick-manifest": "^10.0.0",
|
||||||
|
"npm-registry-fetch": "^18.0.0",
|
||||||
|
"proc-log": "^5.0.0",
|
||||||
|
"promise-retry": "^2.0.1",
|
||||||
|
"sigstore": "^3.0.0",
|
||||||
|
"ssri": "^12.0.0",
|
||||||
|
"tar": "^6.1.11"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"pacote": "bin/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@npmcli/name-from-folder": {
|
"node_modules/npm/node_modules/@npmcli/name-from-folder": {
|
||||||
@@ -5180,25 +5215,6 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@sigstore/bundle": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@sigstore/protobuf-specs": "^0.4.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.17.0 || >=20.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/@sigstore/core": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.17.0 || >=20.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/@sigstore/protobuf-specs": {
|
"node_modules/npm/node_modules/@sigstore/protobuf-specs": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -5207,22 +5223,6 @@
|
|||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@sigstore/sign": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@sigstore/bundle": "^3.1.0",
|
|
||||||
"@sigstore/core": "^2.0.0",
|
|
||||||
"@sigstore/protobuf-specs": "^0.4.0",
|
|
||||||
"make-fetch-happen": "^14.0.2",
|
|
||||||
"proc-log": "^5.0.0",
|
|
||||||
"promise-retry": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.17.0 || >=20.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/@sigstore/tuf": {
|
"node_modules/npm/node_modules/@sigstore/tuf": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -5235,19 +5235,6 @@
|
|||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@sigstore/verify": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@sigstore/bundle": "^3.1.0",
|
|
||||||
"@sigstore/core": "^2.0.0",
|
|
||||||
"@sigstore/protobuf-specs": "^0.4.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.17.0 || >=20.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/@tufjs/canonical-json": {
|
"node_modules/npm/node_modules/@tufjs/canonical-json": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -5256,18 +5243,6 @@
|
|||||||
"node": "^16.14.0 || >=18.0.0"
|
"node": "^16.14.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/@tufjs/models": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@tufjs/canonical-json": "2.0.0",
|
|
||||||
"minimatch": "^9.0.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.17.0 || >=20.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/abbrev": {
|
"node_modules/npm/node_modules/abbrev": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -5277,7 +5252,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/agent-base": {
|
"node_modules/npm/node_modules/agent-base": {
|
||||||
"version": "7.1.4",
|
"version": "7.1.3",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -5304,7 +5279,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/aproba": {
|
"node_modules/npm/node_modules/aproba": {
|
||||||
"version": "2.1.0",
|
"version": "2.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@@ -5334,11 +5309,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/binary-extensions": {
|
"node_modules/npm/node_modules/binary-extensions": {
|
||||||
"version": "3.1.0",
|
"version": "2.3.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.20"
|
"node": ">=8"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
@@ -5382,17 +5357,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/cacache/node_modules/minizlib": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"minipass": "^7.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/cacache/node_modules/mkdirp": {
|
"node_modules/npm/node_modules/cacache/node_modules/mkdirp": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -5451,7 +5415,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/ci-info": {
|
"node_modules/npm/node_modules/ci-info": {
|
||||||
"version": "4.3.0",
|
"version": "4.2.0",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -5571,7 +5535,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/diff": {
|
"node_modules/npm/node_modules/diff": {
|
||||||
"version": "7.0.0",
|
"version": "5.2.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -5753,11 +5717,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/init-package-json": {
|
"node_modules/npm/node_modules/init-package-json": {
|
||||||
"version": "8.2.1",
|
"version": "7.0.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/package-json": "^6.1.0",
|
"@npmcli/package-json": "^6.0.0",
|
||||||
"npm-package-arg": "^12.0.0",
|
"npm-package-arg": "^12.0.0",
|
||||||
"promzard": "^2.0.0",
|
"promzard": "^2.0.0",
|
||||||
"read": "^4.0.0",
|
"read": "^4.0.0",
|
||||||
@@ -5766,7 +5730,7 @@
|
|||||||
"validate-npm-package-name": "^6.0.0"
|
"validate-npm-package-name": "^6.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/ip-address": {
|
"node_modules/npm/node_modules/ip-address": {
|
||||||
@@ -5870,7 +5834,7 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmaccess": {
|
"node_modules/npm/node_modules/libnpmaccess": {
|
||||||
"version": "10.0.1",
|
"version": "9.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5878,61 +5842,60 @@
|
|||||||
"npm-registry-fetch": "^18.0.1"
|
"npm-registry-fetch": "^18.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmdiff": {
|
"node_modules/npm/node_modules/libnpmdiff": {
|
||||||
"version": "8.0.7",
|
"version": "7.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/arborist": "^9.1.4",
|
"@npmcli/arborist": "^8.0.1",
|
||||||
"@npmcli/installed-package-contents": "^3.0.0",
|
"@npmcli/installed-package-contents": "^3.0.0",
|
||||||
"binary-extensions": "^3.0.0",
|
"binary-extensions": "^2.3.0",
|
||||||
"diff": "^7.0.0",
|
"diff": "^5.1.0",
|
||||||
"minimatch": "^9.0.4",
|
"minimatch": "^9.0.4",
|
||||||
"npm-package-arg": "^12.0.0",
|
"npm-package-arg": "^12.0.0",
|
||||||
"pacote": "^21.0.0",
|
"pacote": "^19.0.0",
|
||||||
"tar": "^6.2.1"
|
"tar": "^6.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmexec": {
|
"node_modules/npm/node_modules/libnpmexec": {
|
||||||
"version": "10.1.6",
|
"version": "9.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/arborist": "^9.1.4",
|
"@npmcli/arborist": "^8.0.1",
|
||||||
"@npmcli/package-json": "^6.1.1",
|
|
||||||
"@npmcli/run-script": "^9.0.1",
|
"@npmcli/run-script": "^9.0.1",
|
||||||
"ci-info": "^4.0.0",
|
"ci-info": "^4.0.0",
|
||||||
"npm-package-arg": "^12.0.0",
|
"npm-package-arg": "^12.0.0",
|
||||||
"pacote": "^21.0.0",
|
"pacote": "^19.0.0",
|
||||||
"proc-log": "^5.0.0",
|
"proc-log": "^5.0.0",
|
||||||
"read": "^4.0.0",
|
"read": "^4.0.0",
|
||||||
"read-package-json-fast": "^4.0.0",
|
"read-package-json-fast": "^4.0.0",
|
||||||
"semver": "^7.3.7",
|
"semver": "^7.3.7",
|
||||||
"walk-up-path": "^4.0.0"
|
"walk-up-path": "^3.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmfund": {
|
"node_modules/npm/node_modules/libnpmfund": {
|
||||||
"version": "7.0.7",
|
"version": "6.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/arborist": "^9.1.4"
|
"@npmcli/arborist": "^8.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmorg": {
|
"node_modules/npm/node_modules/libnpmhook": {
|
||||||
"version": "8.0.0",
|
"version": "11.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5940,30 +5903,42 @@
|
|||||||
"npm-registry-fetch": "^18.0.1"
|
"npm-registry-fetch": "^18.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm/node_modules/libnpmorg": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^2.0.0",
|
||||||
|
"npm-registry-fetch": "^18.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmpack": {
|
"node_modules/npm/node_modules/libnpmpack": {
|
||||||
"version": "9.0.7",
|
"version": "8.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/arborist": "^9.1.4",
|
"@npmcli/arborist": "^8.0.1",
|
||||||
"@npmcli/run-script": "^9.0.1",
|
"@npmcli/run-script": "^9.0.1",
|
||||||
"npm-package-arg": "^12.0.0",
|
"npm-package-arg": "^12.0.0",
|
||||||
"pacote": "^21.0.0"
|
"pacote": "^19.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmpublish": {
|
"node_modules/npm/node_modules/libnpmpublish": {
|
||||||
"version": "11.1.0",
|
"version": "10.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@npmcli/package-json": "^6.2.0",
|
|
||||||
"ci-info": "^4.0.0",
|
"ci-info": "^4.0.0",
|
||||||
|
"normalize-package-data": "^7.0.0",
|
||||||
"npm-package-arg": "^12.0.0",
|
"npm-package-arg": "^12.0.0",
|
||||||
"npm-registry-fetch": "^18.0.1",
|
"npm-registry-fetch": "^18.0.1",
|
||||||
"proc-log": "^5.0.0",
|
"proc-log": "^5.0.0",
|
||||||
@@ -5972,22 +5947,22 @@
|
|||||||
"ssri": "^12.0.0"
|
"ssri": "^12.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmsearch": {
|
"node_modules/npm/node_modules/libnpmsearch": {
|
||||||
"version": "9.0.0",
|
"version": "8.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"npm-registry-fetch": "^18.0.1"
|
"npm-registry-fetch": "^18.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmteam": {
|
"node_modules/npm/node_modules/libnpmteam": {
|
||||||
"version": "8.0.1",
|
"version": "7.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5995,11 +5970,11 @@
|
|||||||
"npm-registry-fetch": "^18.0.1"
|
"npm-registry-fetch": "^18.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/libnpmversion": {
|
"node_modules/npm/node_modules/libnpmversion": {
|
||||||
"version": "8.0.1",
|
"version": "7.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6010,7 +5985,7 @@
|
|||||||
"semver": "^7.3.7"
|
"semver": "^7.3.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/lru-cache": {
|
"node_modules/npm/node_modules/lru-cache": {
|
||||||
@@ -6096,17 +6071,6 @@
|
|||||||
"encoding": "^0.1.13"
|
"encoding": "^0.1.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"minipass": "^7.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/minipass-flush": {
|
"node_modules/npm/node_modules/minipass-flush": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -6174,26 +6138,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/minizlib": {
|
"node_modules/npm/node_modules/minizlib": {
|
||||||
"version": "2.1.2",
|
"version": "3.0.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minipass": "^3.0.0",
|
"minipass": "^7.1.2"
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 18"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/minizlib/node_modules/minipass": {
|
|
||||||
"version": "3.3.6",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/mkdirp": {
|
"node_modules/npm/node_modules/mkdirp": {
|
||||||
@@ -6251,17 +6203,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/node-gyp/node_modules/minizlib": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"minipass": "^7.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": {
|
"node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -6315,7 +6256,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/normalize-package-data": {
|
"node_modules/npm/node_modules/normalize-package-data": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6380,14 +6321,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/npm-packlist": {
|
"node_modules/npm/node_modules/npm-packlist": {
|
||||||
"version": "10.0.0",
|
"version": "9.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ignore-walk": "^7.0.0"
|
"ignore-walk": "^7.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/npm-pick-manifest": {
|
"node_modules/npm/node_modules/npm-pick-manifest": {
|
||||||
@@ -6434,17 +6375,6 @@
|
|||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": {
|
|
||||||
"version": "3.0.2",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"minipass": "^7.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/npm/node_modules/npm-user-validate": {
|
"node_modules/npm/node_modules/npm-user-validate": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -6470,7 +6400,7 @@
|
|||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/pacote": {
|
"node_modules/npm/node_modules/pacote": {
|
||||||
"version": "21.0.0",
|
"version": "19.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6483,7 +6413,7 @@
|
|||||||
"fs-minipass": "^3.0.0",
|
"fs-minipass": "^3.0.0",
|
||||||
"minipass": "^7.0.2",
|
"minipass": "^7.0.2",
|
||||||
"npm-package-arg": "^12.0.0",
|
"npm-package-arg": "^12.0.0",
|
||||||
"npm-packlist": "^10.0.0",
|
"npm-packlist": "^9.0.0",
|
||||||
"npm-pick-manifest": "^10.0.0",
|
"npm-pick-manifest": "^10.0.0",
|
||||||
"npm-registry-fetch": "^18.0.0",
|
"npm-registry-fetch": "^18.0.0",
|
||||||
"proc-log": "^5.0.0",
|
"proc-log": "^5.0.0",
|
||||||
@@ -6496,7 +6426,7 @@
|
|||||||
"pacote": "bin/index.js"
|
"pacote": "bin/index.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/parse-conflict-json": {
|
"node_modules/npm/node_modules/parse-conflict-json": {
|
||||||
@@ -6711,6 +6641,54 @@
|
|||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@sigstore/protobuf-specs": "^0.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@sigstore/bundle": "^3.1.0",
|
||||||
|
"@sigstore/core": "^2.0.0",
|
||||||
|
"@sigstore/protobuf-specs": "^0.4.0",
|
||||||
|
"make-fetch-happen": "^14.0.2",
|
||||||
|
"proc-log": "^5.0.0",
|
||||||
|
"promise-retry": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@sigstore/bundle": "^3.1.0",
|
||||||
|
"@sigstore/core": "^2.0.0",
|
||||||
|
"@sigstore/protobuf-specs": "^0.4.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/npm/node_modules/smart-buffer": {
|
"node_modules/npm/node_modules/smart-buffer": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -6721,7 +6699,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/socks": {
|
"node_modules/npm/node_modules/socks": {
|
||||||
"version": "2.8.6",
|
"version": "2.8.5",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6850,11 +6828,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/supports-color": {
|
"node_modules/npm/node_modules/supports-color": {
|
||||||
"version": "10.0.0",
|
"version": "9.4.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
@@ -6906,6 +6884,29 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/npm/node_modules/tar/node_modules/minizlib": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm/node_modules/tar/node_modules/minizlib/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/npm/node_modules/text-table": {
|
"node_modules/npm/node_modules/text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -6945,7 +6946,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": {
|
"node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -6964,13 +6965,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/tuf-js": {
|
"node_modules/npm/node_modules/tuf-js": {
|
||||||
"version": "3.1.0",
|
"version": "3.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tufjs/models": "3.0.1",
|
"@tufjs/models": "3.0.1",
|
||||||
"debug": "^4.4.1",
|
"debug": "^4.3.6",
|
||||||
"make-fetch-happen": "^14.0.3"
|
"make-fetch-happen": "^14.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tufjs/canonical-json": "2.0.0",
|
||||||
|
"minimatch": "^9.0.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
@@ -7022,7 +7035,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/validate-npm-package-name": {
|
"node_modules/npm/node_modules/validate-npm-package-name": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -7030,12 +7043,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/walk-up-path": {
|
"node_modules/npm/node_modules/walk-up-path": {
|
||||||
"version": "4.0.0",
|
"version": "3.0.1",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC"
|
||||||
"engines": {
|
|
||||||
"node": "20 || >=22"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/npm/node_modules/which": {
|
"node_modules/npm/node_modules/which": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
@@ -8492,16 +8502,16 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "13.0.0",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
|
||||||
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
|
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://github.com/sponsors/broofa",
|
"https://github.com/sponsors/broofa",
|
||||||
"https://github.com/sponsors/ctavan"
|
"https://github.com/sponsors/ctavan"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"uuid": "dist-node/bin/uuid"
|
"uuid": "dist/esm/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/verror": {
|
"node_modules/verror": {
|
||||||
|
@@ -26,15 +26,15 @@
|
|||||||
"electron-builder": "^26.0.12",
|
"electron-builder": "^26.0.12",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"is-cidr": "^6.0.0",
|
"is-cidr": "^5.0.3",
|
||||||
"npm": "^11.6.0",
|
"npm": "^10.5.0",
|
||||||
"ol": "^10.2.1",
|
"ol": "^10.2.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.5.0",
|
"pinia-plugin-persistedstate": "^4.5.0",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qrcodejs": "^1.0.0",
|
"qrcodejs": "^1.0.0",
|
||||||
"simple-code-editor": "^2.0.9",
|
"simple-code-editor": "^2.0.9",
|
||||||
"uuid": "^13.0.0",
|
"uuid": "^11.1.0",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.17",
|
||||||
"vue-chartjs": "^5.3.0",
|
"vue-chartjs": "^5.3.0",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.2.5"
|
||||||
|
@@ -114,7 +114,7 @@ const data = computed(() => {
|
|||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress" role="progressbar" style="height: 6px">
|
<div class="progress" role="progressbar" style="height: 6px">
|
||||||
<div class="progress-bar bg-warning" :style="{width: `${data?.Memory.SwapMemory.percent}%` }"></div>
|
<div class="progress-bar bg-warning" :style="{width: `$ data?.Memory.SwapMemory.percent}%` }"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
43
src/wgd.sh
43
src/wgd.sh
@@ -464,6 +464,47 @@ stop_wgd() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ============= Docker Functions =============
|
||||||
|
startwgd_docker() {
|
||||||
|
_checkWireguard
|
||||||
|
printf "[WGDashboard][Docker] WireGuard configuration started\n"
|
||||||
|
{ date; start_core ; printf "\n\n"; } >> ./log/install.txt
|
||||||
|
gunicorn_start
|
||||||
|
}
|
||||||
|
|
||||||
|
start_core() {
|
||||||
|
# Re-assign config_files to ensure it includes any newly created configurations
|
||||||
|
local config_files=$(find /etc/wireguard -type f -name "*.conf")
|
||||||
|
|
||||||
|
# Set file permissions
|
||||||
|
find /etc/wireguard -type f -name "*.conf" -exec chmod 600 {} \;
|
||||||
|
find "$iptable_dir" -type f -name "*.sh" -exec chmod +x {} \;
|
||||||
|
|
||||||
|
# Start WireGuard for each config file
|
||||||
|
for file in $config_files; do
|
||||||
|
config_name=$(basename "$file" ".conf")
|
||||||
|
wg-quick up "$config_name"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
newconf_wgd() {
|
||||||
|
local wg_port_listen=$wg_port
|
||||||
|
local wg_addr_range=$wg_net
|
||||||
|
private_key=$(wg genkey)
|
||||||
|
public_key=$(echo "$private_key" | wg pubkey)
|
||||||
|
cat <<EOF >"/etc/wireguard/wg0.conf"
|
||||||
|
[Interface]
|
||||||
|
PrivateKey = $private_key
|
||||||
|
Address = $wg_addr_range
|
||||||
|
ListenPort = $wg_port_listen
|
||||||
|
SaveConfig = true
|
||||||
|
PostUp = /opt/wireguarddashboard/src/iptable-rules/postup.sh
|
||||||
|
PreDown = /opt/wireguarddashboard/src/iptable-rules/postdown.sh
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============= Docker Functions =============
|
||||||
|
|
||||||
start_wgd_debug() {
|
start_wgd_debug() {
|
||||||
_checkWireguard
|
_checkWireguard
|
||||||
printf "[WGDashboard] Starting WGDashboard in the foreground.\n"
|
printf "[WGDashboard] Starting WGDashboard in the foreground.\n"
|
||||||
@@ -559,4 +600,4 @@ else
|
|||||||
help
|
help
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
printf "%s\n" "$dashes"
|
printf "%s\n" "$dashes"
|
Reference in New Issue
Block a user