Commit 642842c2 authored by shaw's avatar shaw
Browse files

First commit

parent 569f4882
# Sub2API Deployment Files
This directory contains files for deploying Sub2API on Linux servers.
## Deployment Methods
| Method | Best For | Setup Wizard |
|--------|----------|--------------|
| **Docker Compose** | Quick setup, all-in-one | Not needed (auto-setup) |
| **Binary Install** | Production servers, systemd | Web-based wizard |
## Files
| File | Description |
|------|-------------|
| `docker-compose.yml` | Docker Compose configuration |
| `.env.example` | Docker environment variables template |
| `DOCKER.md` | Docker Hub documentation |
| `install.sh` | One-click binary installation script |
| `sub2api.service` | Systemd service unit file |
| `config.example.yaml` | Example configuration file |
---
## Docker Deployment (Recommended)
### Quick Start
```bash
# Clone repository
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api/deploy
# Configure environment
cp .env.example .env
nano .env # Set POSTGRES_PASSWORD (required)
# Start all services
docker-compose up -d
# View logs (check for auto-generated admin password)
docker-compose logs -f sub2api
# Access Web UI
# http://localhost:8080
```
### How Auto-Setup Works
When using Docker Compose with `AUTO_SETUP=true`:
1. On first run, the system automatically:
- Connects to PostgreSQL and Redis
- Creates all database tables
- Generates JWT secret (if not provided)
- Creates admin account (password auto-generated if not provided)
- Writes config.yaml
2. No manual Setup Wizard needed - just configure `.env` and start
3. If `ADMIN_PASSWORD` is not set, check logs for the generated password:
```bash
docker-compose logs sub2api | grep "admin password"
```
### Commands
```bash
# Start services
docker-compose up -d
# Stop services
docker-compose down
# View logs
docker-compose logs -f sub2api
# Restart Sub2API only
docker-compose restart sub2api
# Update to latest version
docker-compose pull
docker-compose up -d
# Remove all data (caution!)
docker-compose down -v
```
### Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `POSTGRES_PASSWORD` | **Yes** | - | PostgreSQL password |
| `SERVER_PORT` | No | `8080` | Server port |
| `ADMIN_EMAIL` | No | `admin@sub2api.local` | Admin email |
| `ADMIN_PASSWORD` | No | *(auto-generated)* | Admin password |
| `JWT_SECRET` | No | *(auto-generated)* | JWT secret |
| `TZ` | No | `Asia/Shanghai` | Timezone |
See `.env.example` for all available options.
---
## Binary Installation
For production servers using systemd.
### One-Line Installation
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
```
### Manual Installation
1. Download the latest release from [GitHub Releases](https://github.com/Wei-Shaw/sub2api/releases)
2. Extract and copy the binary to `/opt/sub2api/`
3. Copy `sub2api.service` to `/etc/systemd/system/`
4. Run:
```bash
sudo systemctl daemon-reload
sudo systemctl enable sub2api
sudo systemctl start sub2api
```
5. Open the Setup Wizard in your browser to complete configuration
### Commands
```bash
# Install
sudo ./install.sh
# Upgrade
sudo ./install.sh upgrade
# Uninstall
sudo ./install.sh uninstall
```
### Service Management
```bash
# Start the service
sudo systemctl start sub2api
# Stop the service
sudo systemctl stop sub2api
# Restart the service
sudo systemctl restart sub2api
# Check status
sudo systemctl status sub2api
# View logs
sudo journalctl -u sub2api -f
# Enable auto-start on boot
sudo systemctl enable sub2api
```
### Configuration
#### Server Address and Port
During installation, you will be prompted to configure the server listen address and port. These settings are stored in the systemd service file as environment variables.
To change after installation:
1. Edit the systemd service:
```bash
sudo systemctl edit sub2api
```
2. Add or modify:
```ini
[Service]
Environment=SERVER_HOST=0.0.0.0
Environment=SERVER_PORT=3000
```
3. Reload and restart:
```bash
sudo systemctl daemon-reload
sudo systemctl restart sub2api
```
#### Application Configuration
The main config file is at `/etc/sub2api/config.yaml` (created by Setup Wizard).
### Prerequisites
- Linux server (Ubuntu 20.04+, Debian 11+, CentOS 8+, etc.)
- PostgreSQL 14+
- Redis 6+
- systemd
### Directory Structure
```
/opt/sub2api/
├── sub2api # Main binary
├── sub2api.backup # Backup (after upgrade)
└── data/ # Runtime data
/etc/sub2api/
└── config.yaml # Configuration file
```
---
## Troubleshooting
### Docker
```bash
# Check container status
docker-compose ps
# View detailed logs
docker-compose logs --tail=100 sub2api
# Check database connection
docker-compose exec postgres pg_isready
# Check Redis connection
docker-compose exec redis redis-cli ping
# Restart all services
docker-compose restart
```
### Binary Install
```bash
# Check service status
sudo systemctl status sub2api
# View recent logs
sudo journalctl -u sub2api -n 50
# Check config file
sudo cat /etc/sub2api/config.yaml
# Check PostgreSQL
sudo systemctl status postgresql
# Check Redis
sudo systemctl status redis
```
### Common Issues
1. **Port already in use**: Change `SERVER_PORT` in `.env` or systemd config
2. **Database connection failed**: Check PostgreSQL is running and credentials are correct
3. **Redis connection failed**: Check Redis is running and password is correct
4. **Permission denied**: Ensure proper file ownership for binary install
# Sub2API Configuration File
# Copy this file to /etc/sub2api/config.yaml and modify as needed
# Documentation: https://github.com/Wei-Shaw/sub2api
# =============================================================================
# Server Configuration
# =============================================================================
server:
# Bind address (0.0.0.0 for all interfaces)
host: "0.0.0.0"
# Port to listen on
port: 8080
# Mode: "debug" for development, "release" for production
mode: "release"
# =============================================================================
# Database Configuration (PostgreSQL)
# =============================================================================
database:
host: "localhost"
port: 5432
user: "postgres"
password: "your_secure_password_here"
dbname: "sub2api"
# SSL mode: disable, require, verify-ca, verify-full
sslmode: "disable"
# =============================================================================
# Redis Configuration
# =============================================================================
redis:
host: "localhost"
port: 6379
# Leave empty if no password is set
password: ""
# Database number (0-15)
db: 0
# =============================================================================
# JWT Configuration
# =============================================================================
jwt:
# IMPORTANT: Change this to a random string in production!
# Generate with: openssl rand -hex 32
secret: "change-this-to-a-secure-random-string"
# Token expiration time in hours
expire_hour: 24
# =============================================================================
# Default Settings
# =============================================================================
default:
# Initial admin account (created on first run)
admin_email: "admin@example.com"
admin_password: "admin123"
# Default settings for new users
user_concurrency: 5 # Max concurrent requests per user
user_balance: 0 # Initial balance for new users
# API key settings
api_key_prefix: "sk-" # Prefix for generated API keys
# Rate multiplier (affects billing calculation)
rate_multiplier: 1.0
# =============================================================================
# Rate Limiting
# =============================================================================
rate_limit:
# Cooldown time (in minutes) when upstream returns 529 (overloaded)
overload_cooldown_minutes: 10
# =============================================================================
# Pricing Data Source (Optional)
# =============================================================================
pricing:
# URL to fetch model pricing data (default: LiteLLM)
remote_url: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
# Hash verification URL (optional)
hash_url: ""
# Local data directory for caching
data_dir: "./data"
# Fallback pricing file
fallback_file: "./resources/model-pricing/model_prices_and_context_window.json"
# Update interval in hours
update_interval_hours: 24
# Hash check interval in minutes
hash_check_interval_minutes: 10
# =============================================================================
# Sub2API Docker Compose Configuration
# =============================================================================
# Quick Start:
# 1. Copy .env.example to .env and configure
# 2. docker-compose up -d
# 3. Check logs: docker-compose logs -f sub2api
# 4. Access: http://localhost:8080
#
# All configuration is done via environment variables.
# No Setup Wizard needed - the system auto-initializes on first run.
# =============================================================================
services:
# ===========================================================================
# Sub2API Application
# ===========================================================================
sub2api:
image: weishaw/sub2api:latest
container_name: sub2api
restart: unless-stopped
ports:
- "${BIND_HOST:-0.0.0.0}:${SERVER_PORT:-8080}:8080"
volumes:
# Data persistence (config.yaml will be auto-generated here)
- sub2api_data:/app/data
environment:
# =======================================================================
# Auto Setup (REQUIRED for Docker deployment)
# =======================================================================
- AUTO_SETUP=true
# =======================================================================
# Server Configuration
# =======================================================================
- SERVER_HOST=0.0.0.0
- SERVER_PORT=8080
- SERVER_MODE=${SERVER_MODE:-release}
# =======================================================================
# Database Configuration (PostgreSQL)
# =======================================================================
- DATABASE_HOST=postgres
- DATABASE_PORT=5432
- DATABASE_USER=${POSTGRES_USER:-sub2api}
- DATABASE_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
- DATABASE_DBNAME=${POSTGRES_DB:-sub2api}
- DATABASE_SSLMODE=disable
# =======================================================================
# Redis Configuration
# =======================================================================
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
- REDIS_DB=${REDIS_DB:-0}
# =======================================================================
# Admin Account (auto-created on first run)
# =======================================================================
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@sub2api.local}
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
# =======================================================================
# JWT Configuration
# =======================================================================
# Leave empty to auto-generate (recommended)
- JWT_SECRET=${JWT_SECRET:-}
- JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24}
# =======================================================================
# Timezone Configuration
# This affects ALL time operations in the application:
# - Database timestamps
# - Usage statistics "today" boundary
# - Subscription expiry times
# - Log timestamps
# Common values: Asia/Shanghai, America/New_York, Europe/London, UTC
# =======================================================================
- TZ=${TZ:-Asia/Shanghai}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- sub2api-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# ===========================================================================
# PostgreSQL Database
# ===========================================================================
postgres:
image: postgres:15-alpine
container_name: sub2api-postgres
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${POSTGRES_USER:-sub2api}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
- POSTGRES_DB=${POSTGRES_DB:-sub2api}
- TZ=${TZ:-Asia/Shanghai}
networks:
- sub2api-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-sub2api} -d ${POSTGRES_DB:-sub2api}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# ===========================================================================
# Redis Cache
# ===========================================================================
redis:
image: redis:7-alpine
container_name: sub2api-redis
restart: unless-stopped
volumes:
- redis_data:/data
command: >
redis-server
--save 60 1
--appendonly yes
--appendfsync everysec
${REDIS_PASSWORD:+--requirepass ${REDIS_PASSWORD}}
environment:
- TZ=${TZ:-Asia/Shanghai}
networks:
- sub2api-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
# =============================================================================
# Volumes
# =============================================================================
volumes:
sub2api_data:
driver: local
postgres_data:
driver: local
redis_data:
driver: local
# =============================================================================
# Networks
# =============================================================================
networks:
sub2api-network:
driver: bridge
#!/bin/bash
#
# Sub2API Installation Script
# Sub2API 安装脚本
# Usage: curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | bash
#
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Configuration
GITHUB_REPO="Wei-Shaw/sub2api"
INSTALL_DIR="/opt/sub2api"
SERVICE_NAME="sub2api"
SERVICE_USER="sub2api"
CONFIG_DIR="/etc/sub2api"
# Server configuration (will be set by user)
SERVER_HOST="0.0.0.0"
SERVER_PORT="8080"
# Language (default: zh = Chinese)
LANG_CHOICE="zh"
# ============================================================
# Language strings / 语言字符串
# ============================================================
# Chinese strings
declare -A MSG_ZH=(
# General
["info"]="信息"
["success"]="成功"
["warning"]="警告"
["error"]="错误"
# Language selection
["select_lang"]="请选择语言 / Select language"
["lang_zh"]="中文"
["lang_en"]="English"
["enter_choice"]="请输入选择 (默认: 1)"
# Installation
["install_title"]="Sub2API 安装脚本"
["run_as_root"]="请使用 root 权限运行 (使用 sudo)"
["detected_platform"]="检测到平台"
["unsupported_arch"]="不支持的架构"
["unsupported_os"]="不支持的操作系统"
["missing_deps"]="缺少依赖"
["install_deps_first"]="请先安装以下依赖"
["fetching_version"]="正在获取最新版本..."
["latest_version"]="最新版本"
["failed_get_version"]="获取最新版本失败"
["downloading"]="正在下载"
["download_failed"]="下载失败"
["verifying_checksum"]="正在校验文件..."
["checksum_verified"]="校验通过"
["checksum_failed"]="校验失败"
["checksum_not_found"]="无法验证校验和(checksums.txt 未找到)"
["extracting"]="正在解压..."
["binary_installed"]="二进制文件已安装到"
["user_exists"]="用户已存在"
["creating_user"]="正在创建系统用户"
["user_created"]="用户已创建"
["setting_up_dirs"]="正在设置目录..."
["dirs_configured"]="目录配置完成"
["installing_service"]="正在安装 systemd 服务..."
["service_installed"]="systemd 服务已安装"
["setting_up_sudoers"]="正在配置 sudoers..."
["sudoers_configured"]="sudoers 配置完成"
["sudoers_failed"]="sudoers 验证失败,已移除文件"
["ready_for_setup"]="准备就绪,可以启动设置向导"
# Completion
["install_complete"]="Sub2API 安装完成!"
["install_dir"]="安装目录"
["next_steps"]="后续步骤"
["step1_check_services"]="确保 PostgreSQL 和 Redis 正在运行:"
["step2_start_service"]="启动 Sub2API 服务:"
["step3_enable_autostart"]="设置开机自启:"
["step4_open_wizard"]="在浏览器中打开设置向导:"
["wizard_guide"]="设置向导将引导您完成:"
["wizard_db"]="数据库配置"
["wizard_redis"]="Redis 配置"
["wizard_admin"]="管理员账号创建"
["useful_commands"]="常用命令"
["cmd_status"]="查看状态"
["cmd_logs"]="查看日志"
["cmd_restart"]="重启服务"
["cmd_stop"]="停止服务"
# Upgrade
["upgrading"]="正在升级 Sub2API..."
["current_version"]="当前版本"
["stopping_service"]="正在停止服务..."
["backup_created"]="备份已创建"
["starting_service"]="正在启动服务..."
["upgrade_complete"]="升级完成!"
# Uninstall
["uninstall_confirm"]="这将从系统中移除 Sub2API。"
["are_you_sure"]="确定要继续吗?(y/N)"
["uninstall_cancelled"]="卸载已取消"
["removing_files"]="正在移除文件..."
["removing_install_dir"]="正在移除安装目录..."
["removing_user"]="正在移除用户..."
["config_not_removed"]="配置目录未被移除"
["remove_manually"]="如不再需要,请手动删除"
["uninstall_complete"]="Sub2API 已卸载"
# Help
["usage"]="用法"
["cmd_none"]="(无参数)"
["cmd_install"]="安装 Sub2API"
["cmd_upgrade"]="升级到最新版本"
["cmd_uninstall"]="卸载 Sub2API"
# Server configuration
["server_config_title"]="服务器配置"
["server_config_desc"]="配置 Sub2API 服务监听地址"
["server_host_prompt"]="服务器监听地址"
["server_host_hint"]="0.0.0.0 表示监听所有网卡,127.0.0.1 仅本地访问"
["server_port_prompt"]="服务器端口"
["server_port_hint"]="建议使用 1024-65535 之间的端口"
["server_config_summary"]="服务器配置"
["invalid_port"]="无效端口号,请输入 1-65535 之间的数字"
)
# English strings
declare -A MSG_EN=(
# General
["info"]="INFO"
["success"]="SUCCESS"
["warning"]="WARNING"
["error"]="ERROR"
# Language selection
["select_lang"]="请选择语言 / Select language"
["lang_zh"]="中文"
["lang_en"]="English"
["enter_choice"]="Enter your choice (default: 1)"
# Installation
["install_title"]="Sub2API Installation Script"
["run_as_root"]="Please run as root (use sudo)"
["detected_platform"]="Detected platform"
["unsupported_arch"]="Unsupported architecture"
["unsupported_os"]="Unsupported OS"
["missing_deps"]="Missing dependencies"
["install_deps_first"]="Please install them first"
["fetching_version"]="Fetching latest version..."
["latest_version"]="Latest version"
["failed_get_version"]="Failed to get latest version"
["downloading"]="Downloading"
["download_failed"]="Download failed"
["verifying_checksum"]="Verifying checksum..."
["checksum_verified"]="Checksum verified"
["checksum_failed"]="Checksum verification failed"
["checksum_not_found"]="Could not verify checksum (checksums.txt not found)"
["extracting"]="Extracting..."
["binary_installed"]="Binary installed to"
["user_exists"]="User already exists"
["creating_user"]="Creating system user"
["user_created"]="User created"
["setting_up_dirs"]="Setting up directories..."
["dirs_configured"]="Directories configured"
["installing_service"]="Installing systemd service..."
["service_installed"]="Systemd service installed"
["setting_up_sudoers"]="Setting up sudoers..."
["sudoers_configured"]="Sudoers configured"
["sudoers_failed"]="Sudoers validation failed, removing file"
["ready_for_setup"]="Ready for Setup Wizard"
# Completion
["install_complete"]="Sub2API installation completed!"
["install_dir"]="Installation directory"
["next_steps"]="NEXT STEPS"
["step1_check_services"]="Make sure PostgreSQL and Redis are running:"
["step2_start_service"]="Start Sub2API service:"
["step3_enable_autostart"]="Enable auto-start on boot:"
["step4_open_wizard"]="Open the Setup Wizard in your browser:"
["wizard_guide"]="The Setup Wizard will guide you through:"
["wizard_db"]="Database configuration"
["wizard_redis"]="Redis configuration"
["wizard_admin"]="Admin account creation"
["useful_commands"]="USEFUL COMMANDS"
["cmd_status"]="Check status"
["cmd_logs"]="View logs"
["cmd_restart"]="Restart"
["cmd_stop"]="Stop"
# Upgrade
["upgrading"]="Upgrading Sub2API..."
["current_version"]="Current version"
["stopping_service"]="Stopping service..."
["backup_created"]="Backup created"
["starting_service"]="Starting service..."
["upgrade_complete"]="Upgrade completed!"
# Uninstall
["uninstall_confirm"]="This will remove Sub2API from your system."
["are_you_sure"]="Are you sure? (y/N)"
["uninstall_cancelled"]="Uninstall cancelled"
["removing_files"]="Removing files..."
["removing_install_dir"]="Removing installation directory..."
["removing_user"]="Removing user..."
["config_not_removed"]="Config directory was NOT removed."
["remove_manually"]="Remove it manually if you no longer need it."
["uninstall_complete"]="Sub2API has been uninstalled"
# Help
["usage"]="Usage"
["cmd_none"]="(none)"
["cmd_install"]="Install Sub2API"
["cmd_upgrade"]="Upgrade to the latest version"
["cmd_uninstall"]="Remove Sub2API"
# Server configuration
["server_config_title"]="Server Configuration"
["server_config_desc"]="Configure Sub2API server listen address"
["server_host_prompt"]="Server listen address"
["server_host_hint"]="0.0.0.0 listens on all interfaces, 127.0.0.1 for local only"
["server_port_prompt"]="Server port"
["server_port_hint"]="Recommended range: 1024-65535"
["server_config_summary"]="Server configuration"
["invalid_port"]="Invalid port number, please enter a number between 1-65535"
)
# Get message based on current language
msg() {
local key="$1"
if [ "$LANG_CHOICE" = "en" ]; then
echo "${MSG_EN[$key]}"
else
echo "${MSG_ZH[$key]}"
fi
}
# Print functions
print_info() {
echo -e "${BLUE}[$(msg 'info')]${NC} $1"
}
print_success() {
echo -e "${GREEN}[$(msg 'success')]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[$(msg 'warning')]${NC} $1"
}
print_error() {
echo -e "${RED}[$(msg 'error')]${NC} $1"
}
# Select language
select_language() {
echo ""
echo -e "${CYAN}=============================================="
echo " $(msg 'select_lang')"
echo "==============================================${NC}"
echo ""
echo " 1) $(msg 'lang_zh') (默认/default)"
echo " 2) $(msg 'lang_en')"
echo ""
# Read with timeout for piped input
read -t 10 -p "$(msg 'enter_choice'): " lang_input 2>/dev/null || lang_input=""
case "$lang_input" in
2|en|EN|english|English)
LANG_CHOICE="en"
;;
*)
LANG_CHOICE="zh"
;;
esac
echo ""
}
# Validate port number
validate_port() {
local port="$1"
if [[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then
return 0
fi
return 1
}
# Configure server settings
configure_server() {
echo ""
echo -e "${CYAN}=============================================="
echo " $(msg 'server_config_title')"
echo "==============================================${NC}"
echo ""
echo -e "${BLUE}$(msg 'server_config_desc')${NC}"
echo ""
# Server host
echo -e "${YELLOW}$(msg 'server_host_hint')${NC}"
read -p "$(msg 'server_host_prompt') [${SERVER_HOST}]: " input_host
if [ -n "$input_host" ]; then
SERVER_HOST="$input_host"
fi
echo ""
# Server port
echo -e "${YELLOW}$(msg 'server_port_hint')${NC}"
while true; do
read -p "$(msg 'server_port_prompt') [${SERVER_PORT}]: " input_port
if [ -z "$input_port" ]; then
# Use default
break
elif validate_port "$input_port"; then
SERVER_PORT="$input_port"
break
else
print_error "$(msg 'invalid_port')"
fi
done
echo ""
print_info "$(msg 'server_config_summary'): ${SERVER_HOST}:${SERVER_PORT}"
echo ""
}
# Check if running as root
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "$(msg 'run_as_root')"
exit 1
fi
}
# Detect OS and architecture
detect_platform() {
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$ARCH" in
x86_64)
ARCH="amd64"
;;
aarch64|arm64)
ARCH="arm64"
;;
*)
print_error "$(msg 'unsupported_arch'): $ARCH"
exit 1
;;
esac
case "$OS" in
linux)
OS="linux"
;;
darwin)
OS="darwin"
;;
*)
print_error "$(msg 'unsupported_os'): $OS"
exit 1
;;
esac
print_info "$(msg 'detected_platform'): ${OS}_${ARCH}"
}
# Check dependencies
check_dependencies() {
local missing=()
if ! command -v curl &> /dev/null; then
missing+=("curl")
fi
if ! command -v tar &> /dev/null; then
missing+=("tar")
fi
if [ ${#missing[@]} -gt 0 ]; then
print_error "$(msg 'missing_deps'): ${missing[*]}"
print_info "$(msg 'install_deps_first')"
exit 1
fi
}
# Get latest release version
get_latest_version() {
print_info "$(msg 'fetching_version')"
LATEST_VERSION=$(curl -s "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
if [ -z "$LATEST_VERSION" ]; then
print_error "$(msg 'failed_get_version')"
exit 1
fi
print_info "$(msg 'latest_version'): $LATEST_VERSION"
}
# Download and extract
download_and_extract() {
local version_num=${LATEST_VERSION#v}
local archive_name="sub2api_${version_num}_${OS}_${ARCH}.tar.gz"
local download_url="https://github.com/${GITHUB_REPO}/releases/download/${LATEST_VERSION}/${archive_name}"
local checksum_url="https://github.com/${GITHUB_REPO}/releases/download/${LATEST_VERSION}/checksums.txt"
print_info "$(msg 'downloading') ${archive_name}..."
# Create temp directory
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
# Download archive
if ! curl -sL "$download_url" -o "$TEMP_DIR/$archive_name"; then
print_error "$(msg 'download_failed')"
exit 1
fi
# Download and verify checksum
print_info "$(msg 'verifying_checksum')"
if curl -sL "$checksum_url" -o "$TEMP_DIR/checksums.txt" 2>/dev/null; then
local expected_checksum=$(grep "$archive_name" "$TEMP_DIR/checksums.txt" | awk '{print $1}')
local actual_checksum=$(sha256sum "$TEMP_DIR/$archive_name" | awk '{print $1}')
if [ "$expected_checksum" != "$actual_checksum" ]; then
print_error "$(msg 'checksum_failed')"
print_error "Expected: $expected_checksum"
print_error "Actual: $actual_checksum"
exit 1
fi
print_success "$(msg 'checksum_verified')"
else
print_warning "$(msg 'checksum_not_found')"
fi
# Extract
print_info "$(msg 'extracting')"
tar -xzf "$TEMP_DIR/$archive_name" -C "$TEMP_DIR"
# Create install directory
mkdir -p "$INSTALL_DIR"
# Copy binary
cp "$TEMP_DIR/sub2api" "$INSTALL_DIR/sub2api"
chmod +x "$INSTALL_DIR/sub2api"
# Copy deploy files if they exist in the archive
if [ -d "$TEMP_DIR/deploy" ]; then
cp -r "$TEMP_DIR/deploy/"* "$INSTALL_DIR/" 2>/dev/null || true
fi
print_success "$(msg 'binary_installed') $INSTALL_DIR/sub2api"
}
# Create system user
create_user() {
if id "$SERVICE_USER" &>/dev/null; then
print_info "$(msg 'user_exists'): $SERVICE_USER"
else
print_info "$(msg 'creating_user') $SERVICE_USER..."
useradd -r -s /bin/false -d "$INSTALL_DIR" "$SERVICE_USER"
print_success "$(msg 'user_created')"
fi
}
# Setup directories and permissions
setup_directories() {
print_info "$(msg 'setting_up_dirs')"
# Create directories
mkdir -p "$INSTALL_DIR"
mkdir -p "$INSTALL_DIR/data"
mkdir -p "$CONFIG_DIR"
# Set ownership
chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR"
chown -R "$SERVICE_USER:$SERVICE_USER" "$CONFIG_DIR"
print_success "$(msg 'dirs_configured')"
}
# Setup sudoers for service restart
setup_sudoers() {
print_info "$(msg 'setting_up_sudoers')"
# Check if sudoers file exists in install dir
if [ -f "$INSTALL_DIR/sub2api-sudoers" ]; then
cp "$INSTALL_DIR/sub2api-sudoers" /etc/sudoers.d/sub2api
else
# Create sudoers file
cat > /etc/sudoers.d/sub2api << 'EOF'
# Sudoers configuration for Sub2API
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl restart sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl stop sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl start sub2api
EOF
fi
# Set correct permissions (required for sudoers files)
chmod 440 /etc/sudoers.d/sub2api
# Validate sudoers file
if visudo -c -f /etc/sudoers.d/sub2api &>/dev/null; then
print_success "$(msg 'sudoers_configured')"
else
print_warning "$(msg 'sudoers_failed')"
rm -f /etc/sudoers.d/sub2api
fi
}
# Install systemd service
install_service() {
print_info "$(msg 'installing_service')"
# Create service file with configured host and port
cat > /etc/systemd/system/sub2api.service << EOF
[Unit]
Description=Sub2API - AI API Gateway Platform
Documentation=https://github.com/Wei-Shaw/sub2api
After=network.target postgresql.service redis.service
Wants=postgresql.service redis.service
[Service]
Type=simple
User=sub2api
Group=sub2api
WorkingDirectory=/opt/sub2api
ExecStart=/opt/sub2api/sub2api
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sub2api
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/sub2api
# Environment - Server configuration
Environment=GIN_MODE=release
Environment=SERVER_HOST=${SERVER_HOST}
Environment=SERVER_PORT=${SERVER_PORT}
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd
systemctl daemon-reload
print_success "$(msg 'service_installed')"
}
# Prepare for setup wizard (no config file needed - setup wizard will create it)
prepare_for_setup() {
print_success "$(msg 'ready_for_setup')"
}
# Print completion message
print_completion() {
local ip_addr
ip_addr=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "YOUR_SERVER_IP")
# Determine display address
local display_host="$ip_addr"
if [ "$SERVER_HOST" = "127.0.0.1" ]; then
display_host="127.0.0.1"
fi
echo ""
echo "=============================================="
print_success "$(msg 'install_complete')"
echo "=============================================="
echo ""
echo "$(msg 'install_dir'): $INSTALL_DIR"
echo "$(msg 'server_config_summary'): ${SERVER_HOST}:${SERVER_PORT}"
echo ""
echo "=============================================="
echo " $(msg 'next_steps')"
echo "=============================================="
echo ""
echo " 1. $(msg 'step1_check_services')"
echo " sudo systemctl status postgresql"
echo " sudo systemctl status redis"
echo ""
echo " 2. $(msg 'step2_start_service')"
echo " sudo systemctl start sub2api"
echo ""
echo " 3. $(msg 'step3_enable_autostart')"
echo " sudo systemctl enable sub2api"
echo ""
echo " 4. $(msg 'step4_open_wizard')"
echo ""
print_info " http://${display_host}:${SERVER_PORT}"
echo ""
echo " $(msg 'wizard_guide')"
echo " - $(msg 'wizard_db')"
echo " - $(msg 'wizard_redis')"
echo " - $(msg 'wizard_admin')"
echo ""
echo "=============================================="
echo " $(msg 'useful_commands')"
echo "=============================================="
echo ""
echo " $(msg 'cmd_status'): sudo systemctl status sub2api"
echo " $(msg 'cmd_logs'): sudo journalctl -u sub2api -f"
echo " $(msg 'cmd_restart'): sudo systemctl restart sub2api"
echo " $(msg 'cmd_stop'): sudo systemctl stop sub2api"
echo ""
echo "=============================================="
}
# Upgrade function
upgrade() {
print_info "$(msg 'upgrading')"
# Get current version
if [ -f "$INSTALL_DIR/sub2api" ]; then
CURRENT_VERSION=$("$INSTALL_DIR/sub2api" --version 2>/dev/null | grep -oP 'v?\d+\.\d+\.\d+' || echo "unknown")
print_info "$(msg 'current_version'): $CURRENT_VERSION"
fi
# Stop service
if systemctl is-active --quiet sub2api; then
print_info "$(msg 'stopping_service')"
systemctl stop sub2api
fi
# Backup current binary
if [ -f "$INSTALL_DIR/sub2api" ]; then
cp "$INSTALL_DIR/sub2api" "$INSTALL_DIR/sub2api.backup"
print_info "$(msg 'backup_created'): $INSTALL_DIR/sub2api.backup"
fi
# Download and install new version
get_latest_version
download_and_extract
# Set permissions
chown "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR/sub2api"
# Start service
print_info "$(msg 'starting_service')"
systemctl start sub2api
print_success "$(msg 'upgrade_complete')"
}
# Uninstall function
uninstall() {
print_warning "$(msg 'uninstall_confirm')"
read -p "$(msg 'are_you_sure') " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "$(msg 'uninstall_cancelled')"
exit 0
fi
print_info "$(msg 'stopping_service')"
systemctl stop sub2api 2>/dev/null || true
systemctl disable sub2api 2>/dev/null || true
print_info "$(msg 'removing_files')"
rm -f /etc/systemd/system/sub2api.service
rm -f /etc/sudoers.d/sub2api
systemctl daemon-reload
print_info "$(msg 'removing_install_dir')"
rm -rf "$INSTALL_DIR"
print_info "$(msg 'removing_user')"
userdel "$SERVICE_USER" 2>/dev/null || true
print_warning "$(msg 'config_not_removed'): $CONFIG_DIR"
print_warning "$(msg 'remove_manually')"
print_success "$(msg 'uninstall_complete')"
}
# Main
main() {
# Select language first
select_language
echo ""
echo "=============================================="
echo " $(msg 'install_title')"
echo "=============================================="
echo ""
# Parse arguments
case "${1:-}" in
upgrade|update)
check_root
detect_platform
upgrade
exit 0
;;
uninstall|remove)
check_root
uninstall
exit 0
;;
--help|-h)
echo "$(msg 'usage'): $0 [command]"
echo ""
echo "Commands:"
echo " $(msg 'cmd_none') $(msg 'cmd_install')"
echo " upgrade $(msg 'cmd_upgrade')"
echo " uninstall $(msg 'cmd_uninstall')"
echo ""
exit 0
;;
esac
# Fresh install
check_root
detect_platform
check_dependencies
configure_server
get_latest_version
download_and_extract
create_user
setup_directories
install_service
setup_sudoers
prepare_for_setup
print_completion
}
main "$@"
# Sudoers configuration for Sub2API
# This file allows the sub2api service user to restart the service without password
#
# Installation:
# sudo cp sub2api-sudoers /etc/sudoers.d/sub2api
# sudo chmod 440 /etc/sudoers.d/sub2api
#
# SECURITY NOTE: This grants limited sudo access only for service management
# Allow sub2api user to restart the service without password
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl restart sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl stop sub2api
sub2api ALL=(ALL) NOPASSWD: /bin/systemctl start sub2api
[Unit]
Description=Sub2API - AI API Gateway Platform
Documentation=https://github.com/Wei-Shaw/sub2api
After=network.target postgresql.service redis.service
Wants=postgresql.service redis.service
[Service]
Type=simple
User=sub2api
Group=sub2api
WorkingDirectory=/opt/sub2api
ExecStart=/opt/sub2api/sub2api
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sub2api
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/sub2api
# Environment - Server configuration
# Modify these values to change listen address and port
Environment=GIN_MODE=release
Environment=SERVER_HOST=0.0.0.0
Environment=SERVER_PORT=8080
[Install]
WantedBy=multi-user.target
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sub2API - AI API Gateway</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
{
"name": "sub2api-frontend",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sub2api-frontend",
"version": "1.0.0",
"dependencies": {
"@vueuse/core": "^10.7.0",
"axios": "^1.6.2",
"chart.js": "^4.4.1",
"pinia": "^2.1.7",
"vue": "^3.4.0",
"vue-chartjs": "^5.3.0",
"vue-i18n": "^9.14.5",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@types/node": "^20.10.5",
"@vitejs/plugin-vue": "^4.5.2",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.32",
"tailwindcss": "^3.4.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vue-tsc": "^1.8.25"
}
},
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.28.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.5",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.5"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.5",
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz",
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@intlify/core-base": {
"version": "9.14.5",
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.5.tgz",
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
"license": "MIT",
"dependencies": {
"@intlify/message-compiler": "9.14.5",
"@intlify/shared": "9.14.5"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.14.5",
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
"license": "MIT",
"dependencies": {
"@intlify/shared": "9.14.5",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "9.14.5",
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.5.tgz",
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
"license": "MIT"
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.stat": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
"integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
"integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
"integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
"integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
"integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
"integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
"integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
"integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
"integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
"integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
"integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
"integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
"integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
"integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
"integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
"integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
"integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
"integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
"integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
"integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
"integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
"integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.26",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.26.tgz",
"integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.20",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": {
"version": "4.6.2",
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
"integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"peerDependencies": {
"vite": "^4.0.0 || ^5.0.0",
"vue": "^3.2.25"
}
},
"node_modules/@volar/language-core": {
"version": "1.11.1",
"resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-1.11.1.tgz",
"integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@volar/source-map": "1.11.1"
}
},
"node_modules/@volar/source-map": {
"version": "1.11.1",
"resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-1.11.1.tgz",
"integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==",
"dev": true,
"license": "MIT",
"dependencies": {
"muggle-string": "^0.3.1"
}
},
"node_modules/@volar/typescript": {
"version": "1.11.1",
"resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-1.11.1.tgz",
"integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@volar/language-core": "1.11.1",
"path-browserify": "^1.0.1"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
"integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/shared": "3.5.25",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
"integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.25",
"@vue/shared": "3.5.25"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz",
"integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/compiler-core": "3.5.25",
"@vue/compiler-dom": "3.5.25",
"@vue/compiler-ssr": "3.5.25",
"@vue/shared": "3.5.25",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.21",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz",
"integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.25",
"@vue/shared": "3.5.25"
}
},
"node_modules/@vue/devtools-api": {
"version": "6.6.4",
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/@vue/language-core": {
"version": "1.8.27",
"resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-1.8.27.tgz",
"integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@volar/language-core": "~1.11.1",
"@volar/source-map": "~1.11.1",
"@vue/compiler-dom": "^3.3.0",
"@vue/shared": "^3.3.0",
"computeds": "^0.0.1",
"minimatch": "^9.0.3",
"muggle-string": "^0.3.1",
"path-browserify": "^1.0.1",
"vue-template-compiler": "^2.7.14"
},
"peerDependencies": {
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.25.tgz",
"integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==",
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.25"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz",
"integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.25",
"@vue/shared": "3.5.25"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz",
"integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.25",
"@vue/runtime-core": "3.5.25",
"@vue/shared": "3.5.25",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz",
"integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==",
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.25",
"@vue/shared": "3.5.25"
},
"peerDependencies": {
"vue": "3.5.25"
}
},
"node_modules/@vue/shared": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz",
"integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
"license": "MIT"
},
"node_modules/@vueuse/core": {
"version": "10.11.1",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz",
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
"license": "MIT",
"dependencies": {
"@types/web-bluetooth": "^0.0.20",
"@vueuse/metadata": "10.11.1",
"@vueuse/shared": "10.11.1",
"vue-demi": ">=0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/metadata": {
"version": "10.11.1",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz",
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "10.11.1",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz",
"integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
"license": "MIT",
"dependencies": {
"vue-demi": ">=0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
"dev": true,
"license": "MIT"
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"dev": true,
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.22",
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.22.tgz",
"integrity": "sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.27.0",
"caniuse-lite": "^1.0.30001754",
"fraction.js": "^5.3.4",
"normalize-range": "^0.1.2",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
"bin": {
"autoprefixer": "bin/autoprefixer"
},
"engines": {
"node": "^10 || ^12 || >=14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "1.13.2",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz",
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.9.7",
"resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz",
"integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
},
"engines": {
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001760",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
"integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0"
},
"node_modules/chart.js": {
"version": "4.5.1",
"resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"license": "MIT",
"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"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/computeds": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/computeds/-/computeds-0.0.1.tgz",
"integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==",
"dev": true,
"license": "MIT"
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true,
"license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/de-indent": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
"dev": true,
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true,
"license": "MIT"
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.267",
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
"dev": true,
"license": "ISC"
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.8"
},
"engines": {
"node": ">=8.6.0"
}
},
"node_modules/fast-glob/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fastq": {
"version": "1.19.1",
"resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz",
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fraction.js": {
"version": "5.3.4",
"resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz",
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true,
"license": "MIT",
"bin": {
"he": "bin/he"
}
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/jiti": {
"version": "1.21.7",
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz",
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
"bin": {
"jiti": "bin/jiti.js"
}
},
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true,
"license": "MIT"
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/muggle-string": {
"version": "0.3.1",
"resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.3.1.tgz",
"integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==",
"dev": true,
"license": "MIT"
},
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/node-releases": {
"version": "2.0.27",
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"dev": true,
"license": "MIT"
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/normalize-range": {
"version": "0.1.2",
"resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz",
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
"dev": true,
"license": "MIT"
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pify": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pinia": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz",
"integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.4.4",
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pirates": {
"version": "4.0.7",
"resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz",
"integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"dev": true,
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
"resolve": "^1.1.7"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"postcss": "^8.0.0"
}
},
"node_modules/postcss-js": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz",
"integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"camelcase-css": "^2.0.1"
},
"engines": {
"node": "^12 || ^14 || >= 16"
},
"peerDependencies": {
"postcss": "^8.4.21"
}
},
"node_modules/postcss-load-config": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
"integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"lilconfig": "^3.1.1"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"jiti": ">=1.21.0",
"postcss": ">=8.0.9",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"jiti": {
"optional": true
},
"postcss": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
},
"node_modules/postcss-nested": {
"version": "6.2.0",
"resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.1.1"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"postcss": "^8.2.14"
}
},
"node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true,
"license": "MIT"
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"pify": "^2.3.0"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz",
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
}
},
"node_modules/rollup": {
"version": "4.53.3",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz",
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.53.3",
"@rollup/rollup-android-arm64": "4.53.3",
"@rollup/rollup-darwin-arm64": "4.53.3",
"@rollup/rollup-darwin-x64": "4.53.3",
"@rollup/rollup-freebsd-arm64": "4.53.3",
"@rollup/rollup-freebsd-x64": "4.53.3",
"@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
"@rollup/rollup-linux-arm-musleabihf": "4.53.3",
"@rollup/rollup-linux-arm64-gnu": "4.53.3",
"@rollup/rollup-linux-arm64-musl": "4.53.3",
"@rollup/rollup-linux-loong64-gnu": "4.53.3",
"@rollup/rollup-linux-ppc64-gnu": "4.53.3",
"@rollup/rollup-linux-riscv64-gnu": "4.53.3",
"@rollup/rollup-linux-riscv64-musl": "4.53.3",
"@rollup/rollup-linux-s390x-gnu": "4.53.3",
"@rollup/rollup-linux-x64-gnu": "4.53.3",
"@rollup/rollup-linux-x64-musl": "4.53.3",
"@rollup/rollup-openharmony-arm64": "4.53.3",
"@rollup/rollup-win32-arm64-msvc": "4.53.3",
"@rollup/rollup-win32-ia32-msvc": "4.53.3",
"@rollup/rollup-win32-x64-gnu": "4.53.3",
"@rollup/rollup-win32-x64-msvc": "4.53.3",
"fsevents": "~2.3.2"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"queue-microtask": "^1.2.2"
}
},
"node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sucrase": {
"version": "3.35.1",
"resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.1.tgz",
"integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
"lines-and-columns": "^1.1.6",
"mz": "^2.7.0",
"pirates": "^4.0.1",
"tinyglobby": "^0.2.11",
"ts-interface-checker": "^0.1.9"
},
"bin": {
"sucrase": "bin/sucrase",
"sucrase-node": "bin/sucrase-node"
},
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tailwindcss": {
"version": "3.4.19",
"resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.19.tgz",
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.7",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0"
}
},
"node_modules/thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dev": true,
"license": "MIT",
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
"integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true,
"license": "MIT"
},
"node_modules/vite": {
"version": "5.4.21",
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
},
"node_modules/vue": {
"version": "3.5.25",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.25",
"@vue/compiler-sfc": "3.5.25",
"@vue/runtime-dom": "3.5.25",
"@vue/server-renderer": "3.5.25",
"@vue/shared": "3.5.25"
},
"peerDependencies": {
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/vue-chartjs": {
"version": "5.3.3",
"resolved": "https://registry.npmmirror.com/vue-chartjs/-/vue-chartjs-5.3.3.tgz",
"integrity": "sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==",
"license": "MIT",
"peerDependencies": {
"chart.js": "^4.1.1",
"vue": "^3.0.0-0 || ^2.7.0"
}
},
"node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-i18n": {
"version": "9.14.5",
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.5.tgz",
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
"license": "MIT",
"dependencies": {
"@intlify/core-base": "9.14.5",
"@intlify/shared": "9.14.5",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-router": {
"version": "4.6.4",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^6.6.4"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"vue": "^3.5.0"
}
},
"node_modules/vue-template-compiler": {
"version": "2.7.16",
"resolved": "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
"integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"de-indent": "^1.0.2",
"he": "^1.2.0"
}
},
"node_modules/vue-tsc": {
"version": "1.8.27",
"resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-1.8.27.tgz",
"integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@volar/typescript": "~1.11.1",
"@vue/language-core": "1.8.27",
"semver": "^7.5.4"
},
"bin": {
"vue-tsc": "bin/vue-tsc.js"
},
"peerDependencies": {
"typescript": "*"
}
}
}
}
{
"name": "sub2api-frontend",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"
},
"dependencies": {
"@vueuse/core": "^10.7.0",
"axios": "^1.6.2",
"chart.js": "^4.4.1",
"pinia": "^2.1.7",
"vue": "^3.4.0",
"vue-chartjs": "^5.3.0",
"vue-i18n": "^9.14.5",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@types/node": "^20.10.5",
"@vitejs/plugin-vue": "^4.5.2",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.32",
"tailwindcss": "^3.4.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vue-tsc": "^1.8.25"
}
}
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
<script setup lang="ts">
import { RouterView, useRouter, useRoute } from 'vue-router'
import { onMounted } from 'vue'
import Toast from '@/components/common/Toast.vue'
import { getPublicSettings } from '@/api/auth'
import { getSetupStatus } from '@/api/setup'
const router = useRouter()
const route = useRoute()
/**
* Update favicon dynamically
* @param logoUrl - URL of the logo to use as favicon
*/
function updateFavicon(logoUrl: string) {
// Find existing favicon link or create new one
let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
if (!link) {
link = document.createElement('link')
link.rel = 'icon'
document.head.appendChild(link)
}
link.type = logoUrl.endsWith('.svg') ? 'image/svg+xml' : 'image/x-icon'
link.href = logoUrl
}
onMounted(async () => {
// Check if setup is needed
try {
const status = await getSetupStatus()
if (status.needs_setup && route.path !== '/setup') {
router.replace('/setup')
return
}
} catch {
// If setup endpoint fails, assume normal mode and continue
}
try {
const settings = await getPublicSettings()
// Update favicon if logo is set
if (settings.site_logo) {
updateFavicon(settings.site_logo)
}
// Update page title if site name is set
if (settings.site_name) {
document.title = `${settings.site_name} - AI API Gateway`
}
} catch (error) {
console.error('Failed to load public settings for favicon:', error)
}
})
</script>
<template>
<RouterView />
<Toast />
</template>
/**
* Admin Accounts API endpoints
* Handles AI platform account management for administrators
*/
import { apiClient } from '../client';
import type {
Account,
CreateAccountRequest,
UpdateAccountRequest,
PaginatedResponse,
AccountUsageInfo,
WindowStats,
} from '@/types';
/**
* List all accounts with pagination
* @param page - Page number (default: 1)
* @param pageSize - Items per page (default: 20)
* @param filters - Optional filters
* @returns Paginated list of accounts
*/
export async function list(
page: number = 1,
pageSize: number = 20,
filters?: {
platform?: string;
type?: string;
status?: string;
search?: string;
}
): Promise<PaginatedResponse<Account>> {
const { data } = await apiClient.get<PaginatedResponse<Account>>('/admin/accounts', {
params: {
page,
page_size: pageSize,
...filters,
},
});
return data;
}
/**
* Get account by ID
* @param id - Account ID
* @returns Account details
*/
export async function getById(id: number): Promise<Account> {
const { data } = await apiClient.get<Account>(`/admin/accounts/${id}`);
return data;
}
/**
* Create new account
* @param accountData - Account data
* @returns Created account
*/
export async function create(accountData: CreateAccountRequest): Promise<Account> {
const { data } = await apiClient.post<Account>('/admin/accounts', accountData);
return data;
}
/**
* Update account
* @param id - Account ID
* @param updates - Fields to update
* @returns Updated account
*/
export async function update(id: number, updates: UpdateAccountRequest): Promise<Account> {
const { data } = await apiClient.put<Account>(`/admin/accounts/${id}`, updates);
return data;
}
/**
* Delete account
* @param id - Account ID
* @returns Success confirmation
*/
export async function deleteAccount(id: number): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(`/admin/accounts/${id}`);
return data;
}
/**
* Toggle account status
* @param id - Account ID
* @param status - New status
* @returns Updated account
*/
export async function toggleStatus(
id: number,
status: 'active' | 'inactive'
): Promise<Account> {
return update(id, { status });
}
/**
* Test account connectivity
* @param id - Account ID
* @returns Test result
*/
export async function testAccount(id: number): Promise<{
success: boolean;
message: string;
latency_ms?: number;
}> {
const { data } = await apiClient.post<{
success: boolean;
message: string;
latency_ms?: number;
}>(`/admin/accounts/${id}/test`);
return data;
}
/**
* Refresh account credentials
* @param id - Account ID
* @returns Updated account
*/
export async function refreshCredentials(id: number): Promise<Account> {
const { data } = await apiClient.post<Account>(`/admin/accounts/${id}/refresh`);
return data;
}
/**
* Get account usage statistics
* @param id - Account ID
* @param period - Time period
* @returns Account usage statistics
*/
export async function getStats(
id: number,
period: string = 'month'
): Promise<{
total_requests: number;
successful_requests: number;
failed_requests: number;
total_tokens: number;
average_response_time: number;
}> {
const { data } = await apiClient.get<{
total_requests: number;
successful_requests: number;
failed_requests: number;
total_tokens: number;
average_response_time: number;
}>(`/admin/accounts/${id}/stats`, {
params: { period },
});
return data;
}
/**
* Clear account error
* @param id - Account ID
* @returns Updated account
*/
export async function clearError(id: number): Promise<Account> {
const { data } = await apiClient.post<Account>(`/admin/accounts/${id}/clear-error`);
return data;
}
/**
* Get account usage information (5h/7d window)
* @param id - Account ID
* @returns Account usage info
*/
export async function getUsage(id: number): Promise<AccountUsageInfo> {
const { data } = await apiClient.get<AccountUsageInfo>(`/admin/accounts/${id}/usage`);
return data;
}
/**
* Clear account rate limit status
* @param id - Account ID
* @returns Success confirmation
*/
export async function clearRateLimit(id: number): Promise<{ message: string }> {
const { data } = await apiClient.post<{ message: string }>(`/admin/accounts/${id}/clear-rate-limit`);
return data;
}
/**
* Generate OAuth authorization URL
* @param endpoint - API endpoint path
* @param config - Proxy configuration
* @returns Auth URL and session ID
*/
export async function generateAuthUrl(
endpoint: string,
config: { proxy_id?: number }
): Promise<{ auth_url: string; session_id: string }> {
const { data } = await apiClient.post<{ auth_url: string; session_id: string }>(endpoint, config);
return data;
}
/**
* Exchange authorization code for tokens
* @param endpoint - API endpoint path
* @param exchangeData - Session ID, code, and optional proxy config
* @returns Token information
*/
export async function exchangeCode(
endpoint: string,
exchangeData: { session_id: string; code: string; proxy_id?: number }
): Promise<Record<string, unknown>> {
const { data } = await apiClient.post<Record<string, unknown>>(endpoint, exchangeData);
return data;
}
/**
* Batch create accounts
* @param accounts - Array of account data
* @returns Results of batch creation
*/
export async function batchCreate(accounts: CreateAccountRequest[]): Promise<{
success: number;
failed: number;
results: Array<{ success: boolean; account?: Account; error?: string }>;
}> {
const { data } = await apiClient.post<{
success: number;
failed: number;
results: Array<{ success: boolean; account?: Account; error?: string }>;
}>('/admin/accounts/batch', { accounts });
return data;
}
/**
* Get account today statistics
* @param id - Account ID
* @returns Today's stats (requests, tokens, cost)
*/
export async function getTodayStats(id: number): Promise<WindowStats> {
const { data } = await apiClient.get<WindowStats>(`/admin/accounts/${id}/today-stats`);
return data;
}
/**
* Set account schedulable status
* @param id - Account ID
* @param schedulable - Whether the account should participate in scheduling
* @returns Updated account
*/
export async function setSchedulable(id: number, schedulable: boolean): Promise<Account> {
const { data } = await apiClient.post<Account>(`/admin/accounts/${id}/schedulable`, { schedulable });
return data;
}
export const accountsAPI = {
list,
getById,
create,
update,
delete: deleteAccount,
toggleStatus,
testAccount,
refreshCredentials,
getStats,
clearError,
getUsage,
getTodayStats,
clearRateLimit,
setSchedulable,
generateAuthUrl,
exchangeCode,
batchCreate,
};
export default accountsAPI;
/**
* Admin Dashboard API endpoints
* Provides system-wide statistics and metrics
*/
import { apiClient } from '../client';
import type { DashboardStats, TrendDataPoint, ModelStat, ApiKeyUsageTrendPoint, UserUsageTrendPoint } from '@/types';
/**
* Get dashboard statistics
* @returns Dashboard statistics including users, keys, accounts, and token usage
*/
export async function getStats(): Promise<DashboardStats> {
const { data } = await apiClient.get<DashboardStats>('/admin/dashboard/stats');
return data;
}
/**
* Get real-time metrics
* @returns Real-time system metrics
*/
export async function getRealtimeMetrics(): Promise<{
active_requests: number;
requests_per_minute: number;
average_response_time: number;
error_rate: number;
}> {
const { data } = await apiClient.get<{
active_requests: number;
requests_per_minute: number;
average_response_time: number;
error_rate: number;
}>('/admin/dashboard/realtime');
return data;
}
export interface TrendParams {
start_date?: string;
end_date?: string;
granularity?: 'day' | 'hour';
}
export interface TrendResponse {
trend: TrendDataPoint[];
start_date: string;
end_date: string;
granularity: string;
}
/**
* Get usage trend data
* @param params - Query parameters for filtering
* @returns Usage trend data
*/
export async function getUsageTrend(params?: TrendParams): Promise<TrendResponse> {
const { data } = await apiClient.get<TrendResponse>('/admin/dashboard/trend', { params });
return data;
}
export interface ModelStatsResponse {
models: ModelStat[];
start_date: string;
end_date: string;
}
/**
* Get model usage statistics
* @param params - Query parameters for filtering
* @returns Model usage statistics
*/
export async function getModelStats(params?: { start_date?: string; end_date?: string }): Promise<ModelStatsResponse> {
const { data } = await apiClient.get<ModelStatsResponse>('/admin/dashboard/models', { params });
return data;
}
export interface ApiKeyTrendParams extends TrendParams {
limit?: number;
}
export interface ApiKeyTrendResponse {
trend: ApiKeyUsageTrendPoint[];
start_date: string;
end_date: string;
granularity: string;
}
/**
* Get API key usage trend data
* @param params - Query parameters for filtering
* @returns API key usage trend data
*/
export async function getApiKeyUsageTrend(params?: ApiKeyTrendParams): Promise<ApiKeyTrendResponse> {
const { data } = await apiClient.get<ApiKeyTrendResponse>('/admin/dashboard/api-keys-trend', { params });
return data;
}
export interface UserTrendParams extends TrendParams {
limit?: number;
}
export interface UserTrendResponse {
trend: UserUsageTrendPoint[];
start_date: string;
end_date: string;
granularity: string;
}
/**
* Get user usage trend data
* @param params - Query parameters for filtering
* @returns User usage trend data
*/
export async function getUserUsageTrend(params?: UserTrendParams): Promise<UserTrendResponse> {
const { data } = await apiClient.get<UserTrendResponse>('/admin/dashboard/users-trend', { params });
return data;
}
export interface BatchUserUsageStats {
user_id: number;
today_actual_cost: number;
total_actual_cost: number;
}
export interface BatchUsersUsageResponse {
stats: Record<string, BatchUserUsageStats>;
}
/**
* Get batch usage stats for multiple users
* @param userIds - Array of user IDs
* @returns Usage stats map keyed by user ID
*/
export async function getBatchUsersUsage(userIds: number[]): Promise<BatchUsersUsageResponse> {
const { data } = await apiClient.post<BatchUsersUsageResponse>('/admin/dashboard/users-usage', {
user_ids: userIds,
});
return data;
}
export interface BatchApiKeyUsageStats {
api_key_id: number;
today_actual_cost: number;
total_actual_cost: number;
}
export interface BatchApiKeysUsageResponse {
stats: Record<string, BatchApiKeyUsageStats>;
}
/**
* Get batch usage stats for multiple API keys
* @param apiKeyIds - Array of API key IDs
* @returns Usage stats map keyed by API key ID
*/
export async function getBatchApiKeysUsage(apiKeyIds: number[]): Promise<BatchApiKeysUsageResponse> {
const { data } = await apiClient.post<BatchApiKeysUsageResponse>('/admin/dashboard/api-keys-usage', {
api_key_ids: apiKeyIds,
});
return data;
}
export const dashboardAPI = {
getStats,
getRealtimeMetrics,
getUsageTrend,
getModelStats,
getApiKeyUsageTrend,
getUserUsageTrend,
getBatchUsersUsage,
getBatchApiKeysUsage,
};
export default dashboardAPI;
/**
* Admin Groups API endpoints
* Handles API key group management for administrators
*/
import { apiClient } from '../client';
import type {
Group,
GroupPlatform,
CreateGroupRequest,
UpdateGroupRequest,
PaginatedResponse,
} from '@/types';
/**
* List all groups with pagination
* @param page - Page number (default: 1)
* @param pageSize - Items per page (default: 20)
* @param filters - Optional filters (platform, status, is_exclusive)
* @returns Paginated list of groups
*/
export async function list(
page: number = 1,
pageSize: number = 20,
filters?: {
platform?: GroupPlatform;
status?: 'active' | 'inactive';
is_exclusive?: boolean;
}
): Promise<PaginatedResponse<Group>> {
const { data } = await apiClient.get<PaginatedResponse<Group>>('/admin/groups', {
params: {
page,
page_size: pageSize,
...filters,
},
});
return data;
}
/**
* Get all active groups (without pagination)
* @param platform - Optional platform filter
* @returns List of all active groups
*/
export async function getAll(platform?: GroupPlatform): Promise<Group[]> {
const { data } = await apiClient.get<Group[]>('/admin/groups/all', {
params: platform ? { platform } : undefined
});
return data;
}
/**
* Get active groups by platform
* @param platform - Platform to filter by
* @returns List of groups for the specified platform
*/
export async function getByPlatform(platform: GroupPlatform): Promise<Group[]> {
return getAll(platform);
}
/**
* Get group by ID
* @param id - Group ID
* @returns Group details
*/
export async function getById(id: number): Promise<Group> {
const { data } = await apiClient.get<Group>(`/admin/groups/${id}`);
return data;
}
/**
* Create new group
* @param groupData - Group data
* @returns Created group
*/
export async function create(groupData: CreateGroupRequest): Promise<Group> {
const { data } = await apiClient.post<Group>('/admin/groups', groupData);
return data;
}
/**
* Update group
* @param id - Group ID
* @param updates - Fields to update
* @returns Updated group
*/
export async function update(id: number, updates: UpdateGroupRequest): Promise<Group> {
const { data } = await apiClient.put<Group>(`/admin/groups/${id}`, updates);
return data;
}
/**
* Delete group
* @param id - Group ID
* @returns Success confirmation
*/
export async function deleteGroup(id: number): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(`/admin/groups/${id}`);
return data;
}
/**
* Toggle group status
* @param id - Group ID
* @param status - New status
* @returns Updated group
*/
export async function toggleStatus(
id: number,
status: 'active' | 'inactive'
): Promise<Group> {
return update(id, { status });
}
/**
* Get group statistics
* @param id - Group ID
* @returns Group usage statistics
*/
export async function getStats(id: number): Promise<{
total_api_keys: number;
active_api_keys: number;
total_requests: number;
total_cost: number;
}> {
const { data } = await apiClient.get<{
total_api_keys: number;
active_api_keys: number;
total_requests: number;
total_cost: number;
}>(`/admin/groups/${id}/stats`);
return data;
}
/**
* Get API keys in a group
* @param id - Group ID
* @param page - Page number
* @param pageSize - Items per page
* @returns Paginated list of API keys in the group
*/
export async function getGroupApiKeys(
id: number,
page: number = 1,
pageSize: number = 20
): Promise<PaginatedResponse<any>> {
const { data } = await apiClient.get<PaginatedResponse<any>>(
`/admin/groups/${id}/api-keys`,
{
params: { page, page_size: pageSize },
}
);
return data;
}
export const groupsAPI = {
list,
getAll,
getByPlatform,
getById,
create,
update,
delete: deleteGroup,
toggleStatus,
getStats,
getGroupApiKeys,
};
export default groupsAPI;
/**
* Admin API barrel export
* Centralized exports for all admin API modules
*/
import dashboardAPI from './dashboard';
import usersAPI from './users';
import groupsAPI from './groups';
import accountsAPI from './accounts';
import proxiesAPI from './proxies';
import redeemAPI from './redeem';
import settingsAPI from './settings';
import systemAPI from './system';
import subscriptionsAPI from './subscriptions';
import usageAPI from './usage';
/**
* Unified admin API object for convenient access
*/
export const adminAPI = {
dashboard: dashboardAPI,
users: usersAPI,
groups: groupsAPI,
accounts: accountsAPI,
proxies: proxiesAPI,
redeem: redeemAPI,
settings: settingsAPI,
system: systemAPI,
subscriptions: subscriptionsAPI,
usage: usageAPI,
};
export { dashboardAPI, usersAPI, groupsAPI, accountsAPI, proxiesAPI, redeemAPI, settingsAPI, systemAPI, subscriptionsAPI, usageAPI };
export default adminAPI;
/**
* Admin Proxies API endpoints
* Handles proxy server management for administrators
*/
import { apiClient } from '../client';
import type {
Proxy,
CreateProxyRequest,
UpdateProxyRequest,
PaginatedResponse,
} from '@/types';
/**
* List all proxies with pagination
* @param page - Page number (default: 1)
* @param pageSize - Items per page (default: 20)
* @param filters - Optional filters
* @returns Paginated list of proxies
*/
export async function list(
page: number = 1,
pageSize: number = 20,
filters?: {
protocol?: string;
status?: 'active' | 'inactive';
search?: string;
}
): Promise<PaginatedResponse<Proxy>> {
const { data } = await apiClient.get<PaginatedResponse<Proxy>>('/admin/proxies', {
params: {
page,
page_size: pageSize,
...filters,
},
});
return data;
}
/**
* Get all active proxies (without pagination)
* @returns List of all active proxies
*/
export async function getAll(): Promise<Proxy[]> {
const { data } = await apiClient.get<Proxy[]>('/admin/proxies/all');
return data;
}
/**
* Get all active proxies with account count (sorted by creation time desc)
* @returns List of all active proxies with account count
*/
export async function getAllWithCount(): Promise<Proxy[]> {
const { data } = await apiClient.get<Proxy[]>('/admin/proxies/all', {
params: { with_count: 'true' },
});
return data;
}
/**
* Get proxy by ID
* @param id - Proxy ID
* @returns Proxy details
*/
export async function getById(id: number): Promise<Proxy> {
const { data } = await apiClient.get<Proxy>(`/admin/proxies/${id}`);
return data;
}
/**
* Create new proxy
* @param proxyData - Proxy data
* @returns Created proxy
*/
export async function create(proxyData: CreateProxyRequest): Promise<Proxy> {
const { data } = await apiClient.post<Proxy>('/admin/proxies', proxyData);
return data;
}
/**
* Update proxy
* @param id - Proxy ID
* @param updates - Fields to update
* @returns Updated proxy
*/
export async function update(id: number, updates: UpdateProxyRequest): Promise<Proxy> {
const { data } = await apiClient.put<Proxy>(`/admin/proxies/${id}`, updates);
return data;
}
/**
* Delete proxy
* @param id - Proxy ID
* @returns Success confirmation
*/
export async function deleteProxy(id: number): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(`/admin/proxies/${id}`);
return data;
}
/**
* Toggle proxy status
* @param id - Proxy ID
* @param status - New status
* @returns Updated proxy
*/
export async function toggleStatus(
id: number,
status: 'active' | 'inactive'
): Promise<Proxy> {
return update(id, { status });
}
/**
* Test proxy connectivity
* @param id - Proxy ID
* @returns Test result with IP info
*/
export async function testProxy(id: number): Promise<{
success: boolean;
message: string;
latency_ms?: number;
ip_address?: string;
city?: string;
region?: string;
country?: string;
}> {
const { data } = await apiClient.post<{
success: boolean;
message: string;
latency_ms?: number;
ip_address?: string;
city?: string;
region?: string;
country?: string;
}>(`/admin/proxies/${id}/test`);
return data;
}
/**
* Get proxy usage statistics
* @param id - Proxy ID
* @returns Proxy usage statistics
*/
export async function getStats(id: number): Promise<{
total_accounts: number;
active_accounts: number;
total_requests: number;
success_rate: number;
average_latency: number;
}> {
const { data } = await apiClient.get<{
total_accounts: number;
active_accounts: number;
total_requests: number;
success_rate: number;
average_latency: number;
}>(`/admin/proxies/${id}/stats`);
return data;
}
/**
* Get accounts using a proxy
* @param id - Proxy ID
* @returns List of accounts using the proxy
*/
export async function getProxyAccounts(id: number): Promise<PaginatedResponse<any>> {
const { data } = await apiClient.get<PaginatedResponse<any>>(
`/admin/proxies/${id}/accounts`
);
return data;
}
/**
* Batch create proxies
* @param proxies - Array of proxy data to create
* @returns Creation result with count of created and skipped
*/
export async function batchCreate(proxies: Array<{
protocol: string;
host: string;
port: number;
username?: string;
password?: string;
}>): Promise<{
created: number;
skipped: number;
}> {
const { data } = await apiClient.post<{
created: number;
skipped: number;
}>('/admin/proxies/batch', { proxies });
return data;
}
export const proxiesAPI = {
list,
getAll,
getAllWithCount,
getById,
create,
update,
delete: deleteProxy,
toggleStatus,
testProxy,
getStats,
getProxyAccounts,
batchCreate,
};
export default proxiesAPI;
/**
* Admin Redeem Codes API endpoints
* Handles redeem code generation and management for administrators
*/
import { apiClient } from '../client';
import type {
RedeemCode,
GenerateRedeemCodesRequest,
RedeemCodeType,
PaginatedResponse,
} from '@/types';
/**
* List all redeem codes with pagination
* @param page - Page number (default: 1)
* @param pageSize - Items per page (default: 20)
* @param filters - Optional filters
* @returns Paginated list of redeem codes
*/
export async function list(
page: number = 1,
pageSize: number = 20,
filters?: {
type?: RedeemCodeType;
status?: 'active' | 'used' | 'expired';
search?: string;
}
): Promise<PaginatedResponse<RedeemCode>> {
const { data } = await apiClient.get<PaginatedResponse<RedeemCode>>('/admin/redeem-codes', {
params: {
page,
page_size: pageSize,
...filters,
},
});
return data;
}
/**
* Get redeem code by ID
* @param id - Redeem code ID
* @returns Redeem code details
*/
export async function getById(id: number): Promise<RedeemCode> {
const { data } = await apiClient.get<RedeemCode>(`/admin/redeem-codes/${id}`);
return data;
}
/**
* Generate new redeem codes
* @param count - Number of codes to generate
* @param type - Type of redeem code
* @param value - Value of the code
* @param groupId - Group ID (required for subscription type)
* @param validityDays - Validity days (for subscription type)
* @returns Array of generated redeem codes
*/
export async function generate(
count: number,
type: RedeemCodeType,
value: number,
groupId?: number | null,
validityDays?: number
): Promise<RedeemCode[]> {
const payload: GenerateRedeemCodesRequest = {
count,
type,
value,
};
// 订阅类型专用字段
if (type === 'subscription') {
payload.group_id = groupId;
if (validityDays && validityDays > 0) {
payload.validity_days = validityDays;
}
}
const { data } = await apiClient.post<RedeemCode[]>('/admin/redeem-codes/generate', payload);
return data;
}
/**
* Delete redeem code
* @param id - Redeem code ID
* @returns Success confirmation
*/
export async function deleteCode(id: number): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(`/admin/redeem-codes/${id}`);
return data;
}
/**
* Batch delete redeem codes
* @param ids - Array of redeem code IDs
* @returns Success confirmation
*/
export async function batchDelete(ids: number[]): Promise<{
deleted: number;
message: string;
}> {
const { data } = await apiClient.post<{
deleted: number;
message: string;
}>('/admin/redeem-codes/batch-delete', { ids });
return data;
}
/**
* Expire redeem code
* @param id - Redeem code ID
* @returns Updated redeem code
*/
export async function expire(id: number): Promise<RedeemCode> {
const { data } = await apiClient.post<RedeemCode>(`/admin/redeem-codes/${id}/expire`);
return data;
}
/**
* Get redeem code statistics
* @returns Statistics about redeem codes
*/
export async function getStats(): Promise<{
total_codes: number;
active_codes: number;
used_codes: number;
expired_codes: number;
total_value_distributed: number;
by_type: Record<RedeemCodeType, number>;
}> {
const { data } = await apiClient.get<{
total_codes: number;
active_codes: number;
used_codes: number;
expired_codes: number;
total_value_distributed: number;
by_type: Record<RedeemCodeType, number>;
}>('/admin/redeem-codes/stats');
return data;
}
/**
* Export redeem codes to CSV
* @param filters - Optional filters
* @returns CSV data as blob
*/
export async function exportCodes(filters?: {
type?: RedeemCodeType;
status?: 'active' | 'used' | 'expired';
}): Promise<Blob> {
const response = await apiClient.get('/admin/redeem-codes/export', {
params: filters,
responseType: 'blob',
});
return response.data;
}
export const redeemAPI = {
list,
getById,
generate,
delete: deleteCode,
batchDelete,
expire,
getStats,
exportCodes,
};
export default redeemAPI;
/**
* Admin Settings API endpoints
* Handles system settings management for administrators
*/
import { apiClient } from '../client';
/**
* System settings interface
*/
export interface SystemSettings {
// Registration settings
registration_enabled: boolean;
email_verify_enabled: boolean;
// Default settings
default_balance: number;
default_concurrency: number;
// OEM settings
site_name: string;
site_logo: string;
site_subtitle: string;
api_base_url: string;
contact_info: string;
// SMTP settings
smtp_host: string;
smtp_port: number;
smtp_username: string;
smtp_password: string;
smtp_from_email: string;
smtp_from_name: string;
smtp_use_tls: boolean;
// Cloudflare Turnstile settings
turnstile_enabled: boolean;
turnstile_site_key: string;
turnstile_secret_key: string;
}
/**
* Get all system settings
* @returns System settings
*/
export async function getSettings(): Promise<SystemSettings> {
const { data } = await apiClient.get<SystemSettings>('/admin/settings');
return data;
}
/**
* Update system settings
* @param settings - Partial settings to update
* @returns Updated settings
*/
export async function updateSettings(settings: Partial<SystemSettings>): Promise<SystemSettings> {
const { data } = await apiClient.put<SystemSettings>('/admin/settings', settings);
return data;
}
/**
* Test SMTP connection request
*/
export interface TestSmtpRequest {
smtp_host: string;
smtp_port: number;
smtp_username: string;
smtp_password: string;
smtp_use_tls: boolean;
}
/**
* Test SMTP connection with provided config
* @param config - SMTP configuration to test
* @returns Test result message
*/
export async function testSmtpConnection(config: TestSmtpRequest): Promise<{ message: string }> {
const { data } = await apiClient.post<{ message: string }>('/admin/settings/test-smtp', config);
return data;
}
/**
* Send test email request
*/
export interface SendTestEmailRequest {
email: string;
smtp_host: string;
smtp_port: number;
smtp_username: string;
smtp_password: string;
smtp_from_email: string;
smtp_from_name: string;
smtp_use_tls: boolean;
}
/**
* Send test email with provided SMTP config
* @param request - Email address and SMTP config
* @returns Test result message
*/
export async function sendTestEmail(request: SendTestEmailRequest): Promise<{ message: string }> {
const { data } = await apiClient.post<{ message: string }>('/admin/settings/send-test-email', request);
return data;
}
export const settingsAPI = {
getSettings,
updateSettings,
testSmtpConnection,
sendTestEmail,
};
export default settingsAPI;
/**
* Admin Subscriptions API endpoints
* Handles user subscription management for administrators
*/
import { apiClient } from '../client';
import type {
UserSubscription,
SubscriptionProgress,
AssignSubscriptionRequest,
BulkAssignSubscriptionRequest,
ExtendSubscriptionRequest,
PaginatedResponse,
} from '@/types';
/**
* List all subscriptions with pagination
* @param page - Page number (default: 1)
* @param pageSize - Items per page (default: 20)
* @param filters - Optional filters (status, user_id, group_id)
* @returns Paginated list of subscriptions
*/
export async function list(
page: number = 1,
pageSize: number = 20,
filters?: {
status?: 'active' | 'expired' | 'revoked';
user_id?: number;
group_id?: number;
}
): Promise<PaginatedResponse<UserSubscription>> {
const { data } = await apiClient.get<PaginatedResponse<UserSubscription>>('/admin/subscriptions', {
params: {
page,
page_size: pageSize,
...filters,
},
});
return data;
}
/**
* Get subscription by ID
* @param id - Subscription ID
* @returns Subscription details
*/
export async function getById(id: number): Promise<UserSubscription> {
const { data } = await apiClient.get<UserSubscription>(`/admin/subscriptions/${id}`);
return data;
}
/**
* Get subscription progress
* @param id - Subscription ID
* @returns Subscription progress with usage stats
*/
export async function getProgress(id: number): Promise<SubscriptionProgress> {
const { data } = await apiClient.get<SubscriptionProgress>(`/admin/subscriptions/${id}/progress`);
return data;
}
/**
* Assign subscription to user
* @param request - Assignment request
* @returns Created subscription
*/
export async function assign(request: AssignSubscriptionRequest): Promise<UserSubscription> {
const { data } = await apiClient.post<UserSubscription>('/admin/subscriptions/assign', request);
return data;
}
/**
* Bulk assign subscriptions to multiple users
* @param request - Bulk assignment request
* @returns Created subscriptions
*/
export async function bulkAssign(request: BulkAssignSubscriptionRequest): Promise<UserSubscription[]> {
const { data } = await apiClient.post<UserSubscription[]>('/admin/subscriptions/bulk-assign', request);
return data;
}
/**
* Extend subscription validity
* @param id - Subscription ID
* @param request - Extension request with days
* @returns Updated subscription
*/
export async function extend(id: number, request: ExtendSubscriptionRequest): Promise<UserSubscription> {
const { data } = await apiClient.post<UserSubscription>(`/admin/subscriptions/${id}/extend`, request);
return data;
}
/**
* Revoke subscription
* @param id - Subscription ID
* @returns Success confirmation
*/
export async function revoke(id: number): Promise<{ message: string }> {
const { data } = await apiClient.delete<{ message: string }>(`/admin/subscriptions/${id}`);
return data;
}
/**
* List subscriptions by group
* @param groupId - Group ID
* @param page - Page number
* @param pageSize - Items per page
* @returns Paginated list of subscriptions in the group
*/
export async function listByGroup(
groupId: number,
page: number = 1,
pageSize: number = 20
): Promise<PaginatedResponse<UserSubscription>> {
const { data } = await apiClient.get<PaginatedResponse<UserSubscription>>(
`/admin/groups/${groupId}/subscriptions`,
{
params: { page, page_size: pageSize },
}
);
return data;
}
/**
* List subscriptions by user
* @param userId - User ID
* @param page - Page number
* @param pageSize - Items per page
* @returns Paginated list of user's subscriptions
*/
export async function listByUser(
userId: number,
page: number = 1,
pageSize: number = 20
): Promise<PaginatedResponse<UserSubscription>> {
const { data } = await apiClient.get<PaginatedResponse<UserSubscription>>(
`/admin/users/${userId}/subscriptions`,
{
params: { page, page_size: pageSize },
}
);
return data;
}
export const subscriptionsAPI = {
list,
getById,
getProgress,
assign,
bulkAssign,
extend,
revoke,
listByGroup,
listByUser,
};
export default subscriptionsAPI;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment