ваше сообщение коммита

This commit is contained in:
2025-10-03 18:48:11 +03:00
parent ad7b8e9716
commit 67cf473455
42 changed files with 5515 additions and 1180 deletions

36
webssh-agent/Dockerfile Normal file
View File

@@ -0,0 +1,36 @@
FROM node:20-alpine
# Устанавливаем необходимые пакеты
RUN apk add --no-cache \
openssh-client \
sshpass \
curl \
wget \
docker-cli
# Создаем рабочую директорию
WORKDIR /app
# Копируем package.json и package-lock.json
COPY package*.json ./
# Устанавливаем зависимости
RUN npm install
# Копируем исходный код
COPY . .
# Создаем SSH директорию для root с правильными правами
RUN mkdir -p /root/.ssh && \
chmod 700 /root/.ssh && \
touch /root/.ssh/config && \
chmod 600 /root/.ssh/config
# Оставляем root для доступа к Docker socket
# USER webssh
# Открываем порт
EXPOSE 3000
# Команда запуска
CMD ["npm", "start"]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,252 @@
# Copyright (c) 2024-2025 Тарабанов Александр Викторович
# All rights reserved.
# This software is proprietary and confidential.
# For licensing inquiries: info@hb3-accelerator.com
# Production docker-compose для VDS
services:
postgres:
image: postgres:16-alpine
container_name: dapp-postgres
restart: unless-stopped
networks:
- dapp_network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backend/db/data:/mnt/isic_csv_data
environment:
- POSTGRES_DB=${DB_NAME:-dapp_db}
- POSTGRES_USER=${DB_USER:-dapp_user}
- POSTGRES_PASSWORD=${DB_PASSWORD:-dapp_password}
# 🆕 SCRAM-SHA-256 для безопасной аутентификации
- POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 --auth-local=scram-sha-256
# НЕ открываем порт 5432 наружу - только внутри Docker сети
healthcheck:
test:
- CMD-SHELL
- pg_isready -U ${DB_USER:-dapp_user} -d ${DB_NAME:-dapp_db}
interval: 5s
timeout: 5s
retries: 5
ollama:
image: digital_legal_entitydle-ollama:latest
container_name: dapp-ollama
restart: unless-stopped
networks:
- dapp_network
logging:
driver: "json-file"
options:
max-size: "20m"
max-file: "3"
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
limits:
cpus: '2.0'
memory: 6G
reservations:
cpus: '1.0'
memory: 4G
environment:
- OLLAMA_HOST=0.0.0.0
- OLLAMA_ORIGINS=*
- OLLAMA_NUM_PARALLEL=2
- OLLAMA_NUM_GPU=0
- OLLAMA_KEEP_ALIVE=86400
- OLLAMA_MODEL_TIMEOUT=0
- OLLAMA_MAX_LOADED_MODELS=2
- OLLAMA_FLASH_ATTENTION=0
- OLLAMA_LLM_LIBRARY=auto
healthcheck:
test: ["CMD", "ollama", "list"]
interval: 30s
timeout: 10s
retries: 5
start_period: 120s
vector-search:
image: digital_legal_entitydle-vector-search:latest
container_name: dapp-vector-search
restart: unless-stopped
networks:
- dapp_network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
depends_on:
ollama:
condition: service_healthy
volumes:
- vector_search_data:/app/data
environment:
# 🆕 Исправленный URL для Ollama
- OLLAMA_BASE_URL=http://dapp-ollama:11434
- OLLAMA_EMBED_MODEL=${OLLAMA_EMBEDDINGS_MODEL:-mxbai-embed-large:latest}
- VECTOR_SEARCH_PORT=8001
- LOG_LEVEL=INFO
# 🆕 Улучшенный health check с fallback
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8001/health || curl -f http://localhost:8001/ || wget -q --spider http://localhost:8001/health || exit 1"]
interval: 30s
timeout: 15s
retries: 15
start_period: 180s
backend:
image: digital_legal_entitydle-backend:latest
container_name: dapp-backend
restart: unless-stopped
networks:
- dapp_network
logging:
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
# 🆕 Исправленные зависимости - backend запускается после postgres и ollama, но не ждет vector-search
depends_on:
postgres:
condition: service_healthy
ollama:
condition: service_healthy
vector-search:
condition: service_started
volumes:
- backend_node_modules:/app/node_modules
- ./ssl:/app/ssl:ro
environment:
- NODE_ENV=production
- PORT=8000
- FRONTEND_URL=https://${DOMAIN}
# 🆕 Исправленные переменные БД с правильными именами хостов
- DB_HOST=dapp-postgres
- DB_PORT=5432
- DB_NAME=${DB_NAME:-dapp_db}
- DB_USER=${DB_USER:-dapp_user}
- DB_PASSWORD=${DB_PASSWORD:-dapp_password}
# 🆕 DATABASE_URL с SCRAM-SHA-256 поддержкой
- DATABASE_URL=postgresql://${DB_USER:-dapp_user}:${DB_PASSWORD:-dapp_password}@dapp-postgres:5432/${DB_NAME:-dapp_db}?sslmode=prefer
# 🆕 Исправленный URL для Ollama
- OLLAMA_BASE_URL=http://dapp-ollama:11434
- OLLAMA_MODEL=${OLLAMA_MODEL:-qwen2.5:7b}
- OLLAMA_EMBEDDINGS_MODEL=${OLLAMA_EMBEDDINGS_MODEL:-qwen2.5:7b}
# 🆕 Исправленный URL для Vector Search
- VECTOR_SEARCH_URL=http://dapp-vector-search:8001
# НЕ открываем порт 8000 наружу - только nginx подключается
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:8000/api/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
frontend:
image: digital_legal_entitydle-frontend:latest
container_name: dapp-frontend
restart: unless-stopped
networks:
- dapp_network
logging:
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
depends_on:
backend:
condition: service_healthy
volumes:
- ./ssl:/app/ssl:ro
environment:
- NODE_ENV=production
- PORT=5173
# 🆕 Исправленный URL для backend
- VITE_BACKEND_URL=https://${DOMAIN}/api
- VITE_WS_URL=wss://${DOMAIN}/ws
# НЕ открываем порт 5173 наружу - только nginx подключается
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:5173', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
# Certbot контейнер для автоматического получения SSL сертификатов
certbot:
image: certbot/certbot
container_name: dapp-certbot
restart: "no"
volumes:
- /etc/letsencrypt:/etc/letsencrypt
- /var/www/certbot:/var/www/certbot
command: certonly --webroot --webroot-path=/var/www/certbot --email ${EMAIL} --agree-tos --no-eff-email -d ${DOMAIN}
depends_on:
- frontend-nginx
# Nginx с автоматическим SSL и поддержкой WebSocket
frontend-nginx:
image: digital_legal_entitydle-frontend-nginx:latest
container_name: dapp-frontend-nginx
restart: unless-stopped
networks:
- dapp_network
ports:
- "80:80"
- "443:443"
environment:
- DOMAIN=${DOMAIN:-localhost}
- BACKEND_CONTAINER=dapp-backend
- EMAIL=${EMAIL}
# 🆕 Переменные для WebSocket поддержки
- WS_BACKEND_CONTAINER=dapp-backend
volumes:
# SSL сертификаты Let's Encrypt (автоматически обновляются)
- /etc/letsencrypt:/etc/ssl/certs:ro
# Webroot для certbot
- /var/www/certbot:/var/www/certbot
depends_on:
- backend
- frontend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health", "||", "curl", "-f", "https://localhost:443/health", "||", "exit", "1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 30s
webssh-agent:
image: digital_legal_entitydle-webssh-agent:latest
container_name: dapp-webssh-agent
restart: unless-stopped
volumes:
- ~/.ssh:/root/.ssh:ro
- /var/run/docker.sock:/var/run/docker.sock:rw
# 🆕 Исправленный проброс портов - только локальный доступ для безопасности
ports:
- "127.0.0.1:3000:3000"
environment:
- NODE_ENV=production
depends_on:
- backend
# 🆕 Явное определение Docker сети для обеспечения связи между контейнерами
networks:
dapp_network:
driver: bridge
name: dapp_network
volumes:
postgres_data:
ollama_data:
vector_search_data:
backend_node_modules:

View File

@@ -0,0 +1,128 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Rate limiting для защиты от DDoS
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api_limit_per_ip:10m rate=5r/s;
# Блокировка известных сканеров и вредоносных ботов
map $http_user_agent $bad_bot {
default 0;
~*bot 1;
~*crawler 1;
~*spider 1;
~*scanner 1;
~*sqlmap 1;
~*nikto 1;
~*dirb 1;
~*gobuster 1;
~*wfuzz 1;
~*burp 1;
~*zap 1;
~*nessus 1;
~*openvas 1;
~*Chrome/[1-4][0-9]\. 1;
~*Firefox/[1-6][0-9]\. 1;
~*Safari/[1-9]\. 1;
~*MSIE\ [1-9]\. 1;
}
server {
listen 80;
server_name DOMAIN_PLACEHOLDER;
root /usr/share/nginx/html;
index index.html;
# Блокировка подозрительных ботов
if ($bad_bot = 1) {
return 403;
}
# Защита от path traversal
if ($request_uri ~* "(\.\.|\.\./|\.\.\\|\.\.%2f|\.\.%5c)") {
return 404;
}
# Защита от опасных расширений
location ~* \.(zip|rar|7z|tar|gz|bz2|xz|sql|sqlite|db|bak|backup|old|csv|php|asp|aspx|jsp|cgi|pl|py|sh|bash|exe|bat|cmd|com|pif|scr|vbs|vbe|jar|war|ear|dll|so|dylib|bin|sys|ini|log|tmp|temp|swp|swo|~)$ {
return 404;
}
# Защита от доступа к чувствительным файлам
location ~* /(\.htaccess|\.htpasswd|\.env|\.git|\.svn|\.DS_Store|Thumbs\.db|web\.config|robots\.txt|sitemap\.xml)$ {
deny all;
return 404;
}
# Защита от доступа к конфигурационным файлам
location ~* /\.(env|config|ini|conf|cfg|yml|yaml|json|xml|sql|db|bak|backup|old|tmp|temp|log)$ {
deny all;
return 404;
}
# Основной location
location / {
# Rate limiting для основных страниц
limit_req zone=req_limit_per_ip burst=20 nodelay;
try_files $uri $uri/ /index.html;
# Базовые заголовки безопасности
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' ws: wss:;" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
}
# Статические файлы
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary Accept-Encoding;
# Заголовки безопасности для статических файлов
add_header X-Content-Type-Options "nosniff" always;
}
# API
location /api/ {
# Rate limiting для API (более строгое)
limit_req zone=api_limit_per_ip burst=10 nodelay;
proxy_pass http://BACKEND_CONTAINER_PLACEHOLDER:8000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Заголовки безопасности для API
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
# WebSocket поддержка
location /ws {
proxy_pass http://BACKEND_CONTAINER_PLACEHOLDER:8000/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
}
# Скрытие информации о сервере
server_tokens off;
}
}

View File

@@ -22,12 +22,14 @@
"express": "^4.18.2",
"cors": "^2.8.5",
"fs-extra": "^11.1.1",
"chalk": "^4.1.2"
"chalk": "^4.1.2",
"ws": "^8.14.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"engines": {
"node": ">=16.0.0"
}
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

1
webssh-agent/ssl Symbolic link
View File

@@ -0,0 +1 @@
../ssl

View File

@@ -0,0 +1,105 @@
const { execSshCommand } = require('./sshUtils');
const log = require('./logger');
/**
* Очистка VDS сервера
*/
const cleanupVdsServer = async (options) => {
log.info('Очистка VDS сервера...');
// Остановка и удаление существующих Docker контейнеров
log.info('Остановка существующих Docker контейнеров...');
await execSshCommand('sudo docker stop $(sudo docker ps -aq) 2>/dev/null || true', options);
await execSshCommand('sudo docker rm $(sudo docker ps -aq) 2>/dev/null || true', options);
// Удаление Docker образов и очистка системы
log.info('Очистка Docker образов и системы...');
await execSshCommand('sudo docker system prune -af || true', options);
await execSshCommand('sudo docker volume prune -f || true', options);
await execSshCommand('sudo docker network prune -f || true', options);
// 🆕 Умная проверка и удаление системного nginx для избежания конфликтов портов
log.info('🔍 Проверка наличия системного nginx...');
const nginxCheck = await execSshCommand('systemctl list-units --type=service --state=active,inactive | grep nginx || echo "nginx not found"', options);
if (nginxCheck.stdout.includes('nginx.service')) {
log.info('⚠️ Обнаружен системный nginx - удаляем для освобождения портов 80/443...');
// Полная остановка и удаление системного nginx
await execSshCommand('sudo systemctl stop nginx || true', options);
await execSshCommand('sudo systemctl disable nginx || true', options);
await execSshCommand('sudo systemctl mask nginx || true', options);
await execSshCommand('sudo pkill -f nginx || true', options);
await execSshCommand('sudo apt-get purge -y nginx nginx-common nginx-full || true', options);
await execSshCommand('sudo apt-get autoremove -y || true', options);
log.success('✅ Системный nginx полностью удален, порты 80/443 освобождены для Docker nginx');
} else {
log.info(' Системный nginx не обнаружен - порты 80/443 свободны для Docker nginx');
}
// Остановка других конфликтующих сервисов
log.info('Остановка других конфликтующих сервисов...');
await execSshCommand('sudo systemctl stop apache2 2>/dev/null || true', options);
await execSshCommand('sudo systemctl disable apache2 2>/dev/null || true', options);
await execSshCommand('sudo systemctl mask apache2 2>/dev/null || true', options);
// Очистка старых пакетов
log.info('Очистка старых пакетов...');
await execSshCommand('sudo apt-get autoremove -y || true', options);
await execSshCommand('sudo apt-get autoclean || true', options);
// Очистка временных файлов
log.info('Очистка временных файлов...');
await execSshCommand('sudo rm -rf /tmp/* /var/tmp/* 2>/dev/null || true', options);
log.success('VDS сервер очищен');
};
/**
* Настройка SSH ключей для root
*/
const setupRootSshKeys = async (publicKey, options) => {
log.info('Настройка SSH ключей...');
// Создание директории .ssh для root
await execSshCommand('sudo mkdir -p /root/.ssh', options);
await execSshCommand('sudo chmod 700 /root/.ssh', options);
// Добавление публичного ключа в authorized_keys
await execSshCommand(`echo "${publicKey}" | sudo tee -a /root/.ssh/authorized_keys`, options);
await execSshCommand('sudo chmod 600 /root/.ssh/authorized_keys', options);
await execSshCommand('sudo chown root:root /root/.ssh/authorized_keys', options);
log.success('SSH ключи созданы и публичный ключ добавлен в authorized_keys');
};
/**
* Отключение парольной аутентификации
*/
const disablePasswordAuth = async (options) => {
log.info('Отключение парольной аутентификации...');
await execSshCommand('sudo sed -i "s/#PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config', options);
await execSshCommand('sudo sed -i "s/PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config', options);
await execSshCommand('sudo systemctl restart ssh', options);
log.success('Парольная аутентификация отключена, доступ только через SSH ключи');
};
/**
* Настройка firewall
*/
const setupFirewall = async (options) => {
log.info('Настройка firewall...');
await execSshCommand('sudo ufw --force enable', options);
await execSshCommand('sudo ufw allow ssh', options);
await execSshCommand('sudo ufw allow 80', options);
await execSshCommand('sudo ufw allow 443', options);
log.success('Firewall настроен');
};
module.exports = {
cleanupVdsServer,
setupRootSshKeys,
disablePasswordAuth,
setupFirewall
};

View File

@@ -0,0 +1,275 @@
const { exec } = require('child_process');
const { execSshCommand, execScpCommand } = require('./sshUtils');
const log = require('./logger');
/**
* Экспорт Docker образов и данных с локальной машины
*/
const exportDockerImages = async (sendWebSocketLog) => {
log.info('Экспорт Docker образов и данных с хоста...');
sendWebSocketLog('info', '📦 Начинаем экспорт Docker образов...', 'export_images', 60);
const images = [
{ name: 'postgres:16-alpine', file: 'dapp-postgres.tar' },
{ name: 'digital_legal_entitydle-ollama:latest', file: 'dapp-ollama.tar' },
{ name: 'digital_legal_entitydle-vector-search:latest', file: 'dapp-vector-search.tar' },
{ name: 'digital_legal_entitydle-backend:latest', file: 'dapp-backend.tar' },
{ name: 'digital_legal_entitydle-frontend:latest', file: 'dapp-frontend.tar' },
{ name: 'digital_legal_entitydle-frontend-nginx:latest', file: 'dapp-frontend-nginx.tar' },
{ name: 'digital_legal_entitydle-webssh-agent:latest', file: 'dapp-webssh-agent.tar' }
];
// Экспортируем все образы
for (let i = 0; i < images.length; i++) {
const image = images[i];
const progress = 60 + Math.floor((i / images.length) * 10); // 60-70%
sendWebSocketLog('info', `📦 Экспорт образа: ${image.name}`, 'export_images', progress);
await new Promise((resolve) => {
exec(`docker save ${image.name} -o /tmp/${image.file}`, (error, stdout, stderr) => {
if (error) {
log.error(`Ошибка экспорта ${image.name}: ${error.message}`);
sendWebSocketLog('error', `❌ Ошибка экспорта ${image.name}`, 'export_images', progress);
} else {
sendWebSocketLog('success', `✅ Экспорт ${image.name} завершен`, 'export_images', progress);
}
resolve();
});
});
}
// Экспортируем данные из volumes
log.info('Экспорт данных из Docker volumes...');
sendWebSocketLog('info', '📦 Экспорт данных из Docker volumes...', 'export_data', 70);
// PostgreSQL данные - проверяем наличие данных перед экспортом
sendWebSocketLog('info', '📦 Экспорт данных PostgreSQL...', 'export_data', 72);
await new Promise((resolve) => {
// Сначала проверим, есть ли данные в volume
exec('docker run --rm -v digital_legal_entitydle_postgres_data:/data alpine ls -la /data/base', (checkError, checkStdout, checkStderr) => {
if (checkError) {
log.error(`Ошибка проверки данных PostgreSQL: ${checkError.message}`);
sendWebSocketLog('error', `❌ Ошибка проверки данных PostgreSQL`, 'export_data', 72);
resolve();
return;
}
// Если данные есть, экспортируем их
exec('docker run --rm -v digital_legal_entitydle_postgres_data:/data -v /tmp:/backup alpine tar czf /backup/postgres_data.tar.gz -C /data .', (error, stdout, stderr) => {
if (error) {
log.error(`Ошибка экспорта данных PostgreSQL: ${error.message}`);
sendWebSocketLog('error', `❌ Ошибка экспорта данных PostgreSQL`, 'export_data', 72);
} else {
log.info(`Данные PostgreSQL экспортированы: ${checkStdout.trim()}`);
sendWebSocketLog('success', `✅ Экспорт данных PostgreSQL завершен (содержит базы данных)`, 'export_data', 72);
}
resolve();
});
});
});
// Ollama данные
sendWebSocketLog('info', '📦 Экспорт данных Ollama...', 'export_data', 75);
await new Promise((resolve) => {
exec('docker run --rm -v digital_legal_entitydle_ollama_data:/data -v /tmp:/backup alpine tar czf /backup/ollama_data.tar.gz -C /data .', (error, stdout, stderr) => {
if (error) {
log.error(`Ошибка экспорта данных Ollama: ${error.message}`);
sendWebSocketLog('error', `❌ Ошибка экспорта данных Ollama`, 'export_data', 75);
} else {
sendWebSocketLog('success', `✅ Экспорт данных Ollama завершен`, 'export_data', 75);
}
resolve();
});
});
// Vector Search данные
sendWebSocketLog('info', '📦 Экспорт данных Vector Search...', 'export_data', 78);
await new Promise((resolve) => {
exec('docker run --rm -v digital_legal_entitydle_vector_search_data:/data -v /tmp:/backup alpine tar czf /backup/vector_search_data.tar.gz -C /data .', (error, stdout, stderr) => {
if (error) {
log.error(`Ошибка экспорта данных Vector Search: ${error.message}`);
sendWebSocketLog('error', `❌ Ошибка экспорта данных Vector Search`, 'export_data', 78);
} else {
sendWebSocketLog('success', `✅ Экспорт данных Vector Search завершен`, 'export_data', 78);
}
resolve();
});
});
// Проверяем размеры экспортированных данных
log.info('Проверка размеров экспортированных данных...');
sendWebSocketLog('info', '📊 Проверка размеров экспортированных данных...', 'export_data', 78);
await new Promise((resolve) => {
exec('ls -lh /tmp/postgres_data.tar.gz /tmp/ollama_data.tar.gz /tmp/vector_search_data.tar.gz 2>/dev/null || echo "Некоторые файлы не найдены"', (error, stdout, stderr) => {
if (stdout && stdout.trim()) {
log.info(`Размеры экспортированных данных:\n${stdout.trim()}`);
sendWebSocketLog('info', `📊 Размеры данных:\n${stdout.trim()}`, 'export_data', 78);
}
resolve();
});
});
// Создаем архив с ВСЕМИ образами и данными приложения
log.info('Создание архива Docker образов и данных на хосте...');
sendWebSocketLog('info', '📦 Создание архива всех данных...', 'export_data', 80);
const tarFiles = images.map(img => img.file).join(' ');
const dataFiles = 'postgres_data.tar.gz ollama_data.tar.gz vector_search_data.tar.gz';
await new Promise((resolve) => {
exec(`chmod 644 /tmp/dapp-*.tar /tmp/*_data.tar.gz && cd /tmp && tar -czf docker-images-and-data.tar.gz ${tarFiles} ${dataFiles}`, (error, stdout, stderr) => {
if (error) {
log.error('Ошибка создания архива: ' + error.message);
sendWebSocketLog('error', '❌ Ошибка создания архива', 'export_data', 80);
} else {
// Проверяем размер финального архива
exec('ls -lh /tmp/docker-images-and-data.tar.gz', (sizeError, sizeStdout, sizeStderr) => {
if (sizeStdout && sizeStdout.trim()) {
log.info(`Финальный архив создан: ${sizeStdout.trim()}`);
sendWebSocketLog('success', `✅ Архив создан успешно (${sizeStdout.trim()})`, 'export_data', 80);
} else {
sendWebSocketLog('success', '✅ Архив создан успешно', 'export_data', 80);
}
resolve();
});
}
});
});
log.success('Docker образы и данные успешно экспортированы');
sendWebSocketLog('success', '✅ Экспорт данных завершен', 'export_data', 80);
};
/**
* Передача Docker образов и данных на VDS
*/
const transferDockerImages = async (options, sendWebSocketLog) => {
const { dockerUser } = options;
log.info('Передача Docker образов и данных на VDS...');
sendWebSocketLog('info', '📤 Передача архива на VDS сервер...', 'transfer', 82);
// Передаем архив образов и данных на VDS через SCP
await execScpCommand(
'/tmp/docker-images-and-data.tar.gz',
`/home/${dockerUser}/dapp/docker-images-and-data.tar.gz`,
options
);
sendWebSocketLog('success', '✅ Архив успешно передан на VDS', 'transfer', 85);
log.success('Docker образы и данные успешно переданы на VDS');
};
/**
* Импорт Docker образов и данных на VDS
*/
const importDockerImages = async (options, sendWebSocketLog) => {
const { dockerUser } = options;
// Создаем скрипт импорта на VDS
sendWebSocketLog('info', '📥 Начинаем импорт данных на VDS...', 'import', 85);
const importScript = `#!/bin/bash
set -e
echo "🚀 Импорт Docker образов и данных на VDS..."
# Проверяем наличие архива
if [ ! -f "./docker-images-and-data.tar.gz" ]; then
echo "❌ Файл docker-images-and-data.tar.gz не найден!"
exit 1
fi
# Создаем директорию для распаковки
mkdir -p ./temp-import
# Распаковываем архив
echo "📦 Распаковка архива..."
tar -xzf ./docker-images-and-data.tar.gz -C ./temp-import
# Импортируем ВСЕ образы приложения
echo "📦 Импорт образа postgres..."
docker load -i ./temp-import/dapp-postgres.tar
echo "📦 Импорт образа ollama..."
docker load -i ./temp-import/dapp-ollama.tar
echo "📦 Импорт образа vector-search..."
docker load -i ./temp-import/dapp-vector-search.tar
echo "📦 Импорт образа backend..."
docker load -i ./temp-import/dapp-backend.tar
echo "📦 Импорт образа frontend..."
docker load -i ./temp-import/dapp-frontend.tar
echo "📦 Импорт образа frontend-nginx..."
docker load -i ./temp-import/dapp-frontend-nginx.tar
echo "📦 Импорт образа webssh-agent..."
docker load -i ./temp-import/dapp-webssh-agent.tar
# 🆕 Импортируем данные в volumes с правильными именами для соответствия docker-compose
echo "📦 Импорт данных PostgreSQL..."
# Удаляем старый volume если существует
docker volume rm dapp_postgres_data 2>/dev/null || true
docker volume create dapp_postgres_data
docker run --rm -v dapp_postgres_data:/data -v ./temp-import:/backup alpine tar xzf /backup/postgres_data.tar.gz -C /data
echo "📦 Импорт данных Ollama..."
# Удаляем старый volume если существует
docker volume rm dapp_ollama_data 2>/dev/null || true
docker volume create dapp_ollama_data
docker run --rm -v dapp_ollama_data:/data -v ./temp-import:/backup alpine tar xzf /backup/ollama_data.tar.gz -C /data
echo "📦 Импорт данных Vector Search..."
# Удаляем старый volume если существует
docker volume rm dapp_vector_search_data 2>/dev/null || true
docker volume create dapp_vector_search_data
docker run --rm -v dapp_vector_search_data:/data -v ./temp-import:/backup alpine tar xzf /backup/vector_search_data.tar.gz -C /data
# Очищаем временные файлы
rm -rf ./temp-import
echo "✅ Образы и данные успешно импортированы!"
echo "📋 Доступные образы:"
docker images | grep -E "digital_legal_entitydle|postgres"
echo "📋 Доступные volumes:"
docker volume ls | grep dapp_`;
await execSshCommand(`echo '${importScript}' | sudo tee /home/${dockerUser}/dapp/import-images-and-data.sh`, options);
await execSshCommand(`sudo chmod +x /home/${dockerUser}/dapp/import-images-and-data.sh`, options);
// Импортируем образы и данные
log.info('Импорт Docker образов и данных...');
sendWebSocketLog('info', '📥 Импорт Docker образов на VDS...', 'import', 87);
await execSshCommand(`cd /home/${dockerUser}/dapp && ./import-images-and-data.sh`, options);
sendWebSocketLog('success', '✅ Импорт Docker образов завершен', 'import', 90);
sendWebSocketLog('info', '📥 Импорт данных в volumes...', 'import', 92);
// Логи от самого скрипта импорта будут видны
sendWebSocketLog('success', '✅ Импорт данных завершен', 'import', 95);
log.success('Docker образы и данные успешно импортированы на VDS');
};
/**
* Очистка временных файлов на локальной машине
*/
const cleanupLocalFiles = async () => {
log.info('Очистка временных файлов на хосте...');
await new Promise((resolve) => {
exec('rm -f /tmp/dapp-*.tar /tmp/*_data.tar.gz /tmp/docker-images-and-data.tar.gz', (error, stdout, stderr) => {
if (error) log.error('Ошибка очистки файлов: ' + error.message);
resolve();
});
});
log.success('Временные файлы очищены (SSH ключи сохранены на хосте)');
};
module.exports = {
exportDockerImages,
transferDockerImages,
importDockerImages,
cleanupLocalFiles
};

View File

@@ -0,0 +1,61 @@
const { exec } = require('child_process');
const log = require('./logger');
/**
* Выполнение команд локально (на хосте)
*/
const execLocalCommand = async (command) => {
return new Promise((resolve) => {
exec(command, (error, stdout, stderr) => {
resolve({
code: error ? error.code : 0,
stdout: stdout || '',
stderr: stderr || ''
});
});
});
};
/**
* Создание SSH ключей локально на хосте
*/
const createSshKeys = async (email) => {
log.info('Создание SSH ключей на хосте...');
return new Promise((resolve) => {
// Сначала исправляем права доступа к SSH конфигу
exec('chmod 600 /root/.ssh/config 2>/dev/null || true', (configError) => {
if (configError) {
log.warn('Не удалось исправить права доступа к SSH конфигу: ' + configError.message);
}
// Создаем SSH ключи
exec(`ssh-keygen -t rsa -b 4096 -C "${email}" -f ~/.ssh/id_rsa -N ""`, (error, stdout, stderr) => {
if (error) {
log.error('Ошибка создания SSH ключей: ' + error.message);
} else {
log.success('SSH ключи успешно созданы на хосте');
// Устанавливаем правильные права доступа к созданным ключам
exec('chmod 600 /root/.ssh/id_rsa && chmod 644 /root/.ssh/id_rsa.pub', (permError) => {
if (permError) {
log.warn('Не удалось установить права доступа к SSH ключам: ' + permError.message);
} else {
log.success('Права доступа к SSH ключам установлены');
}
resolve();
});
}
if (error) {
resolve();
}
});
});
});
};
module.exports = {
execLocalCommand,
createSshKeys
};

View File

@@ -0,0 +1,10 @@
const chalk = require('chalk');
const log = {
info: (message) => console.log(chalk.blue('[INFO]'), message),
success: (message) => console.log(chalk.green('[SUCCESS]'), message),
error: (message) => console.log(chalk.red('[ERROR]'), message),
warn: (message) => console.log(chalk.yellow('[WARN]'), message)
};
module.exports = log;

View File

@@ -0,0 +1,127 @@
const { exec } = require('child_process');
const log = require('./logger');
/**
* Исправление прав доступа к SSH конфигурации
*/
const fixSshPermissions = async () => {
return new Promise((resolve) => {
// Исправляем владельца и права доступа к SSH конфигу
exec('chown root:root /root/.ssh/config 2>/dev/null || true && chmod 600 /root/.ssh/config 2>/dev/null || true', (error) => {
if (error) {
log.warn('Не удалось исправить права доступа к SSH конфигу: ' + error.message);
} else {
log.info('Права доступа к SSH конфигу исправлены');
}
resolve();
});
});
};
/**
* Выполнение SSH команд с поддержкой ключей и пароля
*/
const execSshCommand = async (command, options = {}) => {
const {
sshHost,
sshPort = 22,
sshConnectUser,
sshConnectPassword,
vdsIp
} = options;
// Исправляем права доступа к SSH конфигу перед выполнением команды
await fixSshPermissions();
// Сначала пробуем подключиться с SSH ключами (без BatchMode для возможности fallback на пароль)
let sshCommand = `ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sshConnectUser}@${sshHost || vdsIp} "${command.replace(/"/g, '\\"')}"`;
log.info(`🔍 Выполняем SSH команду: ${sshCommand}`);
return new Promise((resolve) => {
exec(sshCommand, (error, stdout, stderr) => {
log.info(`📤 SSH результат - код: ${error ? error.code : 0}, stdout: "${stdout}", stderr: "${stderr}"`);
if (error && error.code === 255 && sshConnectPassword) {
// Если подключение с ключами не удалось, пробуем с паролем
log.info('SSH ключи не сработали, пробуем с паролем...');
const passwordCommand = `sshpass -p "${sshConnectPassword}" ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sshConnectUser}@${sshHost || vdsIp} "${command.replace(/"/g, '\\"')}"`;
log.info(`🔍 Выполняем SSH команду с паролем: ${passwordCommand}`);
exec(passwordCommand, (passwordError, passwordStdout, passwordStderr) => {
log.info(`📤 SSH с паролем результат - код: ${passwordError ? passwordError.code : 0}, stdout: "${passwordStdout}", stderr: "${passwordStderr}"`);
resolve({
code: passwordError ? passwordError.code : 0,
stdout: passwordStdout || '',
stderr: passwordStderr || ''
});
});
} else {
resolve({
code: error ? error.code : 0,
stdout: stdout || '',
stderr: stderr || ''
});
}
});
});
};
/**
* Выполнение SCP команд с поддержкой ключей и пароля
*/
const execScpCommand = async (sourcePath, targetPath, options = {}) => {
const {
sshHost,
sshPort = 22,
sshConnectUser,
sshConnectPassword,
vdsIp
} = options;
// Исправляем права доступа к SSH конфигу перед выполнением команды
await fixSshPermissions();
const scpCommand = `scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sourcePath} ${sshConnectUser}@${sshHost || vdsIp}:${targetPath}`;
return new Promise((resolve) => {
exec(scpCommand, (error, stdout, stderr) => {
if (error && error.code === 255 && sshConnectPassword) {
// Если SCP с ключами не удался, пробуем с паролем
log.info('SCP с ключами не сработал, пробуем с паролем...');
const passwordScpCommand = `sshpass -p "${sshConnectPassword}" scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sourcePath} ${sshConnectUser}@${sshHost || vdsIp}:${targetPath}`;
exec(passwordScpCommand, (passwordError, passwordStdout, passwordStderr) => {
if (passwordError) {
log.error('❌ Ошибка SCP: ' + passwordError.message);
} else {
log.success('✅ SCP успешно выполнен');
}
resolve({
code: passwordError ? passwordError.code : 0,
stdout: passwordStdout || '',
stderr: passwordStderr || ''
});
});
} else {
if (error) {
log.error('❌ Ошибка SCP: ' + error.message);
} else {
log.success('✅ SCP успешно выполнен');
}
resolve({
code: error ? error.code : 0,
stdout: stdout || '',
stderr: stderr || ''
});
}
});
});
};
module.exports = {
execSshCommand,
execScpCommand,
fixSshPermissions
};

View File

@@ -0,0 +1,140 @@
const { execSshCommand } = require('./sshUtils');
const log = require('./logger');
// Системные требования
const SYSTEM_REQUIREMENTS = {
minMemoryGB: 6, // Минимум 6GB RAM (Ollama требует 4GB + система)
minDiskGB: 30, // Минимум 30GB свободного места (AI модели + Docker образы)
minCpuCores: 2, // Минимум 2 CPU ядра
recommendedMemoryGB: 8, // Рекомендуется 8GB RAM (для комфортной работы)
recommendedDiskGB: 50 // Рекомендуется 50GB свободного места
};
/**
* Проверка системных требований VDS
*/
const checkSystemRequirements = async (options) => {
log.info('🔍 Проверка системных требований VDS...');
try {
// Проверка памяти
log.info('📊 Проверка памяти...');
const memoryResult = await execSshCommand('free -h | grep "Mem:" | awk \'{print $2}\'', options);
const memoryStr = memoryResult.stdout.trim().replace('G', '').replace('Gi', '');
const memoryGB = parseFloat(memoryStr);
// Проверка диска
log.info('💾 Проверка диска...');
const diskResult = await execSshCommand('df -h / | tail -1 | awk \'{print $4}\'', options);
const diskStr = diskResult.stdout.trim().replace('G', '').replace('Gi', '');
const diskGB = parseFloat(diskStr);
// Проверка CPU
log.info('⚡ Проверка CPU...');
const cpuResult = await execSshCommand('nproc', options);
const cpuCores = parseInt(cpuResult.stdout.trim());
// Проверка архитектуры
log.info('🏗️ Проверка архитектуры...');
const archResult = await execSshCommand('uname -m', options);
const architecture = archResult.stdout.trim();
// Дополнительная диагностика архитектуры
const archInfoResult = await execSshCommand('uname -a', options);
log.info(`Архитектура (uname -m): "${architecture}"`);
log.info(`Полная информация (uname -a): "${archInfoResult.stdout.trim()}"`);
// Проверка версии Ubuntu
log.info('🐧 Проверка версии Ubuntu...');
const ubuntuResult = await execSshCommand('lsb_release -d | cut -f2', options);
const ubuntuVersion = ubuntuResult.stdout.trim();
const systemInfo = {
memoryGB: memoryGB,
diskGB: diskGB,
cpuCores: cpuCores,
architecture: architecture,
ubuntuVersion: ubuntuVersion
};
log.info(`📋 Системная информация:`);
log.info(` 💾 Память: ${memoryGB}GB (минимум: ${SYSTEM_REQUIREMENTS.minMemoryGB}GB, рекомендуется: ${SYSTEM_REQUIREMENTS.recommendedMemoryGB}GB)`);
log.info(` 💿 Диск: ${diskGB}GB свободно (минимум: ${SYSTEM_REQUIREMENTS.minDiskGB}GB, рекомендуется: ${SYSTEM_REQUIREMENTS.recommendedDiskGB}GB)`);
log.info(` ⚡ CPU: ${cpuCores} ядер (минимум: ${SYSTEM_REQUIREMENTS.minCpuCores})`);
log.info(` 🏗️ Архитектура: ${architecture}`);
log.info(` 🐧 Ubuntu: ${ubuntuVersion}`);
// Валидация требований
const warnings = [];
const errors = [];
if (memoryGB < SYSTEM_REQUIREMENTS.minMemoryGB) {
errors.push(`Недостаточно памяти: ${memoryGB}GB (требуется минимум ${SYSTEM_REQUIREMENTS.minMemoryGB}GB)`);
} else if (memoryGB < SYSTEM_REQUIREMENTS.recommendedMemoryGB) {
warnings.push(`Мало памяти: ${memoryGB}GB (рекомендуется ${SYSTEM_REQUIREMENTS.recommendedMemoryGB}GB)`);
}
if (diskGB < SYSTEM_REQUIREMENTS.minDiskGB) {
errors.push(`Недостаточно места на диске: ${diskGB}GB (требуется минимум ${SYSTEM_REQUIREMENTS.minDiskGB}GB)`);
} else if (diskGB < SYSTEM_REQUIREMENTS.recommendedDiskGB) {
warnings.push(`Мало места на диске: ${diskGB}GB (рекомендуется ${SYSTEM_REQUIREMENTS.recommendedDiskGB}GB)`);
}
if (cpuCores < SYSTEM_REQUIREMENTS.minCpuCores) {
errors.push(`Недостаточно CPU ядер: ${cpuCores} (требуется минимум ${SYSTEM_REQUIREMENTS.minCpuCores})`);
}
// Проверка архитектуры (поддерживаем различные архитектуры)
const supportedArchitectures = [
'x86_64', 'amd64', 'x64', // Intel/AMD 64-bit
'aarch64', 'arm64', // ARM 64-bit
'armv7l', 'armv8l', // ARM 32/64-bit
'i386', 'i686', // Intel 32-bit
'ppc64le', 's390x' // PowerPC, IBM Z
];
const isSupportedArch = supportedArchitectures.some(arch =>
architecture.toLowerCase().includes(arch.toLowerCase())
);
if (!isSupportedArch) {
// Вместо ошибки делаем предупреждение для неизвестных архитектур
warnings.push(`Неизвестная архитектура: ${architecture} (поддерживаются: x86_64, amd64, aarch64, arm64, armv7l, armv8l, i386, i686, ppc64le, s390x)`);
log.warn(`⚠️ Архитектура ${architecture} не в списке поддерживаемых, но попробуем продолжить...`);
}
// Вывод результатов
if (warnings.length > 0) {
log.warn('⚠️ Предупреждения:');
warnings.forEach(warning => log.warn(` ${warning}`));
}
if (errors.length > 0) {
log.error('❌ Критические ошибки:');
errors.forEach(error => log.error(` ${error}`));
throw new Error(`VDS не соответствует минимальным требованиям: ${errors.join(', ')}`);
}
if (warnings.length === 0 && errors.length === 0) {
log.success('✅ Все системные требования выполнены!');
} else {
log.warn('⚠️ Система соответствует минимальным требованиям, но рекомендуется улучшить конфигурацию');
}
return {
systemInfo,
warnings,
errors,
isCompatible: errors.length === 0,
hasWarnings: warnings.length > 0
};
} catch (error) {
log.error(`Ошибка при проверке системных требований: ${error.message}`);
throw error;
}
};
module.exports = {
checkSystemRequirements,
SYSTEM_REQUIREMENTS
};

View File

@@ -0,0 +1,47 @@
const { execSshCommand } = require('./sshUtils');
const log = require('./logger');
/**
* Создание пользователя с SSH ключами
*/
const createUserWithSshKeys = async (username, publicKey, options) => {
log.info(`Создание пользователя ${username}...`);
// Создание пользователя
await execSshCommand(`sudo useradd -m -s /bin/bash ${username} || true`, options);
await execSshCommand(`sudo usermod -aG sudo ${username}`, options);
// Настройка SSH ключей для пользователя
await execSshCommand(`sudo mkdir -p /home/${username}/.ssh`, options);
await execSshCommand(`echo "${publicKey}" | sudo tee /home/${username}/.ssh/authorized_keys`, options);
await execSshCommand(`sudo chown -R ${username}:${username} /home/${username}/.ssh`, options);
await execSshCommand(`sudo chmod 700 /home/${username}/.ssh`, options);
await execSshCommand(`sudo chmod 600 /home/${username}/.ssh/authorized_keys`, options);
log.success(`Пользователь ${username} создан с SSH ключами`);
};
/**
* Создание всех необходимых пользователей
*/
const createAllUsers = async (ubuntuUser, dockerUser, publicKey, options) => {
// Создание пользователя Ubuntu
await createUserWithSshKeys(ubuntuUser, publicKey, options);
// Создание пользователя Docker
await createUserWithSshKeys(dockerUser, publicKey, options);
// Добавление пользователя Docker в группу docker
await execSshCommand(`sudo usermod -aG docker ${dockerUser}`, options);
// Создание директории для приложения
await execSshCommand(`sudo mkdir -p /home/${dockerUser}/dapp`, options);
await execSshCommand(`sudo chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp`, options);
log.success('Все пользователи созданы');
};
module.exports = {
createUserWithSshKeys,
createAllUsers
};

779
webssh-agent/yarn.lock Normal file
View File

@@ -0,0 +1,779 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
binary-extensions@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
body-parser@1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
brace-expansion@^1.1.7:
version "1.1.12"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.1.1"
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
call-bound@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
dependencies:
call-bind-apply-helpers "^1.0.2"
get-intrinsic "^1.3.0"
chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chokidar@^3.5.2:
version "3.6.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^4:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
encodeurl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
dependencies:
es-errors "^1.3.0"
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^4.18.2:
version "4.21.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32"
integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.7.1"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.3.1"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.12"
proxy-addr "~2.0.7"
qs "6.13.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.19.0"
serve-static "1.16.2"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
finalhandler@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
dependencies:
debug "2.6.9"
encodeurl "~2.0.0"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
fs-extra@^11.1.1:
version "11.3.2"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.2.tgz#c838aeddc6f4a8c74dd15f85e11fe5511bfe02a4"
integrity sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
dependencies:
call-bind-apply-helpers "^1.0.2"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.1.1"
function-bind "^1.1.2"
get-proto "^1.0.1"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
ignore-by-default@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
jsonfile@^6.0.1:
version "6.2.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62"
integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
merge-descriptors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.3, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
nodemon@^3.0.1:
version "3.1.10"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.10.tgz#5015c5eb4fffcb24d98cf9454df14f4fecec9bc1"
integrity sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==
dependencies:
chokidar "^3.5.2"
debug "^4"
ignore-by-default "^1.0.1"
minimatch "^3.1.2"
pstree.remy "^1.1.8"
semver "^7.5.3"
simple-update-notifier "^2.0.0"
supports-color "^5.5.0"
touch "^3.1.0"
undefsafe "^2.0.5"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
object-assign@^4:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-inspect@^1.13.3:
version "1.13.4"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
picomatch@^2.0.4, picomatch@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
pstree.remy@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==
qs@6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
side-channel "^1.0.6"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
safe-buffer@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
semver@^7.5.3:
version "7.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
send@0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.16.2:
version "1.16.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
dependencies:
encodeurl "~2.0.0"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.19.0"
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel-list@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-map@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-weakmap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-map "^1.0.1"
side-channel@^1.0.6:
version "1.1.0"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-list "^1.0.0"
side-channel-map "^1.0.1"
side-channel-weakmap "^1.0.2"
simple-update-notifier@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb"
integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==
dependencies:
semver "^7.5.3"
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
touch@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694"
integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==
type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
undefsafe@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
ws@^8.14.2:
version "8.18.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472"
integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==