commit 2c6a38c80dc15102fbeab58e4140a79c40a7c0f7 Author: CN-JS-HuiBai Date: Tue Apr 7 16:54:24 2026 +0800 first commit diff --git a/Xboard/.docker/.data/.gitignore b/Xboard/.docker/.data/.gitignore new file mode 100644 index 0000000..0377233 --- /dev/null +++ b/Xboard/.docker/.data/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!redis \ No newline at end of file diff --git a/Xboard/.docker/.data/redis/.gitignore b/Xboard/.docker/.data/redis/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/Xboard/.docker/.data/redis/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/Xboard/.docker/supervisor/supervisord.conf b/Xboard/.docker/supervisor/supervisord.conf new file mode 100644 index 0000000..c516e4e --- /dev/null +++ b/Xboard/.docker/supervisor/supervisord.conf @@ -0,0 +1,81 @@ +[supervisord] +nodaemon=true +user=root +logfile=/dev/stdout +logfile_maxbytes=0 +pidfil=/www/storage/logs/supervisor/supervisord.pid +loglevel=info + +[program:octane] +process_name=%(program_name)s_%(process_num)02d +command=php /www/artisan octane:start --host=0.0.0.0 --port=7001 +autostart=%(ENV_ENABLE_WEB)s +autorestart=true +user=www +redirect_stderr=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +numprocs=1 +stopwaitsecs=10 +stopsignal=QUIT +stopasgroup=true +killasgroup=true +priority=100 + +[program:horizon] +process_name=%(program_name)s_%(process_num)02d +command=php /www/artisan horizon +autostart=%(ENV_ENABLE_HORIZON)s +autorestart=true +user=www +redirect_stderr=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +numprocs=1 +stopwaitsecs=3 +stopsignal=SIGINT +stopasgroup=true +killasgroup=true +priority=200 + +[program:redis] +process_name=%(program_name)s_%(process_num)02d +command=redis-server --dir /data + --dbfilename dump.rdb + --save 900 1 + --save 300 10 + --save 60 10000 + --unixsocket /data/redis.sock + --unixsocketperm 777 +autostart=%(ENV_ENABLE_REDIS)s +autorestart=true +user=redis +redirect_stderr=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +numprocs=1 +stopwaitsecs=3 +stopsignal=TERM +stopasgroup=true +killasgroup=true +priority=300 + +[program:ws-server] +process_name=%(program_name)s_%(process_num)02d +command=php /www/artisan ws-server start +autostart=%(ENV_ENABLE_WS_SERVER)s +autorestart=true +user=www +redirect_stderr=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +numprocs=1 +stopwaitsecs=5 +stopsignal=SIGINT +stopasgroup=true +killasgroup=true +priority=400 \ No newline at end of file diff --git a/Xboard/.dockerignore b/Xboard/.dockerignore new file mode 100644 index 0000000..3b0ee11 --- /dev/null +++ b/Xboard/.dockerignore @@ -0,0 +1,26 @@ +/node_modules +/config/v2board.php +/public/hot +/public/storage +/public/env.example.js +/storage/*.key +/vendor +.env +.env.backup +.phpunit.result.cache +.idea +.lock +Homestead.json +Homestead.yaml +npm-debug.log +yarn-error.log +composer.phar +composer.lock +yarn.lock +docker-compose.yml +.DS_Store +/docker +storage/laravels.conf +storage/laravels.pid +storage/laravels-timer-process.pid +/frontend diff --git a/Xboard/.editorconfig b/Xboard/.editorconfig new file mode 100644 index 0000000..6537ca4 --- /dev/null +++ b/Xboard/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 diff --git a/Xboard/.env.example b/Xboard/.env.example new file mode 100644 index 0000000..251858b --- /dev/null +++ b/Xboard/.env.example @@ -0,0 +1,41 @@ +APP_NAME=XBoard +APP_ENV=production +APP_KEY=base64:PZXk5vTuTinfeEVG5FpYv2l6WEhLsyvGpiWK7IgJJ60= +APP_DEBUG=false +APP_URL=http://localhost + +LOG_CHANNEL=stack + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=xboard +DB_USERNAME=root +DB_PASSWORD= + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +BROADCAST_DRIVER=log +CACHE_DRIVER=redis +QUEUE_CONNECTION=redis + +MAIL_DRIVER=smtp +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS=null +MAIL_FROM_NAME=null +MAILGUN_DOMAIN= +MAILGUN_SECRET= + +# google cloud storage +ENABLE_AUTO_BACKUP_AND_UPDATE=false +GOOGLE_CLOUD_KEY_FILE=config/googleCloudStorageKey.json +GOOGLE_CLOUD_STORAGE_BUCKET= + +# Prevent reinstallation +INSTALLED=false \ No newline at end of file diff --git a/Xboard/.gitattributes b/Xboard/.gitattributes new file mode 100644 index 0000000..967315d --- /dev/null +++ b/Xboard/.gitattributes @@ -0,0 +1,5 @@ +* text=auto +*.css linguist-vendored +*.scss linguist-vendored +*.js linguist-vendored +CHANGELOG.md export-ignore diff --git a/Xboard/.github/ISSUE_TEMPLATE/bug-report.md b/Xboard/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..271b8df --- /dev/null +++ b/Xboard/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,39 @@ +--- +name: 🐛 问题反馈 | Bug Report +about: 提交使用过程中遇到的问题 | Report an issue +title: "Bug Report:" +labels: '🐛 bug' +assignees: '' +--- + + + + +> ⚠️ 请务必按照模板填写完整信息,没有详细描述的issue可能会被忽略或关闭 +> ⚠️ Please follow the template to provide complete information, issues without detailed description may be ignored or closed + +**基本信息 | Basic Info** +```yaml +XBoard版本 | Version: +部署方式 | Deployment: [Docker/手动部署] +PHP版本 | Version: +数据库 | Database: +``` + +**问题描述 | Description** + + + +**复现步骤 | Steps** + +1. +2. + +**相关截图 | Screenshots** + + +**日志信息 | Logs** + +```log +// 粘贴日志内容到这里 +``` \ No newline at end of file diff --git a/Xboard/.github/ISSUE_TEMPLATE/feature-request.md b/Xboard/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..5428f95 --- /dev/null +++ b/Xboard/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,28 @@ +--- +name: ✨ 功能请求 | Feature Request +about: 提交新功能建议或改进意见 | Suggest an idea +title: "Feature Request:" +labels: '✨ enhancement' +assignees: '' +--- + +> ⚠️ 请务必按照模板详细描述你的需求,没有详细描述的issue可能会被忽略或关闭 +> ⚠️ Please follow the template to describe your request in detail, issues without detailed description may be ignored or closed + +**需求描述 | Description** + + + +**使用场景 | Use Case** + + + +**功能建议 | Suggestion** + +```yaml +功能形式 | Type: [新功能/功能优化/界面改进] +预期效果 | Expected: +``` + +**补充说明 | Additional** + \ No newline at end of file diff --git a/Xboard/.github/workflows/docker-publish.yml b/Xboard/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..9898924 --- /dev/null +++ b/Xboard/.github/workflows/docker-publish.yml @@ -0,0 +1,100 @@ +name: Docker Build and Publish + +on: + push: + branches: ["master", "new-dev"] + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: 'arm64,amd64' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: linux/amd64,linux/arm64 + driver-opts: | + image=moby/buildkit:v0.20.0 + network=host + + - name: Free Disk Space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune -af + + - name: Login to registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=sha,format=short,prefix=,enable=true + type=raw,value=new,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=${{ steps.get_version.outputs.version }} + labels: | + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + + - name: Get version + id: get_version + run: echo "version=$(git describe --tags --always)" >> $GITHUB_OUTPUT + + - name: Update version in app.php + run: | + VERSION=$(date '+%Y%m%d')-$(git rev-parse --short HEAD) + sed -i "s/'version' => '.*'/'version' => '$VERSION'/g" config/app.php + echo "Updated version to: $VERSION" + + - name: Build and push + id: build-and-push + uses: docker/build-push-action@v5 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + BUILDKIT_INLINE_CACHE=1 + BUILDKIT_MULTI_PLATFORM=1 + CACHEBUST=${{ github.sha }} + REPO_URL=https://github.com/${{ github.repository }} + BRANCH_NAME=${{ github.ref_name }} + provenance: false + outputs: type=registry,push=true + allow: | + network.host + diff --git a/Xboard/.gitignore b/Xboard/.gitignore new file mode 100644 index 0000000..2715539 --- /dev/null +++ b/Xboard/.gitignore @@ -0,0 +1,34 @@ +/node_modules +/config/v2board.php +/config/googleCloudStorageKey.json +/public/hot +/public/storage +/public/env.example.js +*.user.ini +/storage/*.key +/vendor +.env +.env.backup +.phpunit.result.cache +.idea +.lock +Homestead.json +Homestead.yaml +npm-debug.log +yarn-error.log +composer.phar +composer.lock +yarn.lock +docker-compose.yml +.DS_Store +/docker +storage/laravels.conf +storage/laravels.pid +storage/update_pending +storage/laravels-timer-process.pid +cli-php.ini +frontend +docker-compose.yaml +bun.lockb +compose.yaml +.scribe \ No newline at end of file diff --git a/Xboard/.gitmodules b/Xboard/.gitmodules new file mode 100644 index 0000000..060ba10 --- /dev/null +++ b/Xboard/.gitmodules @@ -0,0 +1,3 @@ +[submodule "public/assets/admin"] + path = public/assets/admin + url = https://github.com/cedar2025/xboard-admin-dist.git diff --git a/Xboard/Dockerfile b/Xboard/Dockerfile new file mode 100644 index 0000000..ba41fe1 --- /dev/null +++ b/Xboard/Dockerfile @@ -0,0 +1,47 @@ +FROM phpswoole/swoole:php8.2-alpine + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ + +# Install PHP extensions one by one with lower optimization level for ARM64 compatibility +RUN CFLAGS="-O0" install-php-extensions pcntl && \ + CFLAGS="-O0 -g0" install-php-extensions bcmath && \ + install-php-extensions zip && \ + install-php-extensions redis && \ + apk --no-cache add shadow sqlite mysql-client mysql-dev mariadb-connector-c git patch supervisor redis && \ + addgroup -S -g 1000 www && adduser -S -G www -u 1000 www && \ + (getent group redis || addgroup -S redis) && \ + (getent passwd redis || adduser -S -G redis -H -h /data redis) + +WORKDIR /www + +COPY .docker / + +# Add build arguments +ARG CACHEBUST +ARG REPO_URL +ARG BRANCH_NAME + +RUN echo "Attempting to clone branch: ${BRANCH_NAME} from ${REPO_URL} with CACHEBUST: ${CACHEBUST}" && \ + rm -rf ./* && \ + rm -rf .git && \ + git config --global --add safe.directory /www && \ + git clone --depth 1 --branch ${BRANCH_NAME} ${REPO_URL} . && \ + git submodule update --init --recursive --force + +COPY .docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +RUN composer install --no-cache --no-dev \ + && php artisan storage:link \ + && cp -r plugins/ /opt/default-plugins/ \ + && chown -R www:www /www \ + && chmod -R 775 /www \ + && mkdir -p /data \ + && chown redis:redis /data + +ENV ENABLE_WEB=true \ + ENABLE_HORIZON=true \ + ENABLE_REDIS=false \ + ENABLE_WS_SERVER=false + +EXPOSE 7001 +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/Xboard/LICENSE b/Xboard/LICENSE new file mode 100644 index 0000000..8d23873 --- /dev/null +++ b/Xboard/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Tokumeikoi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Xboard/README.md b/Xboard/README.md new file mode 100644 index 0000000..4d6a618 --- /dev/null +++ b/Xboard/README.md @@ -0,0 +1,100 @@ +# Xboard + +
+ +[![Telegram](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/XboardOfficial) +![PHP](https://img.shields.io/badge/PHP-8.2+-green.svg) +![MySQL](https://img.shields.io/badge/MySQL-5.7+-blue.svg) +[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +
+ +## 📖 Introduction + +Xboard is a modern panel system built on Laravel 11, focusing on providing a clean and efficient user experience. + +## ✨ Features + +- 🚀 Built with Laravel 12 + Octane for significant performance gains +- 🎨 Redesigned admin interface (React + Shadcn UI) +- 📱 Modern user frontend (Vue3 + TypeScript) +- 🐳 Ready-to-use Docker deployment solution +- 🎯 Optimized system architecture for better maintainability + +## 🚀 Quick Start + +```bash +git clone -b compose --depth 1 https://github.com/cedar2025/Xboard && \ +cd Xboard && \ +docker compose run -it --rm \ + -e ENABLE_SQLITE=true \ + -e ENABLE_REDIS=true \ + -e ADMIN_ACCOUNT=admin@demo.com \ + web php artisan xboard:install && \ +docker compose up -d +``` + +> After installation, visit: http://SERVER_IP:7001 +> ⚠️ Make sure to save the admin credentials shown during installation + +## 📖 Documentation + +### 🔄 Upgrade Notice +> 🚨 **Important:** This version involves significant changes. Please strictly follow the upgrade documentation and backup your database before upgrading. Note that upgrading and migration are different processes, do not confuse them. + +### Development Guides +- [Plugin Development Guide](./docs/en/development/plugin-development-guide.md) - Complete guide for developing XBoard plugins + +### Deployment Guides +- [Deploy with 1Panel](./docs/en/installation/1panel.md) +- [Deploy with Docker Compose](./docs/en/installation/docker-compose.md) +- [Deploy with aaPanel](./docs/en/installation/aapanel.md) +- [Deploy with aaPanel + Docker](./docs/en/installation/aapanel-docker.md) (Recommended) + +### Migration Guides +- [Migrate from v2board dev](./docs/en/migration/v2board-dev.md) +- [Migrate from v2board 1.7.4](./docs/en/migration/v2board-1.7.4.md) +- [Migrate from v2board 1.7.3](./docs/en/migration/v2board-1.7.3.md) + +## 🛠️ Tech Stack + +- Backend: Laravel 11 + Octane +- Admin Panel: React + Shadcn UI + TailwindCSS +- User Frontend: Vue3 + TypeScript + NaiveUI +- Deployment: Docker + Docker Compose +- Caching: Redis + Octane Cache + +## 📷 Preview +![Admin Preview](./docs/images/admin.png) + +![User Preview](./docs/images/user.png) + +## ⚠️ Disclaimer + +This project is for learning and communication purposes only. Users are responsible for any consequences of using this project. + +## 🌟 Maintenance Notice + +This project is currently under light maintenance. We will: +- Fix critical bugs and security issues +- Review and merge important pull requests +- Provide necessary updates for compatibility + +However, new feature development may be limited. + +## 🔔 Important Notes + +1. Restart required after modifying admin path: +```bash +docker compose restart +``` + +2. For aaPanel installations, restart the Octane daemon process + +## 🤝 Contributing + +Issues and Pull Requests are welcome to help improve the project. + +## 📈 Star History + +[![Stargazers over time](https://starchart.cc/cedar2025/Xboard.svg)](https://starchart.cc/cedar2025/Xboard) diff --git a/Xboard/app/Console/Commands/BackupDatabase.php b/Xboard/app/Console/Commands/BackupDatabase.php new file mode 100644 index 0000000..14c82ee --- /dev/null +++ b/Xboard/app/Console/Commands/BackupDatabase.php @@ -0,0 +1,100 @@ +argument('upload'); + // 如果是上传到云端则判断是否存在必要配置 + if($isUpload){ + $requiredConfigs = ['database.connections.mysql', 'cloud_storage.google_cloud.key_file', 'cloud_storage.google_cloud.storage_bucket']; + foreach ($requiredConfigs as $config) { + if (blank(config($config))) { + $this->error("❌:缺少必要配置项: $config , 取消备份"); + return; + } + } + } + + // 数据库备份逻辑 + try{ + if (config('database.default') === 'mysql'){ + $databaseBackupPath = storage_path('backup/' . now()->format('Y-m-d_H-i-s') . '_' . config('database.connections.mysql.database') . '_database_backup.sql'); + $this->info("1️⃣:开始备份Mysql"); + \Spatie\DbDumper\Databases\MySql::create() + ->setHost(config('database.connections.mysql.host')) + ->setPort(config('database.connections.mysql.port')) + ->setDbName(config('database.connections.mysql.database')) + ->setUserName(config('database.connections.mysql.username')) + ->setPassword(config('database.connections.mysql.password')) + ->dumpToFile($databaseBackupPath); + $this->info("2️⃣:Mysql备份完成"); + }elseif(config('database.default') === 'sqlite'){ + $databaseBackupPath = storage_path('backup/' . now()->format('Y-m-d_H-i-s') . '_sqlite' . '_database_backup.sql'); + $this->info("1️⃣:开始备份Sqlite"); + \Spatie\DbDumper\Databases\Sqlite::create() + ->setDbName(config('database.connections.sqlite.database')) + ->dumpToFile($databaseBackupPath); + $this->info("2️⃣:Sqlite备份完成"); + }else{ + $this->error('备份失败,你的数据库不是sqlite或者mysql'); + return; + } + $this->info('3️⃣:开始压缩备份文件'); + // 使用 gzip 压缩备份文件 + $compressedBackupPath = $databaseBackupPath . '.gz'; + $gzipCommand = new Process(["gzip", "-c", $databaseBackupPath]); + $gzipCommand->run(); + + // 检查压缩是否成功 + if ($gzipCommand->isSuccessful()) { + // 压缩成功,你可以删除原始备份文件 + file_put_contents($compressedBackupPath, $gzipCommand->getOutput()); + $this->info('4️⃣:文件压缩成功'); + unlink($databaseBackupPath); + } else { + // 压缩失败,处理错误 + echo $gzipCommand->getErrorOutput(); + $this->error('😔:文件压缩失败'); + unlink($databaseBackupPath); + return; + } + if (!$isUpload){ + $this->info("🎉:数据库成功备份到:$compressedBackupPath"); + }else{ + // 传到云盘 + $this->info("5️⃣:开始将备份上传到Google Cloud"); + // Google Cloud Storage 配置 + $storage = new StorageClient([ + 'keyFilePath' => config('cloud_storage.google_cloud.key_file'), + ]); + $bucket = $storage->bucket(config('cloud_storage.google_cloud.storage_bucket')); + $objectName = 'backup/' . now()->format('Y-m-d_H-i-s') . '_database_backup.sql.gz'; + // 上传文件 + $bucket->upload(fopen($compressedBackupPath, 'r'), [ + 'name' => $objectName, + ]); + + // 输出文件链接 + Log::channel('backup')->info("🎉:数据库备份已上传到 Google Cloud Storage: $objectName"); + $this->info("🎉:数据库备份已上传到 Google Cloud Storage: $objectName"); + File::delete($compressedBackupPath); + } + }catch(\Exception $e){ + Log::channel('backup')->error("😔:数据库备份失败 \n" . $e); + $this->error("😔:数据库备份失败\n" . $e); + File::delete($compressedBackupPath); + } + } +} diff --git a/Xboard/app/Console/Commands/CheckCommission.php b/Xboard/app/Console/Commands/CheckCommission.php new file mode 100644 index 0000000..0eabc74 --- /dev/null +++ b/Xboard/app/Console/Commands/CheckCommission.php @@ -0,0 +1,129 @@ +autoCheck(); + $this->autoPayCommission(); + } + + public function autoCheck() + { + if ((int)admin_setting('commission_auto_check_enable', 1)) { + Order::where('commission_status', 0) + ->where('invite_user_id', '!=', NULL) + ->where('status', 3) + ->where('updated_at', '<=', strtotime('-3 day', time())) + ->update([ + 'commission_status' => 1 + ]); + } + } + + public function autoPayCommission() + { + $orders = Order::where('commission_status', 1) + ->where('invite_user_id', '!=', NULL) + ->get(); + foreach ($orders as $order) { + try{ + DB::beginTransaction(); + if (!$this->payHandle($order->invite_user_id, $order)) { + DB::rollBack(); + continue; + } + $order->commission_status = 2; + if (!$order->save()) { + DB::rollBack(); + continue; + } + DB::commit(); + } catch (\Exception $e){ + DB::rollBack(); + throw $e; + } + } + } + + public function payHandle($inviteUserId, Order $order) + { + $level = 3; + if ((int)admin_setting('commission_distribution_enable', 0)) { + $commissionShareLevels = [ + 0 => (int)admin_setting('commission_distribution_l1'), + 1 => (int)admin_setting('commission_distribution_l2'), + 2 => (int)admin_setting('commission_distribution_l3') + ]; + } else { + $commissionShareLevels = [ + 0 => 100 + ]; + } + for ($l = 0; $l < $level; $l++) { + $inviter = User::find($inviteUserId); + if (!$inviter) continue; + if (!isset($commissionShareLevels[$l])) continue; + $commissionBalance = $order->commission_balance * ($commissionShareLevels[$l] / 100); + if (!$commissionBalance) continue; + if ((int)admin_setting('withdraw_close_enable', 0)) { + $inviter->increment('balance', $commissionBalance); + } else { + $inviter->increment('commission_balance', $commissionBalance); + } + if (!$inviter->save()) { + DB::rollBack(); + return false; + } + CommissionLog::create([ + 'invite_user_id' => $inviteUserId, + 'user_id' => $order->user_id, + 'trade_no' => $order->trade_no, + 'order_amount' => $order->total_amount, + 'get_amount' => $commissionBalance + ]); + $inviteUserId = $inviter->invite_user_id; + // update order actual commission balance + $order->actual_commission_balance = $order->actual_commission_balance + $commissionBalance; + } + return true; + } + +} diff --git a/Xboard/app/Console/Commands/CheckOrder.php b/Xboard/app/Console/Commands/CheckOrder.php new file mode 100644 index 0000000..7d03f58 --- /dev/null +++ b/Xboard/app/Console/Commands/CheckOrder.php @@ -0,0 +1,53 @@ +orderBy('created_at', 'ASC') + ->lazyById(200) + ->each(function ($order) { + OrderHandleJob::dispatch($order->trade_no); + }); + } +} diff --git a/Xboard/app/Console/Commands/CheckServer.php b/Xboard/app/Console/Commands/CheckServer.php new file mode 100644 index 0000000..2d3dae9 --- /dev/null +++ b/Xboard/app/Console/Commands/CheckServer.php @@ -0,0 +1,64 @@ +checkOffline(); + } + + private function checkOffline() + { + $servers = ServerService::getAllServers(); + foreach ($servers as $server) { + if ($server['parent_id']) continue; + if ($server['last_check_at'] && (time() - $server['last_check_at']) > 1800) { + $telegramService = new TelegramService(); + $message = sprintf( + "节点掉线通知\r\n----\r\n节点名称:%s\r\n节点地址:%s\r\n", + $server['name'], + $server['host'] + ); + $telegramService->sendMessageWithAdmin($message); + Cache::forget(CacheKey::get(sprintf("SERVER_%s_LAST_CHECK_AT", strtoupper($server['type'])), $server->id)); + } + } + } +} diff --git a/Xboard/app/Console/Commands/CheckTicket.php b/Xboard/app/Console/Commands/CheckTicket.php new file mode 100644 index 0000000..51d4539 --- /dev/null +++ b/Xboard/app/Console/Commands/CheckTicket.php @@ -0,0 +1,51 @@ +where('updated_at', '<=', time() - 24 * 3600) + ->where('reply_status', 0) + ->lazyById(200) + ->each(function ($ticket) { + if ($ticket->user_id === $ticket->last_reply_user_id) return; + $ticket->status = Ticket::STATUS_CLOSED; + $ticket->save(); + }); + } +} diff --git a/Xboard/app/Console/Commands/CheckTrafficExceeded.php b/Xboard/app/Console/Commands/CheckTrafficExceeded.php new file mode 100644 index 0000000..35a1db6 --- /dev/null +++ b/Xboard/app/Console/Commands/CheckTrafficExceeded.php @@ -0,0 +1,63 @@ +whereIn('id', $pendingUserIds) + ->whereRaw('u + d >= transfer_enable') + ->where('transfer_enable', '>', 0) + ->where('banned', 0) + ->select(['id', 'group_id']) + ->get(); + + if ($exceededUsers->isEmpty()) { + return; + } + + $groupedUsers = $exceededUsers->groupBy('group_id'); + $notifiedCount = 0; + + foreach ($groupedUsers as $groupId => $users) { + if (!$groupId) { + continue; + } + + $userIdsInGroup = $users->pluck('id')->toArray(); + $servers = Server::whereJsonContains('group_ids', (string) $groupId)->get(); + + foreach ($servers as $server) { + if (!NodeSyncService::isNodeOnline($server->id)) { + continue; + } + + NodeSyncService::push($server->id, 'sync.user.delta', [ + 'action' => 'remove', + 'users' => array_map(fn($id) => ['id' => $id], $userIdsInGroup), + ]); + $notifiedCount++; + } + } + + $this->info("Checked " . count($pendingUserIds) . " users, notified {$notifiedCount} nodes for " . $exceededUsers->count() . " exceeded users."); + } +} diff --git a/Xboard/app/Console/Commands/ClearUser.php b/Xboard/app/Console/Commands/ClearUser.php new file mode 100644 index 0000000..cd64abe --- /dev/null +++ b/Xboard/app/Console/Commands/ClearUser.php @@ -0,0 +1,51 @@ +where('transfer_enable', 0) + ->where('expired_at', 0) + ->where('last_login_at', NULL); + $count = $builder->count(); + if ($builder->delete()) { + $this->info("已删除{$count}位没有任何数据的用户"); + } + } +} diff --git a/Xboard/app/Console/Commands/HookList.php b/Xboard/app/Console/Commands/HookList.php new file mode 100644 index 0000000..cec2bfd --- /dev/null +++ b/Xboard/app/Console/Commands/HookList.php @@ -0,0 +1,42 @@ +filter(fn($f) => Str::endsWith($f, '.php')); + foreach ($files as $file) { + $content = @file_get_contents($file); + if ($content && preg_match_all($pattern, $content, $matches)) { + foreach ($matches[2] as $hook) { + $hooks->push($hook); + } + } + } + } + $hooks = $hooks->unique()->sort()->values(); + if ($hooks->isEmpty()) { + $this->info('未扫描到任何 hook'); + } else { + $this->info('All Supported Hooks:'); + foreach ($hooks as $hook) { + $this->line(' ' . $hook); + } + } + } +} \ No newline at end of file diff --git a/Xboard/app/Console/Commands/MigrateFromV2b.php b/Xboard/app/Console/Commands/MigrateFromV2b.php new file mode 100644 index 0000000..ace9c85 --- /dev/null +++ b/Xboard/app/Console/Commands/MigrateFromV2b.php @@ -0,0 +1,186 @@ +argument('version'); + if($version === 'config'){ + $this->MigrateV2ConfigToV2Settings(); + return; + } + + // Define your SQL commands based on versions + $sqlCommands = [ + 'dev231027' => [ + // SQL commands for version Dev 2023/10/27 + 'ALTER TABLE v2_order ADD COLUMN surplus_order_ids TEXT NULL;', + 'ALTER TABLE v2_plan DROP COLUMN daily_unit_price, DROP COLUMN transfer_unit_price;', + 'ALTER TABLE v2_server_hysteria DROP COLUMN ignore_client_bandwidth, DROP COLUMN obfs_type;' + ], + '1.7.4' => [ + 'CREATE TABLE `v2_server_vless` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `group_id` TEXT NOT NULL, + `route_id` TEXT NULL, + `name` VARCHAR(255) NOT NULL, + `parent_id` INT NULL, + `host` VARCHAR(255) NOT NULL, + `port` INT NOT NULL, + `server_port` INT NOT NULL, + `tls` BOOLEAN NOT NULL, + `tls_settings` TEXT NULL, + `flow` VARCHAR(64) NULL, + `network` VARCHAR(11) NOT NULL, + `network_settings` TEXT NULL, + `tags` TEXT NULL, + `rate` VARCHAR(11) NOT NULL, + `show` BOOLEAN DEFAULT 0, + `sort` INT NULL, + `created_at` INT NOT NULL, + `updated_at` INT NOT NULL + );' + ], + '1.7.3' => [ + 'ALTER TABLE `v2_stat_order` RENAME TO `v2_stat`;', + "ALTER TABLE `v2_stat` CHANGE COLUMN order_amount paid_total INT COMMENT '订单合计';", + "ALTER TABLE `v2_stat` CHANGE COLUMN order_count paid_count INT COMMENT '邀请佣金';", + "ALTER TABLE `v2_stat` CHANGE COLUMN commission_amount commission_total INT COMMENT '佣金合计';", + "ALTER TABLE `v2_stat` + ADD COLUMN order_count INT NULL, + ADD COLUMN order_total INT NULL, + ADD COLUMN register_count INT NULL, + ADD COLUMN invite_count INT NULL, + ADD COLUMN transfer_used_total VARCHAR(32) NULL; + ", + "CREATE TABLE `v2_log` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `title` TEXT NOT NULL, + `level` VARCHAR(11) NULL, + `host` VARCHAR(255) NULL, + `uri` VARCHAR(255) NOT NULL, + `method` VARCHAR(11) NOT NULL, + `data` TEXT NULL, + `ip` VARCHAR(128) NULL, + `context` TEXT NULL, + `created_at` INT NOT NULL, + `updated_at` INT NOT NULL + );", + 'CREATE TABLE `v2_server_hysteria` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `group_id` VARCHAR(255) NOT NULL, + `route_id` VARCHAR(255) NULL, + `name` VARCHAR(255) NOT NULL, + `parent_id` INT NULL, + `host` VARCHAR(255) NOT NULL, + `port` VARCHAR(11) NOT NULL, + `server_port` INT NOT NULL, + `tags` VARCHAR(255) NULL, + `rate` VARCHAR(11) NOT NULL, + `show` BOOLEAN DEFAULT FALSE, + `sort` INT NULL, + `up_mbps` INT NOT NULL, + `down_mbps` INT NOT NULL, + `server_name` VARCHAR(64) NULL, + `insecure` BOOLEAN DEFAULT FALSE, + `created_at` INT NOT NULL, + `updated_at` INT NOT NULL + );', + "CREATE TABLE `v2_server_vless` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `group_id` TEXT NOT NULL, + `route_id` TEXT NULL, + `name` VARCHAR(255) NOT NULL, + `parent_id` INT NULL, + `host` VARCHAR(255) NOT NULL, + `port` INT NOT NULL, + `server_port` INT NOT NULL, + `tls` BOOLEAN NOT NULL, + `tls_settings` TEXT NULL, + `flow` VARCHAR(64) NULL, + `network` VARCHAR(11) NOT NULL, + `network_settings` TEXT NULL, + `tags` TEXT NULL, + `rate` VARCHAR(11) NOT NULL, + `show` BOOLEAN DEFAULT FALSE, + `sort` INT NULL, + `created_at` INT NOT NULL, + `updated_at` INT NOT NULL + );", + ], + 'wyx2685' => [ + "ALTER TABLE `v2_plan` DROP COLUMN `device_limit`;", + "ALTER TABLE `v2_server_hysteria` DROP COLUMN `version`, DROP COLUMN `obfs`, DROP COLUMN `obfs_password`;", + "ALTER TABLE `v2_server_trojan` DROP COLUMN `network`, DROP COLUMN `network_settings`;", + "ALTER TABLE `v2_user` DROP COLUMN `device_limit`;" + ] + ]; + + if (!$version) { + $version = $this->choice('请选择你迁移前的V2board版本:', array_keys($sqlCommands)); + } + + if (array_key_exists($version, $sqlCommands)) { + + try { + foreach ($sqlCommands[$version] as $sqlCommand) { + // Execute SQL command + DB::statement($sqlCommand); + } + + $this->info('1️⃣、数据库差异矫正成功'); + + // 初始化数据库迁移 + $this->call('db:seed', ['--class' => 'OriginV2bMigrationsTableSeeder']); + $this->info('2️⃣、数据库迁移记录初始化成功'); + + $this->call('xboard:update'); + $this->info('3️⃣、更新成功'); + + $this->info("🎉:成功从 $version 迁移到Xboard"); + } catch (\Exception $e) { + // An error occurred, rollback the transaction + $this->error('迁移失败'. $e->getMessage() ); + } + + + } else { + $this->error("你所输入的版本未找到"); + } + } + + public function MigrateV2ConfigToV2Settings() + { + Artisan::call('config:clear'); + $configValue = config('v2board') ?? []; + + foreach ($configValue as $k => $v) { + // 检查记录是否已存在 + $existingSetting = Setting::where('name', $k)->first(); + + // 如果记录不存在,则插入 + if ($existingSetting) { + $this->warn("配置 {$k} 在数据库已经存在, 忽略"); + continue; + } + Setting::create([ + 'name' => $k, + 'value' => is_array($v)? json_encode($v) : $v, + ]); + $this->info("配置 {$k} 迁移成功"); + } + Artisan::call('config:cache'); + + $this->info('所有配置迁移完成'); + } +} diff --git a/Xboard/app/Console/Commands/NodeWebSocketServer.php b/Xboard/app/Console/Commands/NodeWebSocketServer.php new file mode 100644 index 0000000..cbad533 --- /dev/null +++ b/Xboard/app/Console/Commands/NodeWebSocketServer.php @@ -0,0 +1,34 @@ +argument('action'); + + $argv[1] = $action; + if ($this->option('d')) { + $argv[2] = '-d'; + } + + $host = $this->option('host'); + $port = $this->option('port'); + + $worker = new NodeWorker($host, $port); + $worker->run(); + } +} diff --git a/Xboard/app/Console/Commands/ResetLog.php b/Xboard/app/Console/Commands/ResetLog.php new file mode 100644 index 0000000..89ddfb3 --- /dev/null +++ b/Xboard/app/Console/Commands/ResetLog.php @@ -0,0 +1,48 @@ +delete(); + StatServer::where('record_at', '<', strtotime('-2 month', time()))->delete(); + AdminAuditLog::where('created_at', '<', strtotime('-3 month', time()))->delete(); + } +} diff --git a/Xboard/app/Console/Commands/ResetPassword.php b/Xboard/app/Console/Commands/ResetPassword.php new file mode 100644 index 0000000..3e131f2 --- /dev/null +++ b/Xboard/app/Console/Commands/ResetPassword.php @@ -0,0 +1,55 @@ +argument('password') ; + $user = User::byEmail($this->argument('email'))->first(); + if (!$user) abort(500, '邮箱不存在'); + $password = $password ?? Helper::guid(false); + $user->password = password_hash($password, PASSWORD_DEFAULT); + $user->password_algo = null; + if (!$user->save()) abort(500, '重置失败'); + $this->info("!!!重置成功!!!"); + $this->info("新密码为:{$password},请尽快修改密码。"); + } +} diff --git a/Xboard/app/Console/Commands/ResetTraffic.php b/Xboard/app/Console/Commands/ResetTraffic.php new file mode 100644 index 0000000..5152077 --- /dev/null +++ b/Xboard/app/Console/Commands/ResetTraffic.php @@ -0,0 +1,289 @@ +option('fix-null'); + $force = $this->option('force'); + + $this->info('🚀 开始执行流量重置任务...'); + + if ($fixNull) { + $this->warn('🔧 修正模式 - 将重新计算next_reset_at为null的用户'); + } elseif ($force) { + $this->warn('⚡ 强制模式 - 将重新计算所有用户的重置时间'); + } + + try { + $result = $fixNull ? $this->performFix() : ($force ? $this->performForce() : $this->performReset()); + $this->displayResults($result, $fixNull || $force); + return self::SUCCESS; + + } catch (\Exception $e) { + $this->error("❌ 任务执行失败: {$e->getMessage()}"); + + Log::error('流量重置命令执行失败', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + + return self::FAILURE; + } + } + + private function displayResults(array $result, bool $isSpecialMode): void + { + $this->info("✅ 任务完成!\n"); + + if ($isSpecialMode) { + $this->displayFixResults($result); + } else { + $this->displayExecutionResults($result); + } + } + + private function displayFixResults(array $result): void + { + $this->info("📊 修正结果统计:"); + $this->info("🔍 发现用户总数: {$result['total_found']}"); + $this->info("✅ 成功修正数量: {$result['total_fixed']}"); + $this->info("⏱️ 总执行时间: {$result['duration']} 秒"); + + if ($result['error_count'] > 0) { + $this->warn("⚠️ 错误数量: {$result['error_count']}"); + $this->warn("详细错误信息请查看日志"); + } else { + $this->info("✨ 无错误发生"); + } + + if ($result['total_found'] > 0) { + $avgTime = round($result['duration'] / $result['total_found'], 4); + $this->info("⚡ 平均处理速度: {$avgTime} 秒/用户"); + } + } + + + + private function displayExecutionResults(array $result): void + { + $this->info("📊 执行结果统计:"); + $this->info("👥 处理用户总数: {$result['total_processed']}"); + $this->info("🔄 重置用户数量: {$result['total_reset']}"); + $this->info("⏱️ 总执行时间: {$result['duration']} 秒"); + + if ($result['error_count'] > 0) { + $this->warn("⚠️ 错误数量: {$result['error_count']}"); + $this->warn("详细错误信息请查看日志"); + } else { + $this->info("✨ 无错误发生"); + } + + if ($result['total_processed'] > 0) { + $avgTime = round($result['duration'] / $result['total_processed'], 4); + $this->info("⚡ 平均处理速度: {$avgTime} 秒/用户"); + } + } + + private function performReset(): array + { + $startTime = microtime(true); + $totalResetCount = 0; + $errors = []; + + $users = $this->getResetQuery()->get(); + + if ($users->isEmpty()) { + $this->info("😴 当前没有需要重置的用户"); + return [ + 'total_processed' => 0, + 'total_reset' => 0, + 'error_count' => 0, + 'duration' => round(microtime(true) - $startTime, 2), + ]; + } + + $this->info("找到 {$users->count()} 个需要重置的用户"); + + foreach ($users as $user) { + try { + $totalResetCount += (int) $this->trafficResetService->checkAndReset($user, TrafficResetLog::SOURCE_CRON); + } catch (\Exception $e) { + $errors[] = [ + 'user_id' => $user->id, + 'email' => $user->email, + 'error' => $e->getMessage(), + ]; + Log::error('用户流量重置失败', [ + 'user_id' => $user->id, + 'error' => $e->getMessage(), + ]); + } + } + + return [ + 'total_processed' => $users->count(), + 'total_reset' => $totalResetCount, + 'error_count' => count($errors), + 'duration' => round(microtime(true) - $startTime, 2), + ]; + } + + private function performFix(): array + { + $startTime = microtime(true); + $nullUsers = $this->getNullResetTimeUsers(); + + if ($nullUsers->isEmpty()) { + $this->info("✅ 没有发现next_reset_at为null的用户"); + return [ + 'total_found' => 0, + 'total_fixed' => 0, + 'error_count' => 0, + 'duration' => round(microtime(true) - $startTime, 2), + ]; + } + + $this->info("🔧 发现 {$nullUsers->count()} 个next_reset_at为null的用户,开始修正..."); + + $fixedCount = 0; + $errors = []; + + foreach ($nullUsers as $user) { + try { + $nextResetTime = $this->trafficResetService->calculateNextResetTime($user); + if ($nextResetTime) { + $user->next_reset_at = $nextResetTime->timestamp; + $user->save(); + $fixedCount++; + } + } catch (\Exception $e) { + $errors[] = [ + 'user_id' => $user->id, + 'email' => $user->email, + 'error' => $e->getMessage(), + ]; + Log::error('修正用户next_reset_at失败', [ + 'user_id' => $user->id, + 'error' => $e->getMessage(), + ]); + } + } + + return [ + 'total_found' => $nullUsers->count(), + 'total_fixed' => $fixedCount, + 'error_count' => count($errors), + 'duration' => round(microtime(true) - $startTime, 2), + ]; + } + + private function performForce(): array + { + $startTime = microtime(true); + $allUsers = $this->getAllUsers(); + + if ($allUsers->isEmpty()) { + $this->info("✅ 没有发现需要处理的用户"); + return [ + 'total_found' => 0, + 'total_fixed' => 0, + 'error_count' => 0, + 'duration' => round(microtime(true) - $startTime, 2), + ]; + } + + $this->info("⚡ 发现 {$allUsers->count()} 个用户,开始重新计算重置时间..."); + + $fixedCount = 0; + $errors = []; + + foreach ($allUsers as $user) { + try { + $nextResetTime = $this->trafficResetService->calculateNextResetTime($user); + if ($nextResetTime) { + $user->next_reset_at = $nextResetTime->timestamp; + $user->save(); + $fixedCount++; + } + } catch (\Exception $e) { + $errors[] = [ + 'user_id' => $user->id, + 'email' => $user->email, + 'error' => $e->getMessage(), + ]; + Log::error('强制重新计算用户next_reset_at失败', [ + 'user_id' => $user->id, + 'error' => $e->getMessage(), + ]); + } + } + + return [ + 'total_found' => $allUsers->count(), + 'total_fixed' => $fixedCount, + 'error_count' => count($errors), + 'duration' => round(microtime(true) - $startTime, 2), + ]; + } + + + + private function getResetQuery() + { + return User::where('next_reset_at', '<=', time()) + ->whereNotNull('next_reset_at') + ->where(function ($query) { + $query->where('expired_at', '>', time()) + ->orWhereNull('expired_at'); + }) + ->where('banned', 0) + ->whereNotNull('plan_id'); + } + + + + private function getNullResetTimeUsers() + { + return User::whereNull('next_reset_at') + ->whereNotNull('plan_id') + ->where(function ($query) { + $query->where('expired_at', '>', time()) + ->orWhereNull('expired_at'); + }) + ->where('banned', 0) + ->with('plan:id,name,reset_traffic_method') + ->get(); + } + + private function getAllUsers() + { + return User::whereNotNull('plan_id') + ->where(function ($query) { + $query->where('expired_at', '>', time()) + ->orWhereNull('expired_at'); + }) + ->where('banned', 0) + ->with('plan:id,name,reset_traffic_method') + ->get(); + } + +} \ No newline at end of file diff --git a/Xboard/app/Console/Commands/ResetUser.php b/Xboard/app/Console/Commands/ResetUser.php new file mode 100644 index 0000000..51197ae --- /dev/null +++ b/Xboard/app/Console/Commands/ResetUser.php @@ -0,0 +1,58 @@ +confirm("确定要重置所有用户安全信息吗?")) { + return; + } + ini_set('memory_limit', -1); + $users = User::all(); + foreach ($users as $user) + { + $user->token = Helper::guid(); + $user->uuid = Helper::guid(true); + $user->save(); + $this->info("已重置用户{$user->email}的安全信息"); + } + } +} diff --git a/Xboard/app/Console/Commands/SendRemindMail.php b/Xboard/app/Console/Commands/SendRemindMail.php new file mode 100644 index 0000000..5fdb2d1 --- /dev/null +++ b/Xboard/app/Console/Commands/SendRemindMail.php @@ -0,0 +1,103 @@ +warn('邮件提醒功能未启用'); + return 0; + } + + $chunkSize = max(100, min(2000, (int) $this->option('chunk-size'))); + $mailService = new MailService(); + + $totalUsers = $mailService->getTotalUsersNeedRemind(); + if ($totalUsers === 0) { + $this->info('没有需要发送提醒邮件的用户'); + return 0; + } + + $this->displayInfo($totalUsers, $chunkSize); + + if (!$this->option('force') && !$this->confirm("确定要发送提醒邮件给 {$totalUsers} 个用户吗?")) { + return 0; + } + + $startTime = microtime(true); + $progressBar = $this->output->createProgressBar((int) ceil($totalUsers / $chunkSize)); + $progressBar->start(); + + $statistics = $mailService->processUsersInChunks($chunkSize, function () use ($progressBar) { + $progressBar->advance(); + }); + + $progressBar->finish(); + $this->newLine(); + + $this->displayResults($statistics, microtime(true) - $startTime); + $this->logResults($statistics); + + return 0; + } + + private function displayInfo(int $totalUsers, int $chunkSize): void + { + $this->table(['项目', '值'], [ + ['需要处理的用户', number_format($totalUsers)], + ['批次大小', $chunkSize], + ['预计批次', ceil($totalUsers / $chunkSize)], + ]); + } + + private function displayResults(array $stats, float $duration): void + { + $this->info('✅ 提醒邮件发送完成!'); + + $this->table(['统计项', '数量'], [ + ['总处理用户', number_format($stats['processed_users'])], + ['过期提醒邮件', number_format($stats['expire_emails'])], + ['流量提醒邮件', number_format($stats['traffic_emails'])], + ['跳过用户', number_format($stats['skipped'])], + ['错误数量', number_format($stats['errors'])], + ['总耗时', round($duration, 2) . ' 秒'], + ['平均速度', round($stats['processed_users'] / max($duration, 0.1), 1) . ' 用户/秒'], + ]); + + if ($stats['errors'] > 0) { + $this->warn("⚠️ 有 {$stats['errors']} 个用户的邮件发送失败,请检查日志"); + } + } + + private function logResults(array $statistics): void + { + Log::info('SendRemindMail命令执行完成', ['statistics' => $statistics]); + } +} diff --git a/Xboard/app/Console/Commands/Test.php b/Xboard/app/Console/Commands/Test.php new file mode 100644 index 0000000..667e616 --- /dev/null +++ b/Xboard/app/Console/Commands/Test.php @@ -0,0 +1,41 @@ +info("__ __ ____ _ "); + $this->info("\ \ / /| __ ) ___ __ _ _ __ __| | "); + $this->info(" \ \/ / | __ \ / _ \ / _` | '__/ _` | "); + $this->info(" / /\ \ | |_) | (_) | (_| | | | (_| | "); + $this->info("/_/ \_\|____/ \___/ \__,_|_| \__,_| "); + if ( + (File::exists(base_path() . '/.env') && $this->getEnvValue('INSTALLED')) + || (getenv('INSTALLED', false) && $isDocker) + ) { + $securePath = admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))); + $this->info("访问 http(s)://你的站点/{$securePath} 进入管理面板,你可以在用户中心修改你的密码。"); + $this->warn("如需重新安装请清空目录下 .env 文件的内容(Docker安装方式不可以删除此文件)"); + $this->warn("快捷清空.env命令:"); + note('rm .env && touch .env'); + return; + } + if (is_dir(base_path() . '/.env')) { + $this->error('😔:安装失败,Docker环境下安装请保留空的 .env 文件'); + return; + } + // 选择数据库类型 + $dbType = $enableSqlite ? 'sqlite' : select( + label: '请选择数据库类型', + options: [ + 'sqlite' => 'SQLite (无需额外安装)', + 'mysql' => 'MySQL', + 'postgresql' => 'PostgreSQL' + ], + default: 'sqlite' + ); + + // 使用 match 表达式配置数据库 + $envConfig = match ($dbType) { + 'sqlite' => $this->configureSqlite(), + 'mysql' => $this->configureMysql(), + 'postgresql' => $this->configurePostgresql(), + default => throw new \InvalidArgumentException("不支持的数据库类型: {$dbType}") + }; + + if (is_null($envConfig)) { + return; // 用户选择退出安装 + } + $envConfig['APP_KEY'] = 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')); + $isReidsValid = false; + while (!$isReidsValid) { + // 判断是否为Docker环境 + if ($isDocker == 'true' && ($enableRedis || confirm(label: '是否启用Docker内置的Redis', default: true, yes: '启用', no: '不启用'))) { + $envConfig['REDIS_HOST'] = '/data/redis.sock'; + $envConfig['REDIS_PORT'] = 0; + $envConfig['REDIS_PASSWORD'] = null; + } else { + $envConfig['REDIS_HOST'] = text(label: '请输入Redis地址', default: '127.0.0.1', required: true); + $envConfig['REDIS_PORT'] = text(label: '请输入Redis端口', default: '6379', required: true); + $envConfig['REDIS_PASSWORD'] = text(label: '请输入redis密码(默认: null)', default: ''); + } + $redisConfig = [ + 'client' => 'phpredis', + 'default' => [ + 'host' => $envConfig['REDIS_HOST'], + 'password' => $envConfig['REDIS_PASSWORD'], + 'port' => $envConfig['REDIS_PORT'], + 'database' => 0, + ], + ]; + try { + $redis = new \Illuminate\Redis\RedisManager(app(), 'phpredis', $redisConfig); + $redis->ping(); + $isReidsValid = true; + } catch (\Exception $e) { + // 连接失败,输出错误消息 + $this->error("redis连接失败:" . $e->getMessage()); + $this->info("请重新输入REDIS配置"); + $enableRedis = false; + sleep(1); + } + } + + if (!copy(base_path() . '/.env.example', base_path() . '/.env')) { + abort(500, '复制环境文件失败,请检查目录权限'); + } + ; + $email = !empty($adminAccount) ? $adminAccount : text( + label: '请输入管理员账号', + default: 'admin@demo.com', + required: true, + validate: fn(string $email): ?string => match (true) { + !filter_var($email, FILTER_VALIDATE_EMAIL) => '请输入有效的邮箱地址.', + default => null, + } + ); + $password = Helper::guid(false); + $this->saveToEnv($envConfig); + + $this->call('config:cache'); + Artisan::call('cache:clear'); + $this->info('正在导入数据库请稍等...'); + Artisan::call("migrate", ['--force' => true]); + $this->info(Artisan::output()); + $this->info('数据库导入完成'); + $this->info('开始注册管理员账号'); + if (!self::registerAdmin($email, $password)) { + abort(500, '管理员账号注册失败,请重试'); + } + self::restoreProtectedPlugins($this); + $this->info('正在安装默认插件...'); + PluginManager::installDefaultPlugins(); + $this->info('默认插件安装完成'); + + $this->info('🎉:一切就绪'); + $this->info("管理员邮箱:{$email}"); + $this->info("管理员密码:{$password}"); + + $defaultSecurePath = hash('crc32b', config('app.key')); + $this->info("访问 http(s)://你的站点/{$defaultSecurePath} 进入管理面板,你可以在用户中心修改你的密码。"); + $envConfig['INSTALLED'] = true; + $this->saveToEnv($envConfig); + } catch (\Exception $e) { + $this->error($e); + } + } + + public static function registerAdmin($email, $password) + { + $user = new User(); + $user->email = $email; + if (strlen($password) < 8) { + abort(500, '管理员密码长度最小为8位字符'); + } + $user->password = password_hash($password, PASSWORD_DEFAULT); + $user->uuid = Helper::guid(true); + $user->token = Helper::guid(); + $user->is_admin = 1; + return $user->save(); + } + + private function set_env_var($key, $value) + { + $value = !strpos($value, ' ') ? $value : '"' . $value . '"'; + $key = strtoupper($key); + + $envPath = app()->environmentFilePath(); + $contents = file_get_contents($envPath); + + if (preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches)) { + $contents = str_replace($matches[0], "{$key}={$value}", $contents); + } else { + $contents .= "\n{$key}={$value}\n"; + } + + return file_put_contents($envPath, $contents) !== false; + } + + private function saveToEnv($data = []) + { + foreach ($data as $key => $value) { + self::set_env_var($key, $value); + } + return true; + } + + function getEnvValue($key, $default = null) + { + $dotenv = \Dotenv\Dotenv::createImmutable(base_path()); + $dotenv->load(); + + return Env::get($key, $default); + } + + /** + * 配置 SQLite 数据库 + * + * @return array|null + */ + private function configureSqlite(): ?array + { + $sqliteFile = '.docker/.data/database.sqlite'; + if (!file_exists(base_path($sqliteFile))) { + // 创建空文件 + if (!touch(base_path($sqliteFile))) { + $this->info("sqlite创建成功: $sqliteFile"); + } + } + + $envConfig = [ + 'DB_CONNECTION' => 'sqlite', + 'DB_DATABASE' => $sqliteFile, + 'DB_HOST' => '', + 'DB_USERNAME' => '', + 'DB_PASSWORD' => '', + ]; + + try { + Config::set("database.default", 'sqlite'); + Config::set("database.connections.sqlite.database", base_path($envConfig['DB_DATABASE'])); + DB::purge('sqlite'); + DB::connection('sqlite')->getPdo(); + + if (!blank(DB::connection('sqlite')->getPdo()->query("SELECT name FROM sqlite_master WHERE type='table'")->fetchAll(\PDO::FETCH_COLUMN))) { + if (confirm(label: '检测到数据库中已经存在数据,是否要清空数据库以便安装新的数据?', default: false, yes: '清空', no: '退出安装')) { + $this->info('正在清空数据库请稍等'); + $this->call('db:wipe', ['--force' => true]); + $this->info('数据库清空完成'); + } else { + return null; + } + } + } catch (\Exception $e) { + $this->error("SQLite数据库连接失败:" . $e->getMessage()); + return null; + } + + return $envConfig; + } + + /** + * 配置 MySQL 数据库 + * + * @return array + */ + private function configureMysql(): array + { + while (true) { + $envConfig = [ + 'DB_CONNECTION' => 'mysql', + 'DB_HOST' => text(label: "请输入MySQL数据库地址", default: '127.0.0.1', required: true), + 'DB_PORT' => text(label: '请输入MySQL数据库端口', default: '3306', required: true), + 'DB_DATABASE' => text(label: '请输入MySQL数据库名', default: 'xboard', required: true), + 'DB_USERNAME' => text(label: '请输入MySQL数据库用户名', default: 'root', required: true), + 'DB_PASSWORD' => text(label: '请输入MySQL数据库密码', required: false), + ]; + + try { + Config::set("database.default", 'mysql'); + Config::set("database.connections.mysql.host", $envConfig['DB_HOST']); + Config::set("database.connections.mysql.port", $envConfig['DB_PORT']); + Config::set("database.connections.mysql.database", $envConfig['DB_DATABASE']); + Config::set("database.connections.mysql.username", $envConfig['DB_USERNAME']); + Config::set("database.connections.mysql.password", $envConfig['DB_PASSWORD']); + DB::purge('mysql'); + DB::connection('mysql')->getPdo(); + + if (!blank(DB::connection('mysql')->select('SHOW TABLES'))) { + if (confirm(label: '检测到数据库中已经存在数据,是否要清空数据库以便安装新的数据?', default: false, yes: '清空', no: '不清空')) { + $this->info('正在清空数据库请稍等'); + $this->call('db:wipe', ['--force' => true]); + $this->info('数据库清空完成'); + return $envConfig; + } else { + continue; // 重新输入配置 + } + } + + return $envConfig; + } catch (\Exception $e) { + $this->error("MySQL数据库连接失败:" . $e->getMessage()); + $this->info("请重新输入MySQL数据库配置"); + } + } + } + + /** + * 配置 PostgreSQL 数据库 + * + * @return array + */ + private function configurePostgresql(): array + { + while (true) { + $envConfig = [ + 'DB_CONNECTION' => 'pgsql', + 'DB_HOST' => text(label: "请输入PostgreSQL数据库地址", default: '127.0.0.1', required: true), + 'DB_PORT' => text(label: '请输入PostgreSQL数据库端口', default: '5432', required: true), + 'DB_DATABASE' => text(label: '请输入PostgreSQL数据库名', default: 'xboard', required: true), + 'DB_USERNAME' => text(label: '请输入PostgreSQL数据库用户名', default: 'postgres', required: true), + 'DB_PASSWORD' => text(label: '请输入PostgreSQL数据库密码', required: false), + ]; + + try { + Config::set("database.default", 'pgsql'); + Config::set("database.connections.pgsql.host", $envConfig['DB_HOST']); + Config::set("database.connections.pgsql.port", $envConfig['DB_PORT']); + Config::set("database.connections.pgsql.database", $envConfig['DB_DATABASE']); + Config::set("database.connections.pgsql.username", $envConfig['DB_USERNAME']); + Config::set("database.connections.pgsql.password", $envConfig['DB_PASSWORD']); + DB::purge('pgsql'); + DB::connection('pgsql')->getPdo(); + + // 检查PostgreSQL数据库是否有表 + $tables = DB::connection('pgsql')->select("SELECT tablename FROM pg_tables WHERE schemaname = 'public'"); + if (!blank($tables)) { + if (confirm(label: '检测到数据库中已经存在数据,是否要清空数据库以便安装新的数据?', default: false, yes: '清空', no: '不清空')) { + $this->info('正在清空数据库请稍等'); + $this->call('db:wipe', ['--force' => true]); + $this->info('数据库清空完成'); + return $envConfig; + } else { + continue; // 重新输入配置 + } + } + + return $envConfig; + } catch (\Exception $e) { + $this->error("PostgreSQL数据库连接失败:" . $e->getMessage()); + $this->info("请重新输入PostgreSQL数据库配置"); + } + } + } + + /** + * 还原内置受保护插件(可在安装和更新时调用) + * Docker 部署时 plugins/ 目录被外部挂载覆盖,需要从镜像备份中还原默认插件 + */ + public static function restoreProtectedPlugins(Command $console = null) + { + $backupBase = '/opt/default-plugins'; + $pluginsBase = base_path('plugins'); + + if (!File::isDirectory($backupBase)) { + $console?->info('非 Docker 环境或备份目录不存在,跳过插件还原。'); + return; + } + + foreach (Plugin::PROTECTED_PLUGINS as $pluginCode) { + $dirName = Str::studly($pluginCode); + $source = "{$backupBase}/{$dirName}"; + $target = "{$pluginsBase}/{$dirName}"; + + if (!File::isDirectory($source)) { + continue; + } + + // 先清除旧文件再复制,避免重命名后残留旧文件 + File::deleteDirectory($target); + File::copyDirectory($source, $target); + $console?->info("已同步默认插件 [{$dirName}]"); + } + } +} diff --git a/Xboard/app/Console/Commands/XboardRollback.php b/Xboard/app/Console/Commands/XboardRollback.php new file mode 100644 index 0000000..f9e5f46 --- /dev/null +++ b/Xboard/app/Console/Commands/XboardRollback.php @@ -0,0 +1,45 @@ +info('正在回滚数据库请稍等...'); + \Artisan::call("migrate:rollback"); + $this->info(\Artisan::output()); + } +} diff --git a/Xboard/app/Console/Commands/XboardStatistics.php b/Xboard/app/Console/Commands/XboardStatistics.php new file mode 100644 index 0000000..0bd4736 --- /dev/null +++ b/Xboard/app/Console/Commands/XboardStatistics.php @@ -0,0 +1,75 @@ +statUser(); + // $this->statServer(); + $this->stat(); + info('统计任务执行完毕。耗时:' . (microtime(true) - $startAt) / 1000); + } + + + private function stat() + { + try { + $endAt = strtotime(date('Y-m-d')); + $startAt = strtotime('-1 day', $endAt); + $statisticalService = new StatisticalService(); + $statisticalService->setStartAt($startAt); + $statisticalService->setEndAt($endAt); + $data = $statisticalService->generateStatData(); + $data['record_at'] = $startAt; + $data['record_type'] = 'd'; + $statistic = Stat::where('record_at', $startAt) + ->where('record_type', 'd') + ->first(); + if ($statistic) { + $statistic->update($data); + return; + } + Stat::create($data); + } catch (\Exception $e) { + Log::error($e->getMessage(), ['exception' => $e]); + } + } +} diff --git a/Xboard/app/Console/Commands/XboardUpdate.php b/Xboard/app/Console/Commands/XboardUpdate.php new file mode 100644 index 0000000..65c95da --- /dev/null +++ b/Xboard/app/Console/Commands/XboardUpdate.php @@ -0,0 +1,65 @@ +info('正在导入数据库请稍等...'); + Artisan::call("migrate", ['--force' => true]); + $this->info(Artisan::output()); + $this->info('正在检查内置插件文件...'); + XboardInstall::restoreProtectedPlugins($this); + $this->info('正在检查并安装默认插件...'); + PluginManager::installDefaultPlugins(); + $this->info('默认插件检查完成'); + // Artisan::call('reset:traffic', ['--fix-null' => true]); + $this->info('正在重新计算所有用户的重置时间...'); + Artisan::call('reset:traffic', ['--force' => true]); + $updateService = new UpdateService(); + $updateService->updateVersionCache(); + $themeService = app(ThemeService::class); + $themeService->refreshCurrentTheme(); + Artisan::call('horizon:terminate'); + $this->info('更新完毕,队列服务已重启,你无需进行任何操作。'); + } +} diff --git a/Xboard/app/Console/Kernel.php b/Xboard/app/Console/Kernel.php new file mode 100644 index 0000000..77b1810 --- /dev/null +++ b/Xboard/app/Console/Kernel.php @@ -0,0 +1,68 @@ +command('xboard:statistics')->dailyAt('0:10')->onOneServer(); + // check + $schedule->command('check:order')->everyMinute()->onOneServer()->withoutOverlapping(5); + $schedule->command('check:commission')->everyMinute()->onOneServer()->withoutOverlapping(5); + $schedule->command('check:ticket')->everyMinute()->onOneServer()->withoutOverlapping(5); + $schedule->command('check:traffic-exceeded')->everyMinute()->onOneServer()->withoutOverlapping(10)->runInBackground(); + // reset + $schedule->command('reset:traffic')->everyMinute()->onOneServer()->withoutOverlapping(10); + $schedule->command('reset:log')->daily()->onOneServer(); + // send + $schedule->command('send:remindMail', ['--force'])->dailyAt('11:30')->onOneServer(); + // horizon metrics + $schedule->command('horizon:snapshot')->everyFiveMinutes()->onOneServer(); + // backup Timing + // if (env('ENABLE_AUTO_BACKUP_AND_UPDATE', false)) { + // $schedule->command('backup:database', ['true'])->daily()->onOneServer(); + // } + app(PluginManager::class)->registerPluginSchedules($schedule); + + } + + /** + * Register the commands for the application. + * + * @return void + */ + protected function commands() + { + $this->load(__DIR__ . '/Commands'); + + try { + app(PluginManager::class)->initializeEnabledPlugins(); + } catch (\Exception $e) { + } + require base_path('routes/console.php'); + } +} diff --git a/Xboard/app/Contracts/PaymentInterface.php b/Xboard/app/Contracts/PaymentInterface.php new file mode 100644 index 0000000..2661362 --- /dev/null +++ b/Xboard/app/Contracts/PaymentInterface.php @@ -0,0 +1,10 @@ +message = $message; + $this->code = $code; + $this->errors = $errors; + } + public function errors(){ + return $this->errors; + } + +} diff --git a/Xboard/app/Exceptions/BusinessException.php b/Xboard/app/Exceptions/BusinessException.php new file mode 100644 index 0000000..2079495 --- /dev/null +++ b/Xboard/app/Exceptions/BusinessException.php @@ -0,0 +1,19 @@ +> + */ + protected $dontReport = [ + ApiException::class, + InterceptResponseException::class + ]; + + /** + * A list of the inputs that are never flashed for validation exceptions. + * + * @var array + */ + protected $dontFlash = [ + 'password', + 'password_confirmation', + ]; + + /** + * Report or log an exception. + * + * @param \Throwable $exception + * @return void + * + * @throws \Throwable + */ + public function report(Throwable $exception) + { + parent::report($exception); + } + + /** + * Render an exception into an HTTP response. + * + * @param \Illuminate\Http\Request $request + * @param \Throwable $exception + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \Throwable + */ + public function render($request, Throwable $exception) + { + if ($exception instanceof ViewException) { + return $this->fail([500, '主题渲染失败。如更新主题,参数可能发生变化请重新配置主题后再试。']); + } + // ApiException主动抛出错误 + if ($exception instanceof ApiException) { + $code = $exception->getCode(); + $message = $exception->getMessage(); + $errors = $exception->errors(); + return $this->fail([$code, $message],null,$errors); + } + return parent::render($request, $exception); + } + + /** + * Register the exception handling callbacks for the application. + */ + public function register(): void + { + $this->reportable(function (Throwable $e) { + // + }); + + $this->renderable(function (InterceptResponseException $e) { + return $e->getResponse(); + }); + } + + protected function convertExceptionToArray(Throwable $e) + { + return config('app.debug') ? [ + 'message' => $e->getMessage(), + 'exception' => get_class($e), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => collect($e->getTrace())->map(function ($trace) { + return Arr::except($trace, ['args']); + })->all(), + ] : [ + 'message' => $this->isHttpException($e) ? $e->getMessage() : __("Uh-oh, we've had some problems, we're working on it."), + ]; + } +} diff --git a/Xboard/app/Helpers/ApiResponse.php b/Xboard/app/Helpers/ApiResponse.php new file mode 100644 index 0000000..4820eb4 --- /dev/null +++ b/Xboard/app/Helpers/ApiResponse.php @@ -0,0 +1,79 @@ +jsonResponse('success', $codeResponse, $data, null); + } + + /** + * 失败 + * @param array $codeResponse + * @param mixed $data + * @param mixed $error + * @return JsonResponse + */ + public function fail($codeResponse = ResponseEnum::HTTP_ERROR, $data = null, $error = null): JsonResponse + { + return $this->jsonResponse('fail', $codeResponse, $data, $error); + } + + /** + * json响应 + * @param $status + * @param $codeResponse + * @param $data + * @param $error + * @return JsonResponse + */ + private function jsonResponse($status, $codeResponse, $data, $error): JsonResponse + { + list($code, $message) = $codeResponse; + return response() + ->json([ + 'status' => $status, + // 'code' => $code, + 'message' => $message, + 'data' => $data ?? null, + 'error' => $error, + ], (int) substr(((string) $code), 0, 3)); + } + + + public function paginate(LengthAwarePaginator $page) + { + return response()->json([ + 'total' => $page->total(), + 'current_page' => $page->currentPage(), + 'per_page' => $page->perPage(), + 'last_page' => $page->lastPage(), + 'data' => $page->items() + ]); + } + + /** + * 业务异常返回 + * @param array $codeResponse + * @param string $info + * @throws BusinessException + */ + public function throwBusinessException(array $codeResponse = ResponseEnum::HTTP_ERROR, string $info = '') + { + throw new BusinessException($codeResponse, $info); + } +} \ No newline at end of file diff --git a/Xboard/app/Helpers/Functions.php b/Xboard/app/Helpers/Functions.php new file mode 100644 index 0000000..282d62e --- /dev/null +++ b/Xboard/app/Helpers/Functions.php @@ -0,0 +1,82 @@ +toArray(); + } + + if (is_array($key)) { + $setting->save($key); + return ''; + } + + $default = config('v2board.' . $key) ?? $default; + return $setting->get($key) ?? $default; + } +} + +if (!function_exists('subscribe_template')) { + /** + * Get subscribe template content by protocol name. + */ + function subscribe_template(string $name): ?string + { + return \App\Models\SubscribeTemplate::getContent($name); + } +} + +if (!function_exists('admin_settings_batch')) { + /** + * 批量获取配置参数,性能优化版本 + * + * @param array $keys 配置键名数组 + * @return array 返回键值对数组 + */ + function admin_settings_batch(array $keys): array + { + return app(Setting::class)->getBatch($keys); + } +} + +if (!function_exists('source_base_url')) { + /** + * 获取来源基础URL,优先Referer,其次Host + * @param string $path + * @return string + */ + function source_base_url(string $path = ''): string + { + $baseUrl = ''; + $referer = request()->header('Referer'); + + if ($referer) { + $parsedUrl = parse_url($referer); + if (isset($parsedUrl['scheme']) && isset($parsedUrl['host'])) { + $baseUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host']; + if (isset($parsedUrl['port'])) { + $baseUrl .= ':' . $parsedUrl['port']; + } + } + } + + if (!$baseUrl) { + $baseUrl = request()->getSchemeAndHttpHost(); + } + + $baseUrl = rtrim($baseUrl, '/'); + $path = ltrim($path, '/'); + return $baseUrl . '/' . $path; + } +} diff --git a/Xboard/app/Helpers/ResponseEnum.php b/Xboard/app/Helpers/ResponseEnum.php new file mode 100644 index 0000000..f749ad3 --- /dev/null +++ b/Xboard/app/Helpers/ResponseEnum.php @@ -0,0 +1,81 @@ +isPluginEnabled()) { + return [400, '插件未启用']; + } + return null; + } +} \ No newline at end of file diff --git a/Xboard/app/Http/Controllers/V1/Client/AppController.php b/Xboard/app/Http/Controllers/V1/Client/AppController.php new file mode 100644 index 0000000..4637b08 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Client/AppController.php @@ -0,0 +1,90 @@ +user(); + $userService = new UserService(); + if ($userService->isAvailable($user)) { + $servers = ServerService::getAvailableServers($user); + } + $defaultConfig = base_path() . '/resources/rules/app.clash.yaml'; + $customConfig = base_path() . '/resources/rules/custom.app.clash.yaml'; + if (File::exists($customConfig)) { + $config = Yaml::parseFile($customConfig); + } else { + $config = Yaml::parseFile($defaultConfig); + } + $proxy = []; + $proxies = []; + + foreach ($servers as $item) { + $protocol_settings = $item['protocol_settings']; + if ($item['type'] === 'shadowsocks' + && in_array(data_get($protocol_settings, 'cipher'), [ + 'aes-128-gcm', + 'aes-192-gcm', + 'aes-256-gcm', + 'chacha20-ietf-poly1305' + ]) + ) { + array_push($proxy, \App\Protocols\Clash::buildShadowsocks($user['uuid'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === 'vmess') { + array_push($proxy, \App\Protocols\Clash::buildVmess($user['uuid'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === 'trojan') { + array_push($proxy, \App\Protocols\Clash::buildTrojan($user['uuid'], $item)); + array_push($proxies, $item['name']); + } + } + + $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy); + foreach ($config['proxy-groups'] as $k => $v) { + $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies); + } + return(Yaml::dump($config)); + } + + public function getVersion(Request $request) + { + if (strpos($request->header('user-agent'), 'tidalab/4.0.0') !== false + || strpos($request->header('user-agent'), 'tunnelab/4.0.0') !== false + ) { + if (strpos($request->header('user-agent'), 'Win64') !== false) { + $data = [ + 'version' => admin_setting('windows_version'), + 'download_url' => admin_setting('windows_download_url') + ]; + } else { + $data = [ + 'version' => admin_setting('macos_version'), + 'download_url' => admin_setting('macos_download_url') + ]; + } + }else{ + $data = [ + 'windows_version' => admin_setting('windows_version'), + 'windows_download_url' => admin_setting('windows_download_url'), + 'macos_version' => admin_setting('macos_version'), + 'macos_download_url' => admin_setting('macos_download_url'), + 'android_version' => admin_setting('android_version'), + 'android_download_url' => admin_setting('android_download_url') + ]; + } + return $this->success($data); + } +} diff --git a/Xboard/app/Http/Controllers/V1/Client/ClientController.php b/Xboard/app/Http/Controllers/V1/Client/ClientController.php new file mode 100644 index 0000000..e4437bc --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Client/ClientController.php @@ -0,0 +1,247 @@ + [ + 1 => '[Hy]', + 2 => '[Hy2]' + ], + 'vless' => '[vless]', + 'shadowsocks' => '[ss]', + 'vmess' => '[vmess]', + 'trojan' => '[trojan]', + 'tuic' => '[tuic]', + 'socks' => '[socks]', + 'anytls' => '[anytls]' + ]; + + + public function subscribe(Request $request) + { + HookManager::call('client.subscribe.before'); + $request->validate([ + 'types' => ['nullable', 'string'], + 'filter' => ['nullable', 'string'], + 'flag' => ['nullable', 'string'], + ]); + + $user = $request->user(); + $userService = new UserService(); + + if (!$userService->isAvailable($user)) { + HookManager::call('client.subscribe.unavailable'); + return response('', 403, ['Content-Type' => 'text/plain']); + } + + return $this->doSubscribe($request, $user); + } + + public function doSubscribe(Request $request, $user, $servers = null) + { + if ($servers === null) { + $servers = ServerService::getAvailableServers($user); + $servers = HookManager::filter('client.subscribe.servers', $servers, $user, $request); + } + + $clientInfo = $this->getClientInfo($request); + + $requestedTypes = $this->parseRequestedTypes($request->input('types')); + $filterKeywords = $this->parseFilterKeywords($request->input('filter')); + + $protocolClassName = app('protocols.manager')->matchProtocolClassName($clientInfo['flag']) + ?? General::class; + + $serversFiltered = $this->filterServers( + servers: $servers, + allowedTypes: $requestedTypes, + filterKeywords: $filterKeywords + ); + + $this->setSubscribeInfoToServers($serversFiltered, $user, count($servers) - count($serversFiltered)); + $serversFiltered = $this->addPrefixToServerName($serversFiltered); + + // Instantiate the protocol class with filtered servers and client info + $protocolInstance = app()->make($protocolClassName, [ + 'user' => $user, + 'servers' => $serversFiltered, + 'clientName' => $clientInfo['name'] ?? null, + 'clientVersion' => $clientInfo['version'] ?? null, + 'userAgent' => $clientInfo['flag'] ?? null + ]); + + return $protocolInstance->handle(); + } + + /** + * Parses the input string for requested server types. + */ + private function parseRequestedTypes(?string $typeInputString): array + { + if (blank($typeInputString) || $typeInputString === 'all') { + return Server::VALID_TYPES; + } + + $requested = collect(preg_split('/[|,|]+/', $typeInputString)) + ->map(fn($type) => trim($type)) + ->filter() // Remove empty strings that might result from multiple delimiters + ->all(); + + return array_values(array_intersect($requested, Server::VALID_TYPES)); + } + + /** + * Parses the input string for filter keywords. + */ + private function parseFilterKeywords(?string $filterInputString): ?array + { + if (blank($filterInputString) || mb_strlen($filterInputString) > 20) { + return null; + } + + return collect(preg_split('/[|,|]+/', $filterInputString)) + ->map(fn($keyword) => trim($keyword)) + ->filter() // Remove empty strings + ->all(); + } + + /** + * Filters servers based on allowed types and keywords. + */ + private function filterServers(array $servers, array $allowedTypes, ?array $filterKeywords): array + { + return collect($servers)->filter(function ($server) use ($allowedTypes, $filterKeywords) { + // Condition 1: Server type must be in the list of allowed types + if ($allowedTypes && !in_array($server['type'], $allowedTypes)) { + return false; // Filter out (don't keep) + } + + // Condition 2: If filterKeywords are provided, at least one keyword must match + if (!empty($filterKeywords)) { // Check if $filterKeywords is not empty + $keywordMatch = collect($filterKeywords)->contains(function ($keyword) use ($server) { + return stripos($server['name'], $keyword) !== false + || in_array($keyword, $server['tags'] ?? []); + }); + if (!$keywordMatch) { + return false; // Filter out if no keywords match + } + } + // Keep the server if its type is allowed AND (no filter keywords OR at least one keyword matched) + return true; + })->values()->all(); + } + + private function getClientInfo(Request $request): array + { + $flag = strtolower($request->input('flag') ?? $request->header('User-Agent', '')); + + $clientName = null; + $clientVersion = null; + + if (preg_match('/([a-zA-Z0-9\-_]+)[\/\s]+(v?[0-9]+(?:\.[0-9]+){0,2})/', $flag, $matches)) { + $potentialName = strtolower($matches[1]); + $clientVersion = preg_replace('/^v/', '', $matches[2]); + + if (in_array($potentialName, app('protocols.flags'))) { + $clientName = $potentialName; + } + } + + if (!$clientName) { + $flags = collect(app('protocols.flags'))->sortByDesc(fn($f) => strlen($f))->values()->all(); + foreach ($flags as $name) { + if (stripos($flag, $name) !== false) { + $clientName = $name; + if (!$clientVersion) { + $pattern = '/' . preg_quote($name, '/') . '[\/\s]+(v?[0-9]+(?:\.[0-9]+){0,2})/i'; + if (preg_match($pattern, $flag, $vMatches)) { + $clientVersion = preg_replace('/^v/', '', $vMatches[1]); + } + } + break; + } + } + } + + if (!$clientVersion) { + if (preg_match('/\/v?(\d+(?:\.\d+){0,2})/', $flag, $matches)) { + $clientVersion = $matches[1]; + } + } + + return [ + 'flag' => $flag, + 'name' => $clientName, + 'version' => $clientVersion + ]; + } + + private function setSubscribeInfoToServers(&$servers, $user, $rejectServerCount = 0) + { + if (!isset($servers[0])) + return; + if ($rejectServerCount > 0) { + array_unshift($servers, array_merge($servers[0], [ + 'name' => "过滤掉{$rejectServerCount}条线路", + ])); + } + if (!(int) admin_setting('show_info_to_server_enable', 0)) + return; + $useTraffic = $user['u'] + $user['d']; + $totalTraffic = $user['transfer_enable']; + $remainingTraffic = Helper::trafficConvert($totalTraffic - $useTraffic); + $expiredDate = $user['expired_at'] ? date('Y-m-d', $user['expired_at']) : __('长期有效'); + $userService = new UserService(); + $resetDay = $userService->getResetDay($user); + array_unshift($servers, array_merge($servers[0], [ + 'name' => "套餐到期:{$expiredDate}", + ])); + if ($resetDay) { + array_unshift($servers, array_merge($servers[0], [ + 'name' => "距离下次重置剩余:{$resetDay} 天", + ])); + } + array_unshift($servers, array_merge($servers[0], [ + 'name' => "剩余流量:{$remainingTraffic}", + ])); + } + + private function addPrefixToServerName(array $servers): array + { + if (!admin_setting('show_protocol_to_server_enable', false)) { + return $servers; + } + return collect($servers) + ->map(function (array $server): array { + $server['name'] = $this->getPrefixedServerName($server); + return $server; + }) + ->all(); + } + + private function getPrefixedServerName(array $server): string + { + $type = $server['type'] ?? ''; + if (!isset(self::PROTOCOL_PREFIXES[$type])) { + return $server['name'] ?? ''; + } + $prefix = is_array(self::PROTOCOL_PREFIXES[$type]) + ? self::PROTOCOL_PREFIXES[$type][$server['protocol_settings']['version'] ?? 1] ?? '' + : self::PROTOCOL_PREFIXES[$type]; + return $prefix . ($server['name'] ?? ''); + } +} diff --git a/Xboard/app/Http/Controllers/V1/Guest/CommController.php b/Xboard/app/Http/Controllers/V1/Guest/CommController.php new file mode 100644 index 0000000..00c05c7 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Guest/CommController.php @@ -0,0 +1,39 @@ + admin_setting('tos_url'), + 'is_email_verify' => (int) admin_setting('email_verify', 0) ? 1 : 0, + 'is_invite_force' => (int) admin_setting('invite_force', 0) ? 1 : 0, + 'email_whitelist_suffix' => (int) admin_setting('email_whitelist_enable', 0) + ? Helper::getEmailSuffix() + : 0, + 'is_captcha' => (int) admin_setting('captcha_enable', 0) ? 1 : 0, + 'captcha_type' => admin_setting('captcha_type', 'recaptcha'), + 'recaptcha_site_key' => admin_setting('recaptcha_site_key'), + 'recaptcha_v3_site_key' => admin_setting('recaptcha_v3_site_key'), + 'recaptcha_v3_score_threshold' => admin_setting('recaptcha_v3_score_threshold', 0.5), + 'turnstile_site_key' => admin_setting('turnstile_site_key'), + 'app_description' => admin_setting('app_description'), + 'app_url' => admin_setting('app_url'), + 'logo' => admin_setting('logo'), + // 保持向后兼容 + 'is_recaptcha' => (int) admin_setting('captcha_enable', 0) ? 1 : 0, + ]; + + $data = HookManager::filter('guest_comm_config', $data); + + return $this->success($data); + } +} diff --git a/Xboard/app/Http/Controllers/V1/Guest/PaymentController.php b/Xboard/app/Http/Controllers/V1/Guest/PaymentController.php new file mode 100644 index 0000000..31f444e --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Guest/PaymentController.php @@ -0,0 +1,52 @@ +notify($request->input()); + if (!$verify) { + HookManager::call('payment.notify.failed', [$method, $uuid, $request]); + return $this->fail([422, 'verify error']); + } + HookManager::call('payment.notify.verified', $verify); + if (!$this->handle($verify['trade_no'], $verify['callback_no'])) { + return $this->fail([400, 'handle error']); + } + return (isset($verify['custom_result']) ? $verify['custom_result'] : 'success'); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, 'fail']); + } + } + + private function handle($tradeNo, $callbackNo) + { + $order = Order::where('trade_no', $tradeNo)->first(); + if (!$order) { + return $this->fail([400202, 'order is not found']); + } + if ($order->status !== Order::STATUS_PENDING) + return true; + $orderService = new OrderService($order); + if (!$orderService->paid($callbackNo)) { + return false; + } + + HookManager::call('payment.notify.success', $order); + return true; + } +} diff --git a/Xboard/app/Http/Controllers/V1/Guest/PlanController.php b/Xboard/app/Http/Controllers/V1/Guest/PlanController.php new file mode 100644 index 0000000..4348a25 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Guest/PlanController.php @@ -0,0 +1,25 @@ +planService = $planService; + } + public function fetch(Request $request) + { + $plan = $this->planService->getAvailablePlans(); + return $this->success(PlanResource::collection($plan)); + } +} diff --git a/Xboard/app/Http/Controllers/V1/Guest/TelegramController.php b/Xboard/app/Http/Controllers/V1/Guest/TelegramController.php new file mode 100644 index 0000000..01369fa --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Guest/TelegramController.php @@ -0,0 +1,126 @@ +telegramService = $telegramService; + $this->userService = $userService; + } + + public function webhook(Request $request): void + { + $expectedToken = md5(admin_setting('telegram_bot_token')); + if ($request->input('access_token') !== $expectedToken) { + throw new ApiException('access_token is error', 401); + } + + $data = $request->json()->all(); + + $this->formatMessage($data); + $this->formatChatJoinRequest($data); + $this->handle(); + } + + private function handle(): void + { + if (!$this->msg) + return; + $msg = $this->msg; + $this->processBotName($msg); + try { + HookManager::call('telegram.message.before', [$msg]); + $handled = HookManager::filter('telegram.message.handle', false, [$msg]); + if (!$handled) { + HookManager::call('telegram.message.unhandled', [$msg]); + } + HookManager::call('telegram.message.after', [$msg]); + } catch (\Exception $e) { + HookManager::call('telegram.message.error', [$msg, $e]); + $this->telegramService->sendMessage($msg->chat_id, $e->getMessage()); + } + } + + private function processBotName(object $msg): void + { + $commandParts = explode('@', $msg->command); + + if (count($commandParts) === 2) { + $botName = $this->getBotName(); + if ($commandParts[1] === $botName) { + $msg->command = $commandParts[0]; + } + } + } + + private function getBotName(): string + { + $response = $this->telegramService->getMe(); + return $response->result->username; + } + + private function formatMessage(array $data): void + { + if (!isset($data['message']['text'])) + return; + + $message = $data['message']; + $text = explode(' ', $message['text']); + + $this->msg = (object) [ + 'command' => $text[0], + 'args' => array_slice($text, 1), + 'chat_id' => $message['chat']['id'], + 'message_id' => $message['message_id'], + 'message_type' => 'message', + 'text' => $message['text'], + 'is_private' => $message['chat']['type'] === 'private', + ]; + + if (isset($message['reply_to_message']['text'])) { + $this->msg->message_type = 'reply_message'; + $this->msg->reply_text = $message['reply_to_message']['text']; + } + } + + private function formatChatJoinRequest(array $data): void + { + $joinRequest = $data['chat_join_request'] ?? null; + if (!$joinRequest) + return; + + $chatId = $joinRequest['chat']['id'] ?? null; + $userId = $joinRequest['from']['id'] ?? null; + + if (!$chatId || !$userId) + return; + + $user = User::where('telegram_id', $userId)->first(); + + if (!$user) { + $this->telegramService->declineChatJoinRequest($chatId, $userId); + return; + } + + if (!$this->userService->isAvailable($user)) { + $this->telegramService->declineChatJoinRequest($chatId, $userId); + return; + } + + $this->telegramService->approveChatJoinRequest($chatId, $userId); + } +} diff --git a/Xboard/app/Http/Controllers/V1/Passport/AuthController.php b/Xboard/app/Http/Controllers/V1/Passport/AuthController.php new file mode 100644 index 0000000..5464426 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Passport/AuthController.php @@ -0,0 +1,175 @@ +mailLinkService = $mailLinkService; + $this->registerService = $registerService; + $this->loginService = $loginService; + } + + /** + * 通过邮件链接登录 + */ + public function loginWithMailLink(Request $request) + { + $params = $request->validate([ + 'email' => 'required|email:strict', + 'redirect' => 'nullable' + ]); + + [$success, $result] = $this->mailLinkService->handleMailLink( + $params['email'], + $request->input('redirect') + ); + + if (!$success) { + return $this->fail($result); + } + + return $this->success($result); + } + + /** + * 用户注册 + */ + public function register(AuthRegister $request) + { + [$success, $result] = $this->registerService->register($request); + + if (!$success) { + return $this->fail($result); + } + + $authService = new AuthService($result); + return $this->success($authService->generateAuthData()); + } + + /** + * 用户登录 + */ + public function login(AuthLogin $request) + { + $email = $request->input('email'); + $password = $request->input('password'); + + [$success, $result] = $this->loginService->login($email, $password); + + if (!$success) { + return $this->fail($result); + } + + $authService = new AuthService($result); + return $this->success($authService->generateAuthData()); + } + + /** + * 通过token登录 + */ + public function token2Login(Request $request) + { + // 处理直接通过token重定向 + if ($token = $request->input('token')) { + $redirect = '/#/login?verify=' . $token . '&redirect=' . ($request->input('redirect', 'dashboard')); + + return redirect()->to( + admin_setting('app_url') + ? admin_setting('app_url') . $redirect + : url($redirect) + ); + } + + // 处理通过验证码登录 + if ($verify = $request->input('verify')) { + $userId = $this->mailLinkService->handleTokenLogin($verify); + + if (!$userId) { + return response()->json([ + 'message' => __('Token error') + ], 400); + } + + $user = \App\Models\User::find($userId); + + if (!$user) { + return response()->json([ + 'message' => __('User not found') + ], 400); + } + + $authService = new AuthService($user); + + return response()->json([ + 'data' => $authService->generateAuthData() + ]); + } + + return response()->json([ + 'message' => __('Invalid request') + ], 400); + } + + /** + * 获取快速登录URL + */ + public function getQuickLoginUrl(Request $request) + { + $authorization = $request->input('auth_data') ?? $request->header('authorization'); + + if (!$authorization) { + return response()->json([ + 'message' => ResponseEnum::CLIENT_HTTP_UNAUTHORIZED + ], 401); + } + + $user = AuthService::findUserByBearerToken($authorization); + + if (!$user) { + return response()->json([ + 'message' => ResponseEnum::CLIENT_HTTP_UNAUTHORIZED_EXPIRED + ], 401); + } + + $url = $this->loginService->generateQuickLoginUrl($user, $request->input('redirect')); + return $this->success($url); + } + + /** + * 忘记密码处理 + */ + public function forget(AuthForget $request) + { + [$success, $result] = $this->loginService->resetPassword( + $request->input('email'), + $request->input('email_code'), + $request->input('password') + ); + + if (!$success) { + return $this->fail($result); + } + + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V1/Passport/CommController.php b/Xboard/app/Http/Controllers/V1/Passport/CommController.php new file mode 100644 index 0000000..663badf --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Passport/CommController.php @@ -0,0 +1,76 @@ +verify($request); + if (!$captchaValid) { + return $this->fail($captchaError); + } + + $email = $request->input('email'); + + // 检查白名单后缀限制 + if ((int) admin_setting('email_whitelist_enable', 0)) { + $isRegisteredEmail = User::byEmail($email)->exists(); + if (!$isRegisteredEmail) { + $allowedSuffixes = Helper::getEmailSuffix(); + $emailSuffix = substr(strrchr($email, '@'), 1); + + if (!in_array($emailSuffix, $allowedSuffixes)) { + return $this->fail([400, __('Email suffix is not in whitelist')]); + } + } + } + + if (Cache::get(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email))) { + return $this->fail([400, __('Email verification code has been sent, please request again later')]); + } + $code = rand(100000, 999999); + $subject = admin_setting('app_name', 'XBoard') . __('Email verification code'); + + SendEmailJob::dispatch([ + 'email' => $email, + 'subject' => $subject, + 'template_name' => 'verify', + 'template_value' => [ + 'name' => admin_setting('app_name', 'XBoard'), + 'code' => $code, + 'url' => admin_setting('app_url') + ] + ]); + + Cache::put(CacheKey::get('EMAIL_VERIFY_CODE', $email), $code, 300); + Cache::put(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email), time(), 60); + return $this->success(true); + } + + public function pv(Request $request) + { + $inviteCode = InviteCode::where('code', $request->input('invite_code'))->first(); + if ($inviteCode) { + $inviteCode->pv = $inviteCode->pv + 1; + $inviteCode->save(); + } + + return $this->success(true); + } + +} diff --git a/Xboard/app/Http/Controllers/V1/Server/ShadowsocksTidalabController.php b/Xboard/app/Http/Controllers/V1/Server/ShadowsocksTidalabController.php new file mode 100644 index 0000000..62e5af9 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Server/ShadowsocksTidalabController.php @@ -0,0 +1,65 @@ +attributes->get('node_info'); + Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server->id), time(), 3600); + $users = ServerService::getAvailableUsers($server); + $result = []; + foreach ($users as $user) { + array_push($result, [ + 'id' => $user->id, + 'port' => $server->server_port, + 'cipher' => $server->cipher, + 'secret' => $user->uuid + ]); + } + $eTag = sha1(json_encode($result)); + if (strpos($request->header('If-None-Match'), $eTag) !== false ) { + return response(null,304); + } + return response([ + 'data' => $result + ])->header('ETag', "\"{$eTag}\""); + } + + // 后端提交数据 + public function submit(Request $request) + { + $server = $request->attributes->get('node_info'); + $data = json_decode(request()->getContent(), true); + Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server->id), count($data), 3600); + Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600); + $userService = new UserService(); + $formatData = []; + + foreach ($data as $item) { + $formatData[$item['user_id']] = [$item['u'], $item['d']]; + } + $userService->trafficFetch($server, 'shadowsocks', $formatData); + + return response([ + 'ret' => 1, + 'msg' => 'ok' + ]); + } +} \ No newline at end of file diff --git a/Xboard/app/Http/Controllers/V1/Server/TrojanTidalabController.php b/Xboard/app/Http/Controllers/V1/Server/TrojanTidalabController.php new file mode 100644 index 0000000..ceff48f --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Server/TrojanTidalabController.php @@ -0,0 +1,108 @@ +attributes->get('node_info'); + if ($server->type !== 'trojan') { + return $this->fail([400, '节点不存在']); + } + Cache::put(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server->id), time(), 3600); + $users = ServerService::getAvailableUsers($server); + $result = []; + foreach ($users as $user) { + $user->trojan_user = [ + "password" => $user->uuid, + ]; + unset($user->uuid); + array_push($result, $user); + } + $eTag = sha1(json_encode($result)); + if (strpos($request->header('If-None-Match'), $eTag) !== false) { + return response(null, 304); + } + return response([ + 'msg' => 'ok', + 'data' => $result, + ])->header('ETag', "\"{$eTag}\""); + } + + // 后端提交数据 + public function submit(Request $request) + { + $server = $request->attributes->get('node_info'); + if ($server->type !== 'trojan') { + return $this->fail([400, '节点不存在']); + } + $data = json_decode(request()->getContent(), true); + Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600); + Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600); + $userService = new UserService(); + $formatData = []; + foreach ($data as $item) { + $formatData[$item['user_id']] = [$item['u'], $item['d']]; + } + $userService->trafficFetch($server, 'trojan', $formatData); + + return response([ + 'ret' => 1, + 'msg' => 'ok' + ]); + } + + // 后端获取配置 + public function config(Request $request) + { + $server = $request->attributes->get('node_info'); + if ($server->type !== 'trojan') { + return $this->fail([400, '节点不存在']); + } + $request->validate([ + 'node_id' => 'required', + 'local_port' => 'required' + ], [ + 'node_id.required' => '节点ID不能为空', + 'local_port.required' => '本地端口不能为空' + ]); + try { + $json = $this->getTrojanConfig($server, $request->input('local_port')); + } catch (\Exception $e) { + \Log::error($e); + return $this->fail([500, '配置获取失败']); + } + + return (json_encode($json, JSON_UNESCAPED_UNICODE)); + } + + private function getTrojanConfig($server, int $localPort) + { + $protocolSettings = $server->protocol_settings; + $json = json_decode(self::TROJAN_CONFIG); + $json->local_port = $server->server_port; + $json->ssl->sni = data_get($protocolSettings, 'server_name', $server->host); + $json->ssl->cert = "/root/.cert/server.crt"; + $json->ssl->key = "/root/.cert/server.key"; + $json->api->api_port = $localPort; + return $json; + } +} \ No newline at end of file diff --git a/Xboard/app/Http/Controllers/V1/Server/UniProxyController.php b/Xboard/app/Http/Controllers/V1/Server/UniProxyController.php new file mode 100644 index 0000000..5901c4b --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/Server/UniProxyController.php @@ -0,0 +1,178 @@ +attributes->get('node_info'); + } + + // 后端获取用户 + public function user(Request $request) + { + ini_set('memory_limit', -1); + $node = $this->getNodeInfo($request); + $nodeType = $node->type; + $nodeId = $node->id; + Cache::put(CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_CHECK_AT', $nodeId), time(), 3600); + $users = ServerService::getAvailableUsers($node); + + $response['users'] = $users; + + $eTag = sha1(json_encode($response)); + if (strpos($request->header('If-None-Match', ''), $eTag) !== false) { + return response(null, 304); + } + + return response($response)->header('ETag', "\"{$eTag}\""); + } + + // 后端提交数据 + public function push(Request $request) + { + $res = json_decode(request()->getContent(), true); + if (!is_array($res)) { + return $this->fail([422, 'Invalid data format']); + } + $data = array_filter($res, function ($item) { + return is_array($item) + && count($item) === 2 + && is_numeric($item[0]) + && is_numeric($item[1]); + }); + if (empty($data)) { + return $this->success(true); + } + $node = $this->getNodeInfo($request); + $nodeType = $node->type; + $nodeId = $node->id; + + Cache::put( + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_ONLINE_USER', $nodeId), + count($data), + 3600 + ); + Cache::put( + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_PUSH_AT', $nodeId), + time(), + 3600 + ); + + $userService = new UserService(); + $userService->trafficFetch($node, $nodeType, $data); + return $this->success(true); + } + + // 后端获取配置 + public function config(Request $request) + { + $node = $this->getNodeInfo($request); + $response = ServerService::buildNodeConfig($node); + + $response['base_config'] = [ + 'push_interval' => (int) admin_setting('server_push_interval', 60), + 'pull_interval' => (int) admin_setting('server_pull_interval', 60) + ]; + + $eTag = sha1(json_encode($response)); + if (strpos($request->header('If-None-Match', ''), $eTag) !== false) { + return response(null, 304); + } + return response($response)->header('ETag', "\"{$eTag}\""); + } + + // 获取在线用户数据 + public function alivelist(Request $request): JsonResponse + { + $node = $this->getNodeInfo($request); + $deviceLimitUsers = ServerService::getAvailableUsers($node) + ->where('device_limit', '>', 0); + + $alive = $this->deviceStateService->getAliveList(collect($deviceLimitUsers)); + + return response()->json(['alive' => (object) $alive]); + } + + // 后端提交在线数据 + public function alive(Request $request): JsonResponse + { + $node = $this->getNodeInfo($request); + $data = json_decode(request()->getContent(), true); + if ($data === null) { + return response()->json([ + 'error' => 'Invalid online data' + ], 400); + } + + foreach ($data as $uid => $ips) { + $this->deviceStateService->setDevices((int) $uid, $node->id, $ips); + } + + return response()->json(['data' => true]); + } + + // 提交节点负载状态 + public function status(Request $request): JsonResponse + { + $node = $this->getNodeInfo($request); + + $data = $request->validate([ + 'cpu' => 'required|numeric|min:0|max:100', + 'mem.total' => 'required|integer|min:0', + 'mem.used' => 'required|integer|min:0', + 'swap.total' => 'required|integer|min:0', + 'swap.used' => 'required|integer|min:0', + 'disk.total' => 'required|integer|min:0', + 'disk.used' => 'required|integer|min:0', + ]); + + $nodeType = $node->type; + $nodeId = $node->id; + + $statusData = [ + 'cpu' => (float) $data['cpu'], + 'mem' => [ + 'total' => (int) $data['mem']['total'], + 'used' => (int) $data['mem']['used'], + ], + 'swap' => [ + 'total' => (int) $data['swap']['total'], + 'used' => (int) $data['swap']['used'], + ], + 'disk' => [ + 'total' => (int) $data['disk']['total'], + 'used' => (int) $data['disk']['used'], + ], + 'updated_at' => now()->timestamp, + ]; + + $cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3); + cache([ + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LOAD_STATUS', $nodeId) => $statusData, + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_LOAD_AT', $nodeId) => now()->timestamp, + ], $cacheTime); + + return response()->json(['data' => true, "code" => 0, "message" => "success"]); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/CommController.php b/Xboard/app/Http/Controllers/V1/User/CommController.php new file mode 100644 index 0000000..fa0ae27 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/CommController.php @@ -0,0 +1,39 @@ + (int)admin_setting('telegram_bot_enable', 0), + 'telegram_discuss_link' => admin_setting('telegram_discuss_link'), + 'stripe_pk' => admin_setting('stripe_pk_live'), + 'withdraw_methods' => admin_setting('commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT), + 'withdraw_close' => (int)admin_setting('withdraw_close_enable', 0), + 'currency' => admin_setting('currency', 'CNY'), + 'currency_symbol' => admin_setting('currency_symbol', '¥'), + 'commission_distribution_enable' => (int)admin_setting('commission_distribution_enable', 0), + 'commission_distribution_l1' => admin_setting('commission_distribution_l1'), + 'commission_distribution_l2' => admin_setting('commission_distribution_l2'), + 'commission_distribution_l3' => admin_setting('commission_distribution_l3') + ]; + return $this->success($data); + } + + public function getStripePublicKey(Request $request) + { + $payment = Payment::where('id', $request->input('id')) + ->where('payment', 'StripeCredit') + ->first(); + if (!$payment) throw new ApiException('payment is not found'); + return $this->success($payment->config['stripe_pk_live']); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/CouponController.php b/Xboard/app/Http/Controllers/V1/User/CouponController.php new file mode 100644 index 0000000..b7c091f --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/CouponController.php @@ -0,0 +1,25 @@ +input('code'))) { + return $this->fail([422, __('Coupon cannot be empty')]); + } + $couponService = new CouponService($request->input('code')); + $couponService->setPlanId($request->input('plan_id')); + $couponService->setUserId($request->user()->id); + $couponService->setPeriod($request->input('period')); + $couponService->check(); + return $this->success(CouponResource::make($couponService->getCoupon())); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/GiftCardController.php b/Xboard/app/Http/Controllers/V1/User/GiftCardController.php new file mode 100644 index 0000000..507c164 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/GiftCardController.php @@ -0,0 +1,193 @@ +input('code')); + $giftCardService->setUser($request->user()); + + // 1. 验证礼品卡本身是否有效 (如不存在、已过期、已禁用) + $giftCardService->validateIsActive(); + + // 2. 检查用户是否满足使用条件,但不在此处抛出异常 + $eligibility = $giftCardService->checkUserEligibility(); + + // 3. 获取卡片信息和奖励预览 + $codeInfo = $giftCardService->getCodeInfo(); + $rewardPreview = $giftCardService->previewRewards(); + + return $this->success([ + 'code_info' => $codeInfo, // 这里面已经包含 plan_info + 'reward_preview' => $rewardPreview, + 'can_redeem' => $eligibility['can_redeem'], + 'reason' => $eligibility['reason'], + ]); + + } catch (ApiException $e) { + // 这里只捕获 validateIsActive 抛出的异常 + return $this->fail([400, $e->getMessage()]); + } catch (\Exception $e) { + Log::error('礼品卡查询失败', [ + 'code' => $request->input('code'), + 'user_id' => $request->user()->id, + 'error' => $e->getMessage(), + ]); + return $this->fail([500, '查询失败,请稍后重试']); + } + } + + /** + * 使用兑换码 + */ + public function redeem(GiftCardRedeemRequest $request) + { + try { + $giftCardService = new GiftCardService($request->input('code')); + $giftCardService->setUser($request->user()); + $giftCardService->validate(); + + // 使用礼品卡 + $result = $giftCardService->redeem([ + // 'ip_address' => $request->ip(), + 'user_agent' => $request->userAgent(), + ]); + + Log::info('礼品卡使用成功', [ + 'code' => $request->input('code'), + 'user_id' => $request->user()->id, + 'rewards' => $result['rewards'], + ]); + + return $this->success([ + 'message' => '兑换成功!', + 'rewards' => $result['rewards'], + 'invite_rewards' => $result['invite_rewards'], + 'template_name' => $result['template_name'], + ]); + + } catch (ApiException $e) { + return $this->fail([400, $e->getMessage()]); + } catch (\Exception $e) { + Log::error('礼品卡使用失败', [ + 'code' => $request->input('code'), + 'user_id' => $request->user()->id, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + return $this->fail([500, '兑换失败,请稍后重试']); + } + } + + /** + * 获取用户兑换记录 + */ + public function history(Request $request) + { + $request->validate([ + 'page' => 'integer|min:1', + 'per_page' => 'integer|min:1|max:100', + ]); + + $perPage = $request->input('per_page', 15); + + $usages = GiftCardUsage::with(['template', 'code']) + ->where('user_id', $request->user()->id) + ->orderBy('created_at', 'desc') + ->paginate($perPage); + + $data = $usages->getCollection()->map(function (GiftCardUsage $usage) { + return [ + 'id' => $usage->id, + 'code' => ($usage->code instanceof \App\Models\GiftCardCode && $usage->code->code) + ? (substr($usage->code->code, 0, 8) . '****') + : '', + 'template_name' => $usage->template->name ?? '', + 'template_type' => $usage->template->type ?? '', + 'template_type_name' => $usage->template->type_name ?? '', + 'rewards_given' => $usage->rewards_given, + 'invite_rewards' => $usage->invite_rewards, + 'multiplier_applied' => $usage->multiplier_applied, + 'created_at' => $usage->created_at, + ]; + })->values(); + return response()->json([ + 'data' => $data, + 'pagination' => [ + 'current_page' => $usages->currentPage(), + 'last_page' => $usages->lastPage(), + 'per_page' => $usages->perPage(), + 'total' => $usages->total(), + ], + ]); + } + + /** + * 获取兑换记录详情 + */ + public function detail(Request $request) + { + $request->validate([ + 'id' => 'required|integer|exists:v2_gift_card_usage,id', + ]); + + $usage = GiftCardUsage::with(['template', 'code', 'inviteUser']) + ->where('user_id', $request->user()->id) + ->where('id', $request->input('id')) + ->first(); + + if (!$usage) { + return $this->fail([404, '记录不存在']); + } + + return $this->success([ + 'id' => $usage->id, + 'code' => $usage->code->code ?? '', + 'template' => [ + 'name' => $usage->template->name ?? '', + 'description' => $usage->template->description ?? '', + 'type' => $usage->template->type ?? '', + 'type_name' => $usage->template->type_name ?? '', + 'icon' => $usage->template->icon ?? '', + 'theme_color' => $usage->template->theme_color ?? '', + ], + 'rewards_given' => $usage->rewards_given, + 'invite_rewards' => $usage->invite_rewards, + 'invite_user' => $usage->inviteUser ? [ + 'id' => $usage->inviteUser->id ?? '', + 'email' => isset($usage->inviteUser->email) ? (substr($usage->inviteUser->email, 0, 3) . '***@***') : '', + ] : null, + 'user_level_at_use' => $usage->user_level_at_use, + 'plan_id_at_use' => $usage->plan_id_at_use, + 'multiplier_applied' => $usage->multiplier_applied, + // 'ip_address' => $usage->ip_address, + 'notes' => $usage->notes, + 'created_at' => $usage->created_at, + ]); + } + + /** + * 获取可用的礼品卡类型 + */ + public function types(Request $request) + { + return $this->success([ + 'types' => \App\Models\GiftCardTemplate::getTypeMap(), + ]); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/InviteController.php b/Xboard/app/Http/Controllers/V1/User/InviteController.php new file mode 100644 index 0000000..cbde315 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/InviteController.php @@ -0,0 +1,79 @@ +user()->id)->where('status', 0)->count() >= admin_setting('invite_gen_limit', 5)) { + return $this->fail([400,__('The maximum number of creations has been reached')]); + } + $inviteCode = new InviteCode(); + $inviteCode->user_id = $request->user()->id; + $inviteCode->code = Helper::randomChar(8); + return $this->success($inviteCode->save()); + } + + public function details(Request $request) + { + $current = $request->input('current') ? $request->input('current') : 1; + $pageSize = $request->input('page_size') >= 10 ? $request->input('page_size') : 10; + $builder = CommissionLog::where('invite_user_id', $request->user()->id) + ->where('get_amount', '>', 0) + ->orderBy('created_at', 'DESC'); + $total = $builder->count(); + $details = $builder->forPage($current, $pageSize) + ->get(); + return response([ + 'data' => ComissionLogResource::collection($details), + 'total' => $total + ]); + } + + public function fetch(Request $request) + { + $commission_rate = admin_setting('invite_commission', 10); + $user = User::find($request->user()->id) + ->load(['codes' => fn($query) => $query->where('status', 0)]); + if ($user->commission_rate) { + $commission_rate = $user->commission_rate; + } + $uncheck_commission_balance = (int)Order::where('status', 3) + ->where('commission_status', 0) + ->where('invite_user_id', $user->id) + ->sum('commission_balance'); + if (admin_setting('commission_distribution_enable', 0)) { + $uncheck_commission_balance = $uncheck_commission_balance * (admin_setting('commission_distribution_l1') / 100); + } + $stat = [ + //已注册用户数 + (int)User::where('invite_user_id', $user->id)->count(), + //有效的佣金 + (int)CommissionLog::where('invite_user_id', $user->id) + ->sum('get_amount'), + //确认中的佣金 + $uncheck_commission_balance, + //佣金比例 + (int)$commission_rate, + //可用佣金 + (int)$user->commission_balance + ]; + $data = [ + 'codes' => InviteCodeResource::collection($user->codes), + 'stat' => $stat + ]; + return $this->success($data); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/KnowledgeController.php b/Xboard/app/Http/Controllers/V1/User/KnowledgeController.php new file mode 100644 index 0000000..e3646c9 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/KnowledgeController.php @@ -0,0 +1,150 @@ +userService = $userService; + } + + public function fetch(Request $request) + { + $request->validate([ + 'id' => 'nullable|sometimes|integer|min:1', + 'language' => 'nullable|sometimes|string|max:10', + 'keyword' => 'nullable|sometimes|string|max:255', + ]); + + return $request->input('id') + ? $this->fetchSingle($request) + : $this->fetchList($request); + } + + private function fetchSingle(Request $request) + { + $knowledge = $this->buildKnowledgeQuery() + ->where('id', $request->input('id')) + ->first(); + + if (!$knowledge) { + return $this->fail([500, __('Article does not exist')]); + } + + $knowledge = $knowledge->toArray(); + $knowledge = $this->processKnowledgeContent($knowledge, $request->user()); + + return $this->success(KnowledgeResource::make($knowledge)); + } + + private function fetchList(Request $request) + { + $builder = $this->buildKnowledgeQuery(['id', 'category', 'title', 'updated_at', 'body']) + ->where('language', $request->input('language')) + ->orderBy('sort', 'ASC'); + + $keyword = $request->input('keyword'); + if ($keyword) { + $builder = $builder->where(function ($query) use ($keyword) { + $query->where('title', 'LIKE', "%{$keyword}%") + ->orWhere('body', 'LIKE', "%{$keyword}%"); + }); + } + + $knowledges = $builder->get() + ->map(function ($knowledge) use ($request) { + $knowledge = $knowledge->toArray(); + $knowledge = $this->processKnowledgeContent($knowledge, $request->user()); + return KnowledgeResource::make($knowledge); + }) + ->groupBy('category'); + + return $this->success($knowledges); + } + + private function buildKnowledgeQuery(array $select = ['*']) + { + return Knowledge::select($select)->where('show', 1); + } + + private function processKnowledgeContent(array $knowledge, User $user): array + { + if (!isset($knowledge['body'])) { + return $knowledge; + } + + if (!$this->userService->isAvailable($user)) { + $this->formatAccessData($knowledge['body']); + } + $subscribeUrl = Helper::getSubscribeUrl($user['token']); + $knowledge['body'] = $this->replacePlaceholders($knowledge['body'], $subscribeUrl); + + return $knowledge; + } + + private function formatAccessData(&$body): void + { + $rules = [ + [ + 'type' => 'regex', + 'pattern' => '/(.*?)/s', + 'replacement' => '
' . __('You must have a valid subscription to view content in this area') . '
' + ] + ]; + + $this->applyReplacementRules($body, $rules); + } + + private function replacePlaceholders(string $body, string $subscribeUrl): string + { + $rules = [ + [ + 'type' => 'string', + 'search' => '{{siteName}}', + 'replacement' => admin_setting('app_name', 'XBoard') + ], + [ + 'type' => 'string', + 'search' => '{{subscribeUrl}}', + 'replacement' => $subscribeUrl + ], + [ + 'type' => 'string', + 'search' => '{{urlEncodeSubscribeUrl}}', + 'replacement' => urlencode($subscribeUrl) + ], + [ + 'type' => 'string', + 'search' => '{{safeBase64SubscribeUrl}}', + 'replacement' => str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($subscribeUrl)) + ] + ]; + + $this->applyReplacementRules($body, $rules); + return $body; + } + + private function applyReplacementRules(string &$body, array $rules): void + { + foreach ($rules as $rule) { + if ($rule['type'] === 'regex') { + $body = preg_replace($rule['pattern'], $rule['replacement'], $body); + } else { + $body = str_replace($rule['search'], $rule['replacement'], $body); + } + } + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/NoticeController.php b/Xboard/app/Http/Controllers/V1/User/NoticeController.php new file mode 100644 index 0000000..9382e06 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/NoticeController.php @@ -0,0 +1,26 @@ +input('current') ? $request->input('current') : 1; + $pageSize = 5; + $model = Notice::orderBy('sort', 'ASC') + ->orderBy('id', 'DESC') + ->where('show', true); + $total = $model->count(); + $res = $model->forPage($current, $pageSize) + ->get(); + return response([ + 'data' => $res, + 'total' => $total + ]); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/OrderController.php b/Xboard/app/Http/Controllers/V1/User/OrderController.php new file mode 100644 index 0000000..7b28128 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/OrderController.php @@ -0,0 +1,212 @@ +validate([ + 'status' => 'nullable|integer|in:0,1,2,3', + ]); + $orders = Order::with('plan') + ->where('user_id', $request->user()->id) + ->when($request->input('status') !== null, function ($query) use ($request) { + $query->where('status', $request->input('status')); + }) + ->orderBy('created_at', 'DESC') + ->get(); + + return $this->success(OrderResource::collection($orders)); + } + + public function detail(Request $request) + { + $request->validate([ + 'trade_no' => 'required|string', + ]); + $order = Order::with(['payment', 'plan']) + ->where('user_id', $request->user()->id) + ->where('trade_no', $request->input('trade_no')) + ->first(); + if (!$order) { + return $this->fail([400, __('Order does not exist or has been paid')]); + } + $order['try_out_plan_id'] = (int) admin_setting('try_out_plan_id'); + if (!$order->plan) { + return $this->fail([400, __('Subscription plan does not exist')]); + } + if ($order->surplus_order_ids) { + $order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get(); + } + return $this->success(OrderResource::make($order)); + } + + public function save(OrderSave $request) + { + $request->validate([ + 'plan_id' => 'required|exists:App\Models\Plan,id', + 'period' => 'required|string' + ]); + + $user = User::findOrFail($request->user()->id); + $userService = app(UserService::class); + + if ($userService->isNotCompleteOrderByUserId($user->id)) { + throw new ApiException(__('You have an unpaid or pending order, please try again later or cancel it')); + } + + $plan = Plan::findOrFail($request->input('plan_id')); + $planService = new PlanService($plan); + + $planService->validatePurchase($user, $request->input('period')); + + $order = OrderService::createFromRequest( + $user, + $plan, + $request->input('period'), + $request->input('coupon_code') + ); + + return $this->success($order->trade_no); + } + + protected function applyCoupon(Order $order, string $couponCode): void + { + $couponService = new CouponService($couponCode); + if (!$couponService->use($order)) { + throw new ApiException(__('Coupon failed')); + } + $order->coupon_id = $couponService->getId(); + } + + protected function handleUserBalance(Order $order, User $user, UserService $userService): void + { + $remainingBalance = $user->balance - $order->total_amount; + + if ($remainingBalance > 0) { + if (!$userService->addBalance($order->user_id, -$order->total_amount)) { + throw new ApiException(__('Insufficient balance')); + } + $order->balance_amount = $order->total_amount; + $order->total_amount = 0; + } else { + if (!$userService->addBalance($order->user_id, -$user->balance)) { + throw new ApiException(__('Insufficient balance')); + } + $order->balance_amount = $user->balance; + $order->total_amount = $order->total_amount - $user->balance; + } + } + + public function checkout(Request $request) + { + $tradeNo = $request->input('trade_no'); + $method = $request->input('method'); + $order = Order::where('trade_no', $tradeNo) + ->where('user_id', $request->user()->id) + ->where('status', 0) + ->first(); + if (!$order) { + return $this->fail([400, __('Order does not exist or has been paid')]); + } + // free process + if ($order->total_amount <= 0) { + $orderService = new OrderService($order); + if (!$orderService->paid($order->trade_no)) + return $this->fail([400, '支付失败']); + return response([ + 'type' => -1, + 'data' => true + ]); + } + $payment = Payment::find($method); + if (!$payment || !$payment->enable) { + return $this->fail([400, __('Payment method is not available')]); + } + $paymentService = new PaymentService($payment->payment, $payment->id); + $order->handling_amount = NULL; + if ($payment->handling_fee_fixed || $payment->handling_fee_percent) { + $order->handling_amount = (int) round(($order->total_amount * ($payment->handling_fee_percent / 100)) + $payment->handling_fee_fixed); + } + $order->payment_id = $method; + if (!$order->save()) + return $this->fail([400, __('Request failed, please try again later')]); + $result = $paymentService->pay([ + 'trade_no' => $tradeNo, + 'total_amount' => isset($order->handling_amount) ? ($order->total_amount + $order->handling_amount) : $order->total_amount, + 'user_id' => $order->user_id, + 'stripe_token' => $request->input('token') + ]); + return response([ + 'type' => $result['type'], + 'data' => $result['data'] + ]); + } + + public function check(Request $request) + { + $tradeNo = $request->input('trade_no'); + $order = Order::where('trade_no', $tradeNo) + ->where('user_id', $request->user()->id) + ->first(); + if (!$order) { + return $this->fail([400, __('Order does not exist')]); + } + return $this->success($order->status); + } + + public function getPaymentMethod() + { + $methods = Payment::select([ + 'id', + 'name', + 'payment', + 'icon', + 'handling_fee_fixed', + 'handling_fee_percent' + ]) + ->where('enable', 1) + ->orderBy('sort', 'ASC') + ->get(); + + return $this->success($methods); + } + + public function cancel(Request $request) + { + if (empty($request->input('trade_no'))) { + return $this->fail([422, __('Invalid parameter')]); + } + $order = Order::where('trade_no', $request->input('trade_no')) + ->where('user_id', $request->user()->id) + ->first(); + if (!$order) { + return $this->fail([400, __('Order does not exist')]); + } + if ($order->status !== 0) { + return $this->fail([400, __('You can only cancel pending orders')]); + } + $orderService = new OrderService($order); + if (!$orderService->cancel()) { + return $this->fail([400, __('Cancel failed')]); + } + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/PlanController.php b/Xboard/app/Http/Controllers/V1/User/PlanController.php new file mode 100644 index 0000000..7db7aad --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/PlanController.php @@ -0,0 +1,38 @@ +planService = $planService; + } + public function fetch(Request $request) + { + $user = User::find($request->user()->id); + if ($request->input('id')) { + $plan = Plan::where('id', $request->input('id'))->first(); + if (!$plan) { + return $this->fail([400, __('Subscription plan does not exist')]); + } + if (!$this->planService->isPlanAvailableForUser($plan, $user)) { + return $this->fail([400, __('Subscription plan does not exist')]); + } + return $this->success(PlanResource::make($plan)); + } + + $plans = $this->planService->getAvailablePlans(); + return $this->success(PlanResource::collection($plans)); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/ServerController.php b/Xboard/app/Http/Controllers/V1/User/ServerController.php new file mode 100644 index 0000000..f12b005 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/ServerController.php @@ -0,0 +1,31 @@ +user()->id); + $servers = []; + $userService = new UserService(); + if ($userService->isAvailable($user)) { + $servers = ServerService::getAvailableServers($user); + } + $eTag = sha1(json_encode(array_column($servers, 'cache_key'))); + if (strpos($request->header('If-None-Match', ''), $eTag) !== false ) { + return response(null,304); + } + $data = NodeResource::collection($servers); + return response([ + 'data' => $data + ])->header('ETag', "\"{$eTag}\""); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/StatController.php b/Xboard/app/Http/Controllers/V1/User/StatController.php new file mode 100644 index 0000000..11bb9c0 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/StatController.php @@ -0,0 +1,26 @@ +startOfMonth()->timestamp; + $records = StatUser::query() + ->where('user_id', $request->user()->id) + ->where('record_at', '>=', $startDate) + ->orderBy('record_at', 'DESC') + ->get(); + + $data = TrafficLogResource::collection(collect($records)); + return $this->success($data); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/TelegramController.php b/Xboard/app/Http/Controllers/V1/User/TelegramController.php new file mode 100644 index 0000000..2cf65c7 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/TelegramController.php @@ -0,0 +1,26 @@ +getMe(); + $data = [ + 'username' => $response->result->username + ]; + return $this->success($data); + } + + public function unbind(Request $request) + { + $user = User::where('user_id', $request->user()->id)->first(); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/TicketController.php b/Xboard/app/Http/Controllers/V1/User/TicketController.php new file mode 100644 index 0000000..05ca915 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/TicketController.php @@ -0,0 +1,154 @@ +input('id')) { + $ticket = Ticket::where('id', $request->input('id')) + ->where('user_id', $request->user()->id) + ->first() + ->load('message'); + if (!$ticket) { + return $this->fail([400, __('Ticket does not exist')]); + } + $ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get(); + $ticket['message']->each(function ($message) use ($ticket) { + $message['is_me'] = ($message['user_id'] == $ticket->user_id); + }); + return $this->success(TicketResource::make($ticket)->additional(['message' => true])); + } + $ticket = Ticket::where('user_id', $request->user()->id) + ->orderBy('created_at', 'DESC') + ->get(); + return $this->success(TicketResource::collection($ticket)); + } + + public function save(TicketSave $request) + { + $ticketService = new TicketService(); + $ticket = $ticketService->createTicket( + $request->user()->id, + $request->input('subject'), + $request->input('level'), + $request->input('message') + ); + HookManager::call('ticket.create.after', $ticket); + return $this->success(true); + + } + + public function reply(Request $request) + { + if (empty($request->input('id'))) { + return $this->fail([400, __('Invalid parameter')]); + } + if (empty($request->input('message'))) { + return $this->fail([400, __('Message cannot be empty')]); + } + $ticket = Ticket::where('id', $request->input('id')) + ->where('user_id', $request->user()->id) + ->first(); + if (!$ticket) { + return $this->fail([400, __('Ticket does not exist')]); + } + if ($ticket->status) { + return $this->fail([400, __('The ticket is closed and cannot be replied')]); + } + if ((int) admin_setting('ticket_must_wait_reply', 0) && $request->user()->id == $this->getLastMessage($ticket->id)->user_id) { + return $this->fail(codeResponse: [400, __('Please wait for the technical enginneer to reply')]); + } + $ticketService = new TicketService(); + if ( + !$ticketService->reply( + $ticket, + $request->input('message'), + $request->user()->id + ) + ) { + return $this->fail([400, __('Ticket reply failed')]); + } + HookManager::call('ticket.reply.user.after', $ticket); + return $this->success(true); + } + + + public function close(Request $request) + { + if (empty($request->input('id'))) { + return $this->fail([422, __('Invalid parameter')]); + } + $ticket = Ticket::where('id', $request->input('id')) + ->where('user_id', $request->user()->id) + ->first(); + if (!$ticket) { + return $this->fail([400, __('Ticket does not exist')]); + } + $ticket->status = Ticket::STATUS_CLOSED; + if (!$ticket->save()) { + return $this->fail([500, __('Close failed')]); + } + return $this->success(true); + } + + private function getLastMessage($ticketId) + { + return TicketMessage::where('ticket_id', $ticketId) + ->orderBy('id', 'DESC') + ->first(); + } + + public function withdraw(TicketWithdraw $request) + { + if ((int) admin_setting('withdraw_close_enable', 0)) { + return $this->fail([400, 'Unsupported withdraw']); + } + if ( + !in_array( + $request->input('withdraw_method'), + admin_setting('commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT) + ) + ) { + return $this->fail([422, __('Unsupported withdrawal method')]); + } + $user = User::find($request->user()->id); + $limit = admin_setting('commission_withdraw_limit', 100); + if ($limit > ($user->commission_balance / 100)) { + return $this->fail([422, __('The current required minimum withdrawal commission is :limit', ['limit' => $limit])]); + } + try { + $ticketService = new TicketService(); + $subject = __('[Commission Withdrawal Request] This ticket is opened by the system'); + $message = sprintf( + "%s\r\n%s", + __('Withdrawal method') . ":" . $request->input('withdraw_method'), + __('Withdrawal account') . ":" . $request->input('withdraw_account') + ); + $ticket = $ticketService->createTicket( + $request->user()->id, + $subject, + 2, + $message + ); + } catch (\Exception $e) { + throw $e; + } + HookManager::call('ticket.create.after', $ticket); + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V1/User/UserController.php b/Xboard/app/Http/Controllers/V1/User/UserController.php new file mode 100644 index 0000000..c561988 --- /dev/null +++ b/Xboard/app/Http/Controllers/V1/User/UserController.php @@ -0,0 +1,223 @@ +loginService = $loginService; + } + + public function getActiveSession(Request $request) + { + $user = $request->user(); + $authService = new AuthService($user); + return $this->success($authService->getSessions()); + } + + public function removeActiveSession(Request $request) + { + $user = $request->user(); + $authService = new AuthService($user); + return $this->success($authService->removeSession($request->input('session_id'))); + } + + public function checkLogin(Request $request) + { + $data = [ + 'is_login' => $request->user()?->id ? true : false + ]; + if ($request->user()?->is_admin) { + $data['is_admin'] = true; + } + return $this->success($data); + } + + public function changePassword(UserChangePassword $request) + { + $user = $request->user(); + if ( + !Helper::multiPasswordVerify( + $user->password_algo, + $user->password_salt, + $request->input('old_password'), + $user->password + ) + ) { + return $this->fail([400, __('The old password is wrong')]); + } + $user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT); + $user->password_algo = NULL; + $user->password_salt = NULL; + if (!$user->save()) { + return $this->fail([400, __('Save failed')]); + } + + $currentToken = $user->currentAccessToken(); + if ($currentToken) { + $user->tokens()->where('id', '!=', $currentToken->id)->delete(); + } else { + $user->tokens()->delete(); + } + + return $this->success(true); + } + + public function info(Request $request) + { + $user = User::where('id', $request->user()->id) + ->select([ + 'email', + 'transfer_enable', + 'last_login_at', + 'created_at', + 'banned', + 'remind_expire', + 'remind_traffic', + 'expired_at', + 'balance', + 'commission_balance', + 'plan_id', + 'discount', + 'commission_rate', + 'telegram_id', + 'uuid' + ]) + ->first(); + if (!$user) { + return $this->fail([400, __('The user does not exist')]); + } + $user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon'; + return $this->success($user); + } + + public function getStat(Request $request) + { + $stat = [ + Order::where('status', 0) + ->where('user_id', $request->user()->id) + ->count(), + Ticket::where('status', 0) + ->where('user_id', $request->user()->id) + ->count(), + User::where('invite_user_id', $request->user()->id) + ->count() + ]; + return $this->success($stat); + } + + public function getSubscribe(Request $request) + { + $user = User::where('id', $request->user()->id) + ->select([ + 'plan_id', + 'token', + 'expired_at', + 'u', + 'd', + 'transfer_enable', + 'email', + 'uuid', + 'device_limit', + 'speed_limit', + 'next_reset_at' + ]) + ->first(); + if (!$user) { + return $this->fail([400, __('The user does not exist')]); + } + if ($user->plan_id) { + $user['plan'] = Plan::find($user->plan_id); + if (!$user['plan']) { + return $this->fail([400, __('Subscription plan does not exist')]); + } + } + $user['subscribe_url'] = Helper::getSubscribeUrl($user['token']); + $userService = new UserService(); + $user['reset_day'] = $userService->getResetDay($user); + $user = HookManager::filter('user.subscribe.response', $user); + return $this->success($user); + } + + public function resetSecurity(Request $request) + { + $user = $request->user(); + $user->uuid = Helper::guid(true); + $user->token = Helper::guid(); + if (!$user->save()) { + return $this->fail([400, __('Reset failed')]); + } + return $this->success(Helper::getSubscribeUrl($user->token)); + } + + public function update(UserUpdate $request) + { + $updateData = $request->only([ + 'remind_expire', + 'remind_traffic' + ]); + + $user = $request->user(); + try { + $user->update($updateData); + } catch (\Exception $e) { + return $this->fail([400, __('Save failed')]); + } + + return $this->success(true); + } + + public function transfer(UserTransfer $request) + { + $amount = $request->input('transfer_amount'); + try { + DB::transaction(function () use ($request, $amount) { + $user = User::lockForUpdate()->find($request->user()->id); + if (!$user) { + throw new \Exception(__('The user does not exist')); + } + if ($amount > $user->commission_balance) { + throw new \Exception(__('Insufficient commission balance')); + } + $user->commission_balance -= $amount; + $user->balance += $amount; + if (!$user->save()) { + throw new \Exception(__('Transfer failed')); + } + }); + } catch (\Exception $e) { + return $this->fail([400, $e->getMessage()]); + } + return $this->success(true); + } + + public function getQuickLoginUrl(Request $request) + { + $user = $request->user(); + + $url = $this->loginService->generateQuickLoginUrl($user, $request->input('redirect')); + return $this->success($url); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/ConfigController.php b/Xboard/app/Http/Controllers/V2/Admin/ConfigController.php new file mode 100644 index 0000000..81c5570 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/ConfigController.php @@ -0,0 +1,300 @@ +success($files); + } + + public function getThemeTemplate() + { + $path = public_path('theme/'); + $files = array_map(function ($item) use ($path) { + return str_replace($path, '', $item); + }, glob($path . '*')); + return $this->success($files); + } + + public function testSendMail(Request $request) + { + $mailLog = MailService::sendEmail([ + 'email' => $request->user()->email, + 'subject' => 'This is xboard test email', + 'template_name' => 'notify', + 'template_value' => [ + 'name' => admin_setting('app_name', 'XBoard'), + 'content' => 'This is xboard test email', + 'url' => admin_setting('app_url') + ] + ]); + return response([ + 'data' => $mailLog, + ]); + } + public function setTelegramWebhook(Request $request) + { + $hookUrl = $this->resolveTelegramWebhookUrl(); + if (blank($hookUrl)) { + return $this->fail([422, 'Telegram Webhook地址未配置']); + } + $hookUrl .= '?' . http_build_query([ + 'access_token' => md5(admin_setting('telegram_bot_token', $request->input('telegram_bot_token'))) + ]); + $telegramService = new TelegramService($request->input('telegram_bot_token')); + $telegramService->getMe(); + $telegramService->setWebhook(url: $hookUrl); + $telegramService->registerBotCommands(); + return $this->success([ + 'success' => true, + 'webhook_url' => $hookUrl, + 'webhook_base_url' => $this->getTelegramWebhookBaseUrl(), + ]); + } + + public function fetch(Request $request) + { + $key = $request->input('key'); + $configMappings = $this->getConfigMappings(); + if ($key && isset($configMappings[$key])) { + return $this->success([$key => $configMappings[$key]]); + } + + return $this->success($configMappings); + } + + /** + * 获取配置映射数据 + * + * @return array 配置映射数组 + */ + private function getConfigMappings(): array + { + return [ + 'invite' => [ + 'invite_force' => (bool) admin_setting('invite_force', 0), + 'invite_commission' => admin_setting('invite_commission', 10), + 'invite_gen_limit' => admin_setting('invite_gen_limit', 5), + 'invite_never_expire' => (bool) admin_setting('invite_never_expire', 0), + 'commission_first_time_enable' => (bool) admin_setting('commission_first_time_enable', 1), + 'commission_auto_check_enable' => (bool) admin_setting('commission_auto_check_enable', 1), + 'commission_withdraw_limit' => admin_setting('commission_withdraw_limit', 100), + 'commission_withdraw_method' => admin_setting('commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT), + 'withdraw_close_enable' => (bool) admin_setting('withdraw_close_enable', 0), + 'commission_distribution_enable' => (bool) admin_setting('commission_distribution_enable', 0), + 'commission_distribution_l1' => admin_setting('commission_distribution_l1'), + 'commission_distribution_l2' => admin_setting('commission_distribution_l2'), + 'commission_distribution_l3' => admin_setting('commission_distribution_l3') + ], + 'site' => [ + 'logo' => admin_setting('logo'), + 'force_https' => (int) admin_setting('force_https', 0), + 'stop_register' => (int) admin_setting('stop_register', 0), + 'app_name' => admin_setting('app_name', 'XBoard'), + 'app_description' => admin_setting('app_description', 'XBoard is best!'), + 'app_url' => admin_setting('app_url'), + 'subscribe_url' => admin_setting('subscribe_url'), + 'try_out_plan_id' => (int) admin_setting('try_out_plan_id', 0), + 'try_out_hour' => (int) admin_setting('try_out_hour', 1), + 'tos_url' => admin_setting('tos_url'), + 'currency' => admin_setting('currency', 'CNY'), + 'currency_symbol' => admin_setting('currency_symbol', '¥'), + 'ticket_must_wait_reply' => (bool) admin_setting('ticket_must_wait_reply', 0), + ], + 'subscribe' => [ + 'plan_change_enable' => (bool) admin_setting('plan_change_enable', 1), + 'reset_traffic_method' => (int) admin_setting('reset_traffic_method', 0), + 'surplus_enable' => (bool) admin_setting('surplus_enable', 1), + 'new_order_event_id' => (int) admin_setting('new_order_event_id', 0), + 'renew_order_event_id' => (int) admin_setting('renew_order_event_id', 0), + 'change_order_event_id' => (int) admin_setting('change_order_event_id', 0), + 'show_info_to_server_enable' => (bool) admin_setting('show_info_to_server_enable', 0), + 'show_protocol_to_server_enable' => (bool) admin_setting('show_protocol_to_server_enable', 0), + 'default_remind_expire' => (bool) admin_setting('default_remind_expire', 1), + 'default_remind_traffic' => (bool) admin_setting('default_remind_traffic', 1), + 'subscribe_path' => admin_setting('subscribe_path', 's'), + ], + 'frontend' => [ + 'frontend_theme' => admin_setting('frontend_theme', 'Xboard'), + 'frontend_theme_sidebar' => admin_setting('frontend_theme_sidebar', 'light'), + 'frontend_theme_header' => admin_setting('frontend_theme_header', 'dark'), + 'frontend_theme_color' => admin_setting('frontend_theme_color', 'default'), + 'frontend_background_url' => admin_setting('frontend_background_url'), + ], + 'server' => [ + 'server_token' => admin_setting('server_token'), + 'server_pull_interval' => admin_setting('server_pull_interval', 60), + 'server_push_interval' => admin_setting('server_push_interval', 60), + 'device_limit_mode' => (int) admin_setting('device_limit_mode', 0), + 'server_ws_enable' => (bool) admin_setting('server_ws_enable', 1), + 'server_ws_url' => admin_setting('server_ws_url', ''), + ], + 'email' => [ + 'email_template' => admin_setting('email_template', 'default'), + 'email_host' => admin_setting('email_host'), + 'email_port' => admin_setting('email_port'), + 'email_username' => admin_setting('email_username'), + 'email_password' => admin_setting('email_password'), + 'email_encryption' => admin_setting('email_encryption'), + 'email_from_address' => admin_setting('email_from_address'), + 'remind_mail_enable' => (bool) admin_setting('remind_mail_enable', false), + ], + 'telegram' => [ + 'telegram_bot_enable' => (bool) admin_setting('telegram_bot_enable', 0), + 'telegram_bot_token' => admin_setting('telegram_bot_token'), + 'telegram_webhook_url' => admin_setting('telegram_webhook_url'), + 'telegram_discuss_link' => admin_setting('telegram_discuss_link') + ], + 'app' => [ + 'windows_version' => admin_setting('windows_version', ''), + 'windows_download_url' => admin_setting('windows_download_url', ''), + 'macos_version' => admin_setting('macos_version', ''), + 'macos_download_url' => admin_setting('macos_download_url', ''), + 'android_version' => admin_setting('android_version', ''), + 'android_download_url' => admin_setting('android_download_url', '') + ], + 'safe' => [ + 'email_verify' => (bool) admin_setting('email_verify', 0), + 'safe_mode_enable' => (bool) admin_setting('safe_mode_enable', 0), + 'secure_path' => admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))), + 'email_whitelist_enable' => (bool) admin_setting('email_whitelist_enable', 0), + 'email_whitelist_suffix' => admin_setting('email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT), + 'email_gmail_limit_enable' => (bool) admin_setting('email_gmail_limit_enable', 0), + 'captcha_enable' => (bool) admin_setting('captcha_enable', 0), + 'captcha_type' => admin_setting('captcha_type', 'recaptcha'), + 'recaptcha_key' => admin_setting('recaptcha_key', ''), + 'recaptcha_site_key' => admin_setting('recaptcha_site_key', ''), + 'recaptcha_v3_secret_key' => admin_setting('recaptcha_v3_secret_key', ''), + 'recaptcha_v3_site_key' => admin_setting('recaptcha_v3_site_key', ''), + 'recaptcha_v3_score_threshold' => admin_setting('recaptcha_v3_score_threshold', 0.5), + 'turnstile_secret_key' => admin_setting('turnstile_secret_key', ''), + 'turnstile_site_key' => admin_setting('turnstile_site_key', ''), + 'register_limit_by_ip_enable' => (bool) admin_setting('register_limit_by_ip_enable', 0), + 'register_limit_count' => admin_setting('register_limit_count', 3), + 'register_limit_expire' => admin_setting('register_limit_expire', 60), + 'password_limit_enable' => (bool) admin_setting('password_limit_enable', 1), + 'password_limit_count' => admin_setting('password_limit_count', 5), + 'password_limit_expire' => admin_setting('password_limit_expire', 60), + // 保持向后兼容 + 'recaptcha_enable' => (bool) admin_setting('captcha_enable', 0) + ], + 'subscribe_template' => [ + 'subscribe_template_singbox' => $this->formatTemplateContent( + subscribe_template('singbox') ?? '', + 'json' + ), + 'subscribe_template_clash' => subscribe_template('clash') ?? '', + 'subscribe_template_clashmeta' => subscribe_template('clashmeta') ?? '', + 'subscribe_template_stash' => subscribe_template('stash') ?? '', + 'subscribe_template_surge' => subscribe_template('surge') ?? '', + 'subscribe_template_surfboard' => subscribe_template('surfboard') ?? '' + ] + ]; + } + + public function save(ConfigSave $request) + { + $data = $request->validated(); + + $templateKeys = [ + 'subscribe_template_singbox' => 'singbox', + 'subscribe_template_clash' => 'clash', + 'subscribe_template_clashmeta' => 'clashmeta', + 'subscribe_template_stash' => 'stash', + 'subscribe_template_surge' => 'surge', + 'subscribe_template_surfboard' => 'surfboard', + ]; + + foreach ($data as $k => $v) { + if (isset($templateKeys[$k])) { + SubscribeTemplate::setContent($templateKeys[$k], $v); + continue; + } + if ($k == 'frontend_theme') { + $themeService = app(ThemeService::class); + $themeService->switch($v); + } + admin_setting([$k => $v]); + } + + return $this->success(true); + } + + /** + * 格式化模板内容 + * + * @param mixed $content 模板内容 + * @param string $format 输出格式 (json|string) + * @return string 格式化后的内容 + */ + private function formatTemplateContent(mixed $content, string $format = 'string'): string + { + return match ($format) { + 'json' => match (true) { + is_array($content) => json_encode( + value: $content, + flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES + ), + + is_string($content) && str($content)->isJson() => rescue( + callback: fn() => json_encode( + value: json_decode($content, associative: true, flags: JSON_THROW_ON_ERROR), + flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES + ), + rescue: $content, + report: false + ), + + default => str($content)->toString() + }, + + default => str($content)->toString() + }; + } + + private function getTelegramWebhookBaseUrl(): ?string + { + $customUrl = trim((string) admin_setting('telegram_webhook_url', '')); + if ($customUrl !== '') { + return rtrim($customUrl, '/'); + } + + $appUrl = trim((string) admin_setting('app_url', '')); + if ($appUrl !== '') { + return rtrim($appUrl, '/'); + } + + return null; + } + + private function resolveTelegramWebhookUrl(): ?string + { + $baseUrl = $this->getTelegramWebhookBaseUrl(); + if (!$baseUrl) { + return null; + } + + if (str_contains($baseUrl, '/api/v1/guest/telegram/webhook')) { + return $baseUrl; + } + + return $baseUrl . '/api/v1/guest/telegram/webhook'; + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/CouponController.php b/Xboard/app/Http/Controllers/V2/Admin/CouponController.php new file mode 100644 index 0000000..364dff4 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/CouponController.php @@ -0,0 +1,186 @@ +has('filter')) { + collect($request->input('filter'))->each(function ($filter) use ($builder) { + $key = $filter['id']; + $value = $filter['value']; + $builder->where(function ($query) use ($key, $value) { + if (is_array($value)) { + $query->whereIn($key, $value); + } else { + $query->where($key, 'like', "%{$value}%"); + } + }); + }); + } + + if ($request->has('sort')) { + collect($request->input('sort'))->each(function ($sort) use ($builder) { + $key = $sort['id']; + $value = $sort['desc'] ? 'DESC' : 'ASC'; + $builder->orderBy($key, $value); + }); + } + } + public function fetch(Request $request) + { + $current = $request->input('current', 1); + $pageSize = $request->input('pageSize', 10); + $builder = Coupon::query(); + $this->applyFiltersAndSorts($request, $builder); + $coupons = $builder + ->orderBy('created_at', 'desc') + ->paginate($pageSize, ["*"], 'page', $current); + return $this->paginate($coupons); + } + + public function update(Request $request) + { + $params = $request->validate([ + 'id' => 'required|numeric', + 'show' => 'nullable|boolean' + ], [ + 'id.required' => '优惠券ID不能为空', + 'id.numeric' => '优惠券ID必须为数字' + ]); + try { + DB::beginTransaction(); + $coupon = Coupon::find($request->input('id')); + if (!$coupon) { + throw new ApiException(400201, '优惠券不存在'); + } + $coupon->update($params); + DB::commit(); + } catch (\Exception $e) { + \Log::error($e); + return $this->fail([500, '保存失败']); + } + } + + public function show(Request $request) + { + $request->validate([ + 'id' => 'required|numeric' + ], [ + 'id.required' => '优惠券ID不能为空', + 'id.numeric' => '优惠券ID必须为数字' + ]); + $coupon = Coupon::find($request->input('id')); + if (!$coupon) { + return $this->fail([400202, '优惠券不存在']); + } + $coupon->show = !$coupon->show; + if (!$coupon->save()) { + return $this->fail([500, '保存失败']); + } + return $this->success(true); + } + + public function generate(CouponGenerate $request) + { + if ($request->input('generate_count')) { + $this->multiGenerate($request); + return; + } + + $params = $request->validated(); + if (!$request->input('id')) { + if (!isset($params['code'])) { + $params['code'] = Helper::randomChar(8); + } + if (!Coupon::create($params)) { + return $this->fail([500, '创建失败']); + } + } else { + try { + Coupon::find($request->input('id'))->update($params); + } catch (\Exception $e) { + \Log::error($e); + return $this->fail([500, '保存失败']); + } + } + + return $this->success(true); + } + + private function multiGenerate(CouponGenerate $request) + { + $coupons = []; + $coupon = $request->validated(); + $coupon['created_at'] = $coupon['updated_at'] = time(); + $coupon['show'] = 1; + unset($coupon['generate_count']); + for ($i = 0; $i < $request->input('generate_count'); $i++) { + $coupon['code'] = Helper::randomChar(8); + array_push($coupons, $coupon); + } + try { + DB::beginTransaction(); + if ( + !Coupon::insert(array_map(function ($item) use ($coupon) { + // format data + if (isset($item['limit_plan_ids']) && is_array($item['limit_plan_ids'])) { + $item['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']); + } + if (isset($item['limit_period']) && is_array($item['limit_period'])) { + $item['limit_period'] = json_encode($coupon['limit_period']); + } + return $item; + }, $coupons)) + ) { + throw new \Exception(); + } + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return $this->fail([500, '生成失败']); + } + + $data = "名称,类型,金额或比例,开始时间,结束时间,可用次数,可用于订阅,券码,生成时间\r\n"; + foreach ($coupons as $coupon) { + $type = ['', '金额', '比例'][$coupon['type']]; + $value = ['', ($coupon['value'] / 100), $coupon['value']][$coupon['type']]; + $startTime = date('Y-m-d H:i:s', $coupon['started_at']); + $endTime = date('Y-m-d H:i:s', $coupon['ended_at']); + $limitUse = $coupon['limit_use'] ?? '不限制'; + $createTime = date('Y-m-d H:i:s', $coupon['created_at']); + $limitPlanIds = isset($coupon['limit_plan_ids']) ? implode("/", $coupon['limit_plan_ids']) : '不限制'; + $data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n"; + } + echo $data; + } + + public function drop(Request $request) + { + $request->validate([ + 'id' => 'required|numeric' + ], [ + 'id.required' => '优惠券ID不能为空', + 'id.numeric' => '优惠券ID必须为数字' + ]); + $coupon = Coupon::find($request->input('id')); + if (!$coupon) { + return $this->fail([400202, '优惠券不存在']); + } + if (!$coupon->delete()) { + return $this->fail([500, '删除失败']); + } + + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/GiftCardController.php b/Xboard/app/Http/Controllers/V2/Admin/GiftCardController.php new file mode 100644 index 0000000..2c8ecf5 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/GiftCardController.php @@ -0,0 +1,622 @@ +validate([ + 'type' => 'integer|min:1|max:10', + 'status' => 'integer|in:0,1', + 'page' => 'integer|min:1', + 'per_page' => 'integer|min:1|max:1000', + ]); + + $query = GiftCardTemplate::query(); + + if ($request->has('type')) { + $query->where('type', $request->input('type')); + } + + if ($request->has('status')) { + $query->where('status', $request->input('status')); + } + + $perPage = $request->input('per_page', 15); + $templates = $query->orderBy('sort', 'asc') + ->orderBy('created_at', 'desc') + ->paginate($perPage); + + $data = $templates->getCollection()->map(function ($template) { + return [ + 'id' => $template->id, + 'name' => $template->name, + 'description' => $template->description, + 'type' => $template->type, + 'type_name' => $template->type_name, + 'status' => $template->status, + 'conditions' => $template->conditions, + 'rewards' => $template->rewards, + 'limits' => $template->limits, + 'special_config' => $template->special_config, + 'icon' => $template->icon, + 'background_image' => $template->background_image, + 'theme_color' => $template->theme_color, + 'sort' => $template->sort, + 'admin_id' => $template->admin_id, + 'created_at' => $template->created_at, + 'updated_at' => $template->updated_at, + // 统计信息 + 'codes_count' => $template->codes()->count(), + 'used_count' => $template->usages()->count(), + ]; + })->values(); + + return $this->paginate( $templates); + } + + /** + * 创建礼品卡模板 + */ + public function createTemplate(Request $request) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'nullable|string', + 'type' => [ + 'required', + 'integer', + Rule::in(array_keys(GiftCardTemplate::getTypeMap())) + ], + 'status' => 'boolean', + 'conditions' => 'nullable|array', + 'rewards' => 'required|array', + 'limits' => 'nullable|array', + 'special_config' => 'nullable|array', + 'icon' => 'nullable|string|max:255', + 'background_image' => 'nullable|string|url|max:255', + 'theme_color' => 'nullable|string|regex:/^#[0-9A-Fa-f]{6}$/', + 'sort' => 'integer|min:0', + ], [ + 'name.required' => '礼品卡名称不能为空', + 'type.required' => '礼品卡类型不能为空', + 'type.in' => '无效的礼品卡类型', + 'rewards.required' => '奖励配置不能为空', + 'theme_color.regex' => '主题色格式不正确', + 'background_image.url' => '背景图片必须是有效的URL', + ]); + + try { + $template = GiftCardTemplate::create([ + 'name' => $request->input('name'), + 'description' => $request->input('description'), + 'type' => $request->input('type'), + 'status' => $request->input('status', true), + 'conditions' => $request->input('conditions'), + 'rewards' => $request->input('rewards'), + 'limits' => $request->input('limits'), + 'special_config' => $request->input('special_config'), + 'icon' => $request->input('icon'), + 'background_image' => $request->input('background_image'), + 'theme_color' => $request->input('theme_color', '#1890ff'), + 'sort' => $request->input('sort', 0), + 'admin_id' => $request->user()->id, + 'created_at' => time(), + 'updated_at' => time(), + ]); + + return $this->success($template); + } catch (\Exception $e) { + Log::error('创建礼品卡模板失败', [ + 'admin_id' => $request->user()->id, + 'data' => $request->all(), + 'error' => $e->getMessage(), + ]); + return $this->fail([500, '创建失败']); + } + } + + /** + * 更新礼品卡模板 + */ + public function updateTemplate(Request $request) + { + $validatedData = $request->validate([ + 'id' => 'required|integer|exists:v2_gift_card_template,id', + 'name' => 'sometimes|required|string|max:255', + 'description' => 'sometimes|nullable|string', + 'type' => [ + 'sometimes', + 'required', + 'integer', + Rule::in(array_keys(GiftCardTemplate::getTypeMap())) + ], + 'status' => 'sometimes|boolean', + 'conditions' => 'sometimes|nullable|array', + 'rewards' => 'sometimes|required|array', + 'limits' => 'sometimes|nullable|array', + 'special_config' => 'sometimes|nullable|array', + 'icon' => 'sometimes|nullable|string|max:255', + 'background_image' => 'sometimes|nullable|string|url|max:255', + 'theme_color' => 'sometimes|nullable|string|regex:/^#[0-9A-Fa-f]{6}$/', + 'sort' => 'sometimes|integer|min:0', + ]); + + $template = GiftCardTemplate::find($validatedData['id']); + if (!$template) { + return $this->fail([404, '模板不存在']); + } + + try { + $updateData = collect($validatedData)->except('id')->all(); + + if (empty($updateData)) { + return $this->success($template); + } + + $updateData['updated_at'] = time(); + + $template->update($updateData); + + return $this->success($template->fresh()); + } catch (\Exception $e) { + Log::error('更新礼品卡模板失败', [ + 'admin_id' => $request->user()->id, + 'template_id' => $template->id, + 'error' => $e->getMessage(), + ]); + return $this->fail([500, '更新失败']); + } + } + + /** + * 删除礼品卡模板 + */ + public function deleteTemplate(Request $request) + { + $request->validate([ + 'id' => 'required|integer|exists:v2_gift_card_template,id', + ]); + + $template = GiftCardTemplate::find($request->input('id')); + if (!$template) { + return $this->fail([404, '模板不存在']); + } + + // 检查是否有关联的兑换码 + if ($template->codes()->exists()) { + return $this->fail([400, '该模板下存在兑换码,无法删除']); + } + + try { + $template->delete(); + return $this->success(true); + } catch (\Exception $e) { + Log::error('删除礼品卡模板失败', [ + 'admin_id' => $request->user()->id, + 'template_id' => $template->id, + 'error' => $e->getMessage(), + ]); + return $this->fail([500, '删除失败']); + } + } + + /** + * 生成兑换码 + */ + public function generateCodes(Request $request) + { + $request->validate([ + 'template_id' => 'required|integer|exists:v2_gift_card_template,id', + 'count' => 'required|integer|min:1|max:10000', + 'prefix' => 'nullable|string|max:10|regex:/^[A-Z0-9]*$/', + 'expires_hours' => 'nullable|integer|min:1', + 'max_usage' => 'integer|min:1|max:1000', + ], [ + 'template_id.required' => '请选择礼品卡模板', + 'count.required' => '请指定生成数量', + 'count.max' => '单次最多生成10000个兑换码', + 'prefix.regex' => '前缀只能包含大写字母和数字', + ]); + + $template = GiftCardTemplate::find($request->input('template_id')); + if (!$template->isAvailable()) { + return $this->fail([400, '模板已被禁用']); + } + + try { + $options = [ + 'prefix' => $request->input('prefix', 'GC'), + 'max_usage' => $request->input('max_usage', 1), + ]; + + if ($request->has('expires_hours')) { + $options['expires_at'] = time() + ($request->input('expires_hours') * 3600); + } + + $batchId = GiftCardCode::batchGenerate( + $request->input('template_id'), + $request->input('count'), + $options + ); + + // 查询本次生成的所有兑换码 + $codes = GiftCardCode::where('batch_id', $batchId)->get(); + + // 判断是否导出 CSV + if ($request->input('download_csv')) { + $headers = [ + 'Content-Type' => 'text/csv', + 'Content-Disposition' => 'attachment; filename="gift_codes.csv"', + ]; + $callback = function () use ($codes, $template) { + $handle = fopen('php://output', 'w'); + // 表头 + fputcsv($handle, [ + '兑换码', + '前缀', + '有效期', + '最大使用次数', + '批次号', + '创建时间', + '模板名称', + '模板类型', + '模板奖励', + '状态', + '使用者', + '使用时间', + '备注' + ]); + foreach ($codes as $code) { + $expireDate = $code->expires_at ? date('Y-m-d H:i:s', $code->expires_at) : '长期有效'; + $createDate = date('Y-m-d H:i:s', $code->created_at); + $templateName = $template->name ?? ''; + $templateType = $template->type ?? ''; + $templateRewards = $template->rewards ? json_encode($template->rewards, JSON_UNESCAPED_UNICODE) : ''; + // 状态判断 + $status = $code->status_name; + $usedBy = $code->user_id ?? ''; + $usedAt = $code->used_at ? date('Y-m-d H:i:s', $code->used_at) : ''; + $remark = $code->remark ?? ''; + fputcsv($handle, [ + $code->code, + $code->prefix ?? '', + $expireDate, + $code->max_usage, + $code->batch_id, + $createDate, + $templateName, + $templateType, + $templateRewards, + $status, + $usedBy, + $usedAt, + $remark, + ]); + } + fclose($handle); + }; + return response()->streamDownload($callback, 'gift_codes.csv', $headers); + } + + Log::info('批量生成兑换码', [ + 'admin_id' => $request->user()->id, + 'template_id' => $request->input('template_id'), + 'count' => $request->input('count'), + 'batch_id' => $batchId, + ]); + + return $this->success([ + 'batch_id' => $batchId, + 'count' => $request->input('count'), + 'message' => '生成成功', + ]); + } catch (\Exception $e) { + Log::error('生成兑换码失败', [ + 'admin_id' => $request->user()->id, + 'data' => $request->all(), + 'error' => $e->getMessage(), + ]); + return $this->fail([500, '生成失败']); + } + } + + /** + * 获取兑换码列表 + */ + public function codes(Request $request) + { + $request->validate([ + 'template_id' => 'integer|exists:v2_gift_card_template,id', + 'batch_id' => 'string', + 'status' => 'integer|in:0,1,2,3', + 'page' => 'integer|min:1', + 'per_page' => 'integer|min:1|max:500', + ]); + + $query = GiftCardCode::with(['template', 'user']); + + if ($request->has('template_id')) { + $query->where('template_id', $request->input('template_id')); + } + + if ($request->has('batch_id')) { + $query->where('batch_id', $request->input('batch_id')); + } + + if ($request->has('status')) { + $query->where('status', $request->input('status')); + } + + $perPage = $request->input('per_page', 15); + $codes = $query->orderBy('created_at', 'desc')->paginate($perPage); + + $data = $codes->getCollection()->map(function ($code) { + return [ + 'id' => $code->id, + 'template_id' => $code->template_id, + 'template_name' => $code->template->name ?? '', + 'code' => $code->code, + 'batch_id' => $code->batch_id, + 'status' => $code->status, + 'status_name' => $code->status_name, + 'user_id' => $code->user_id, + 'user_email' => $code->user ? (substr($code->user->email ?? '', 0, 3) . '***@***') : null, + 'used_at' => $code->used_at, + 'expires_at' => $code->expires_at, + 'usage_count' => $code->usage_count, + 'max_usage' => $code->max_usage, + 'created_at' => $code->created_at, + ]; + })->values(); + + return $this->paginate($codes); + } + + /** + * 禁用/启用兑换码 + */ + public function toggleCode(Request $request) + { + $request->validate([ + 'id' => 'required|integer|exists:v2_gift_card_code,id', + 'action' => 'required|string|in:disable,enable', + ]); + + $code = GiftCardCode::find($request->input('id')); + if (!$code) { + return $this->fail([404, '兑换码不存在']); + } + + try { + if ($request->input('action') === 'disable') { + $code->markAsDisabled(); + } else { + if ($code->status === GiftCardCode::STATUS_DISABLED) { + $code->status = GiftCardCode::STATUS_UNUSED; + $code->save(); + } + } + + return $this->success([ + 'message' => $request->input('action') === 'disable' ? '已禁用' : '已启用', + ]); + } catch (\Exception $e) { + return $this->fail([500, '操作失败']); + } + } + + /** + * 导出兑换码 + */ + public function exportCodes(Request $request) + { + $request->validate([ + 'batch_id' => 'required|string|exists:v2_gift_card_code,batch_id', + ]); + + $codes = GiftCardCode::where('batch_id', $request->input('batch_id')) + ->orderBy('created_at', 'asc') + ->get(['code']); + + $content = $codes->pluck('code')->implode("\n"); + + return response($content) + ->header('Content-Type', 'text/plain') + ->header('Content-Disposition', 'attachment; filename="gift_cards_' . $request->input('batch_id') . '.txt"'); + } + + /** + * 获取使用记录 + */ + public function usages(Request $request) + { + $request->validate([ + 'template_id' => 'integer|exists:v2_gift_card_template,id', + 'user_id' => 'integer|exists:v2_user,id', + 'page' => 'integer|min:1', + 'per_page' => 'integer|min:1|max:500', + ]); + + $query = GiftCardUsage::with(['template', 'code', 'user', 'inviteUser']); + + if ($request->has('template_id')) { + $query->where('template_id', $request->input('template_id')); + } + + if ($request->has('user_id')) { + $query->where('user_id', $request->input('user_id')); + } + + $perPage = $request->input('per_page', 15); + $usages = $query->orderBy('created_at', 'desc')->paginate($perPage); + + $usages->transform(function ($usage) { + return [ + 'id' => $usage->id, + 'code' => $usage->code->code ?? '', + 'template_name' => $usage->template->name ?? '', + 'user_email' => $usage->user->email ?? '', + 'invite_user_email' => $usage->inviteUser ? (substr($usage->inviteUser->email ?? '', 0, 3) . '***@***') : null, + 'rewards_given' => $usage->rewards_given, + 'invite_rewards' => $usage->invite_rewards, + 'multiplier_applied' => $usage->multiplier_applied, + 'created_at' => $usage->created_at, + ]; + })->values(); + return $this->paginate($usages); + } + + /** + * 获取统计数据 + */ + public function statistics(Request $request) + { + $request->validate([ + 'start_date' => 'date_format:Y-m-d', + 'end_date' => 'date_format:Y-m-d', + ]); + + $startDate = $request->input('start_date', date('Y-m-d', strtotime('-30 days'))); + $endDate = $request->input('end_date', date('Y-m-d')); + + // 总体统计 + $totalStats = [ + 'templates_count' => GiftCardTemplate::count(), + 'active_templates_count' => GiftCardTemplate::where('status', 1)->count(), + 'codes_count' => GiftCardCode::count(), + 'used_codes_count' => GiftCardCode::where('status', GiftCardCode::STATUS_USED)->count(), + 'usages_count' => GiftCardUsage::count(), + ]; + + // 每日使用统计 + $driver = DB::connection()->getDriverName(); + $dateExpression = "date(created_at, 'unixepoch')"; // Default for SQLite + if ($driver === 'mysql') { + $dateExpression = 'DATE(FROM_UNIXTIME(created_at))'; + } elseif ($driver === 'pgsql') { + $dateExpression = 'date(to_timestamp(created_at))'; + } + + $dailyUsages = GiftCardUsage::selectRaw("{$dateExpression} as date, COUNT(*) as count") + ->whereRaw("{$dateExpression} BETWEEN ? AND ?", [$startDate, $endDate]) + ->groupBy('date') + ->orderBy('date') + ->get(); + + // 类型统计 + $typeStats = GiftCardUsage::with('template') + ->selectRaw('template_id, COUNT(*) as count') + ->groupBy('template_id') + ->get() + ->map(function ($item) { + return [ + 'template_name' => $item->template->name ?? '', + 'type_name' => $item->template->type_name ?? '', + 'count' => $item->count ?? 0, + ]; + }); + + return $this->success([ + 'total_stats' => $totalStats, + 'daily_usages' => $dailyUsages, + 'type_stats' => $typeStats, + ]); + } + + /** + * 获取所有可用的礼品卡类型 + */ + public function types() + { + return $this->success(GiftCardTemplate::getTypeMap()); + } + + /** + * 更新单个兑换码 + */ + public function updateCode(Request $request) + { + $validatedData = $request->validate([ + 'id' => 'required|integer|exists:v2_gift_card_code,id', + 'expires_at' => 'sometimes|nullable|integer', + 'max_usage' => 'sometimes|integer|min:1|max:1000', + 'status' => 'sometimes|integer|in:0,1,2,3', + ]); + + $code = GiftCardCode::find($validatedData['id']); + if (!$code) { + return $this->fail([404, '礼品卡不存在']); + } + + try { + $updateData = collect($validatedData)->except('id')->all(); + + if (empty($updateData)) { + return $this->success($code); + } + + $updateData['updated_at'] = time(); + $code->update($updateData); + + return $this->success($code->fresh()); + } catch (\Exception $e) { + Log::error('更新礼品卡信息失败', [ + 'admin_id' => $request->user()->id, + 'code_id' => $code->id, + 'error' => $e->getMessage(), + ]); + return $this->fail([500, '更新失败']); + } + } + + /** + * 删除礼品卡 + */ + public function deleteCode(Request $request) + { + $request->validate([ + 'id' => 'required|integer|exists:v2_gift_card_code,id', + ]); + + $code = GiftCardCode::find($request->input('id')); + if (!$code) { + return $this->fail([404, '礼品卡不存在']); + } + + // 检查是否已被使用 + if ($code->status === GiftCardCode::STATUS_USED) { + return $this->fail([400, '该礼品卡已被使用,无法删除']); + } + + try { + // 检查是否有关联的使用记录 + if ($code->usages()->exists()) { + return $this->fail([400, '该礼品卡存在使用记录,无法删除']); + } + + $code->delete(); + return $this->success(['message' => '删除成功']); + } catch (\Exception $e) { + Log::error('删除礼品卡失败', [ + 'admin_id' => $request->user()->id, + 'code_id' => $code->id, + 'error' => $e->getMessage(), + ]); + return $this->fail([500, '删除失败']); + } + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/KnowledgeController.php b/Xboard/app/Http/Controllers/V2/Admin/KnowledgeController.php new file mode 100644 index 0000000..d773a8d --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/KnowledgeController.php @@ -0,0 +1,113 @@ +input('id')) { + $knowledge = Knowledge::find($request->input('id'))->toArray(); + if (!$knowledge) + return $this->fail([400202, '知识不存在']); + return $this->success($knowledge); + } + $data = Knowledge::select(['title', 'id', 'updated_at', 'category', 'show']) + ->orderBy('sort', 'ASC') + ->get(); + return $this->success($data); + } + + public function getCategory(Request $request) + { + return $this->success(array_keys(Knowledge::get()->groupBy('category')->toArray())); + } + + public function save(KnowledgeSave $request) + { + $params = $request->validated(); + + if (!$request->input('id')) { + if (!Knowledge::create($params)) { + return $this->fail([500, '创建失败']); + } + } else { + try { + Knowledge::find($request->input('id'))->update($params); + } catch (\Exception $e) { + \Log::error($e); + return $this->fail([500, '创建失败']); + } + } + + return $this->success(true); + } + + public function show(Request $request) + { + $request->validate([ + 'id' => 'required|numeric' + ], [ + 'id.required' => '知识库ID不能为空' + ]); + $knowledge = Knowledge::find($request->input('id')); + if (!$knowledge) { + throw new ApiException('知识不存在'); + } + $knowledge->show = !$knowledge->show; + if (!$knowledge->save()) { + throw new ApiException('保存失败'); + } + + return $this->success(true); + } + + public function sort(Request $request) + { + $request->validate([ + 'ids' => 'required|array' + ], [ + 'ids.required' => '参数有误', + 'ids.array' => '参数有误' + ]); + try { + DB::beginTransaction(); + foreach ($request->input('ids') as $k => $v) { + $knowledge = Knowledge::find($v); + $knowledge->timestamps = false; + $knowledge->update(['sort' => $k + 1]); + } + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + throw new ApiException('保存失败'); + } + return $this->success(true); + } + + public function drop(Request $request) + { + $request->validate([ + 'id' => 'required|numeric' + ], [ + 'id.required' => '知识库ID不能为空' + ]); + $knowledge = Knowledge::find($request->input('id')); + if (!$knowledge) { + return $this->fail([400202, '知识不存在']); + } + if (!$knowledge->delete()) { + return $this->fail([500, '删除失败']); + } + + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/NoticeController.php b/Xboard/app/Http/Controllers/V2/Admin/NoticeController.php new file mode 100644 index 0000000..854b313 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/NoticeController.php @@ -0,0 +1,101 @@ +success( + Notice::orderBy('sort', 'ASC') + ->orderBy('id', 'DESC') + ->get() + ); + } + + public function save(NoticeSave $request) + { + $data = $request->only([ + 'title', + 'content', + 'img_url', + 'tags', + 'show', + 'popup' + ]); + if (!$request->input('id')) { + if (!Notice::create($data)) { + return $this->fail([500, '保存失败']); + } + } else { + try { + Notice::find($request->input('id'))->update($data); + } catch (\Exception $e) { + return $this->fail([500, '保存失败']); + } + } + return $this->success(true); + } + + + + public function show(Request $request) + { + if (empty($request->input('id'))) { + return $this->fail([500, '公告ID不能为空']); + } + $notice = Notice::find($request->input('id')); + if (!$notice) { + return $this->fail([400202, '公告不存在']); + } + $notice->show = $notice->show ? 0 : 1; + if (!$notice->save()) { + return $this->fail([500, '保存失败']); + } + + return $this->success(true); + } + + public function drop(Request $request) + { + if (empty($request->input('id'))) { + return $this->fail([422, '公告ID不能为空']); + } + $notice = Notice::find($request->input('id')); + if (!$notice) { + return $this->fail([400202, '公告不存在']); + } + if (!$notice->delete()) { + return $this->fail([500, '删除失败']); + } + return $this->success(true); + } + + public function sort(Request $request) + { + $params = $request->validate([ + 'ids' => 'required|array' + ]); + + try { + DB::beginTransaction(); + foreach ($params['ids'] as $k => $v) { + $notice = Notice::findOrFail($v); + $notice->update(['sort' => $k + 1]); + } + DB::commit(); + return $this->success(true); + } catch (\Exception $e) { + DB::rollBack(); + \Log::error($e); + return $this->fail([500, '排序保存失败']); + } + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/OrderController.php b/Xboard/app/Http/Controllers/V2/Admin/OrderController.php new file mode 100644 index 0000000..c06c7c8 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/OrderController.php @@ -0,0 +1,252 @@ +find($request->input('id')); + if (!$order) + return $this->fail([400202, '订单不存在']); + if ($order->surplus_order_ids) { + $order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get(); + } + $order['period'] = PlanService::getLegacyPeriod((string) $order->period); + return $this->success($order); + } + + public function fetch(Request $request) + { + $current = $request->input('current', 1); + $pageSize = $request->input('pageSize', 10); + $orderModel = Order::with('plan:id,name'); + + if ($request->boolean('is_commission')) { + $orderModel->whereNotNull('invite_user_id') + ->whereNotIn('status', [0, 2]) + ->where('commission_balance', '>', 0); + } + + $this->applyFiltersAndSorts($request, $orderModel); + + /** @var \Illuminate\Pagination\LengthAwarePaginator $paginatedResults */ + $paginatedResults = $orderModel + ->latest('created_at') + ->paginate( + perPage: $pageSize, + page: $current + ); + + $paginatedResults->getCollection()->transform(function ($order) { + $orderArray = $order->toArray(); + $orderArray['period'] = PlanService::getLegacyPeriod((string) $order->period); + return $orderArray; + }); + + return $this->paginate($paginatedResults); + } + + private function applyFiltersAndSorts(Request $request, Builder $builder): void + { + $this->applyFilters($request, $builder); + $this->applySorting($request, $builder); + } + + private function applyFilters(Request $request, Builder $builder): void + { + if (!$request->has('filter')) { + return; + } + + collect($request->input('filter'))->each(function ($filter) use ($builder) { + $field = $filter['id']; + $value = $filter['value']; + + $builder->where(function ($query) use ($field, $value) { + $this->buildFilterQuery($query, $field, $value); + }); + }); + } + + private function buildFilterQuery(Builder $query, string $field, mixed $value): void + { + // Handle array values for 'in' operations + if (is_array($value)) { + $query->whereIn($field, $value); + return; + } + + // Handle operator-based filtering + if (!is_string($value) || !str_contains($value, ':')) { + $query->where($field, 'like', "%{$value}%"); + return; + } + + [$operator, $filterValue] = explode(':', $value, 2); + + // Convert numeric strings to appropriate type + if (is_numeric($filterValue)) { + $filterValue = strpos($filterValue, '.') !== false + ? (float) $filterValue + : (int) $filterValue; + } + + // Apply operator + $query->where($field, match (strtolower($operator)) { + 'eq' => '=', + 'gt' => '>', + 'gte' => '>=', + 'lt' => '<', + 'lte' => '<=', + 'like' => 'like', + 'notlike' => 'not like', + 'null' => static fn($q) => $q->whereNull($field), + 'notnull' => static fn($q) => $q->whereNotNull($field), + default => 'like' + }, match (strtolower($operator)) { + 'like', 'notlike' => "%{$filterValue}%", + 'null', 'notnull' => null, + default => $filterValue + }); + } + + private function applySorting(Request $request, Builder $builder): void + { + if (!$request->has('sort')) { + return; + } + + collect($request->input('sort'))->each(function ($sort) use ($builder) { + $field = $sort['id']; + $direction = $sort['desc'] ? 'DESC' : 'ASC'; + $builder->orderBy($field, $direction); + }); + } + + public function paid(Request $request) + { + $order = Order::where('trade_no', $request->input('trade_no')) + ->first(); + if (!$order) { + return $this->fail([400202, '订单不存在']); + } + if ($order->status !== 0) + return $this->fail([400, '只能对待支付的订单进行操作']); + + $orderService = new OrderService($order); + if (!$orderService->paid('manual_operation')) { + return $this->fail([500, '更新失败']); + } + return $this->success(true); + } + + public function cancel(Request $request) + { + $order = Order::where('trade_no', $request->input('trade_no')) + ->first(); + if (!$order) { + return $this->fail([400202, '订单不存在']); + } + if ($order->status !== 0) + return $this->fail([400, '只能对待支付的订单进行操作']); + + $orderService = new OrderService($order); + if (!$orderService->cancel()) { + return $this->fail([400, '更新失败']); + } + return $this->success(true); + } + + public function update(OrderUpdate $request) + { + $params = $request->only([ + 'commission_status' + ]); + + $order = Order::where('trade_no', $request->input('trade_no')) + ->first(); + if (!$order) { + return $this->fail([400202, '订单不存在']); + } + + try { + $order->update($params); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '更新失败']); + } + + return $this->success(true); + } + + public function assign(OrderAssign $request) + { + $plan = Plan::find($request->input('plan_id')); + $user = User::byEmail($request->input('email'))->first(); + + if (!$user) { + return $this->fail([400202, '该用户不存在']); + } + + if (!$plan) { + return $this->fail([400202, '该订阅不存在']); + } + + $userService = new UserService(); + if ($userService->isNotCompleteOrderByUserId($user->id)) { + return $this->fail([400, '该用户还有待支付的订单,无法分配']); + } + + try { + DB::beginTransaction(); + $order = new Order(); + $orderService = new OrderService($order); + $order->user_id = $user->id; + $order->plan_id = $plan->id; + $period = $request->input('period'); + $order->period = PlanService::getPeriodKey((string) $period); + $order->trade_no = Helper::guid(); + $order->total_amount = $request->input('total_amount'); + + if (PlanService::getPeriodKey((string) $order->period) === Plan::PERIOD_RESET_TRAFFIC) { + $order->type = Order::TYPE_RESET_TRAFFIC; + } else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) { + $order->type = Order::TYPE_UPGRADE; + } else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) { + $order->type = Order::TYPE_RENEWAL; + } else { + $order->type = Order::TYPE_NEW_PURCHASE; + } + + $orderService->setInvite($user); + + if (!$order->save()) { + DB::rollBack(); + return $this->fail([500, '订单创建失败']); + } + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + throw $e; + } + + return $this->success($order->trade_no); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/PaymentController.php b/Xboard/app/Http/Controllers/V2/Admin/PaymentController.php new file mode 100644 index 0000000..6649aaa --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/PaymentController.php @@ -0,0 +1,133 @@ +success(array_unique($methods)); + } + + public function fetch() + { + $payments = Payment::orderBy('sort', 'ASC')->get(); + foreach ($payments as $k => $v) { + $notifyUrl = url("/api/v1/guest/payment/notify/{$v->payment}/{$v->uuid}"); + if ($v->notify_domain) { + $parseUrl = parse_url($notifyUrl); + $notifyUrl = $v->notify_domain . $parseUrl['path']; + } + $payments[$k]['notify_url'] = $notifyUrl; + } + return $this->success($payments); + } + + public function getPaymentForm(Request $request) + { + try { + $paymentService = new PaymentService($request->input('payment'), $request->input('id')); + return $this->success(collect($paymentService->form())); + } catch (\Exception $e) { + return $this->fail([400, '支付方式不存在或未启用']); + } + } + + public function show(Request $request) + { + $payment = Payment::find($request->input('id')); + if (!$payment) + return $this->fail([400202, '支付方式不存在']); + $payment->enable = !$payment->enable; + if (!$payment->save()) + return $this->fail([500, '保存失败']); + return $this->success(true); + } + + public function save(Request $request) + { + if (!admin_setting('app_url')) { + return $this->fail([400, '请在站点配置中配置站点地址']); + } + $params = $request->validate([ + 'name' => 'required', + 'icon' => 'nullable', + 'payment' => 'required', + 'config' => 'required', + 'notify_domain' => 'nullable|url', + 'handling_fee_fixed' => 'nullable|integer', + 'handling_fee_percent' => 'nullable|numeric|between:0,100' + ], [ + 'name.required' => '显示名称不能为空', + 'payment.required' => '网关参数不能为空', + 'config.required' => '配置参数不能为空', + 'notify_domain.url' => '自定义通知域名格式有误', + 'handling_fee_fixed.integer' => '固定手续费格式有误', + 'handling_fee_percent.between' => '百分比手续费范围须在0-100之间' + ]); + if ($request->input('id')) { + $payment = Payment::find($request->input('id')); + if (!$payment) + return $this->fail([400202, '支付方式不存在']); + try { + $payment->update($params); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '保存失败']); + } + return $this->success(true); + } + $params['uuid'] = Helper::randomChar(8); + if (!Payment::create($params)) { + return $this->fail([500, '保存失败']); + } + return $this->success(true); + } + + public function drop(Request $request) + { + $payment = Payment::find($request->input('id')); + if (!$payment) + return $this->fail([400202, '支付方式不存在']); + return $this->success($payment->delete()); + } + + + public function sort(Request $request) + { + $request->validate([ + 'ids' => 'required|array' + ], [ + 'ids.required' => '参数有误', + 'ids.array' => '参数有误' + ]); + try { + DB::beginTransaction(); + foreach ($request->input('ids') as $k => $v) { + if (!Payment::find($v)->update(['sort' => $k + 1])) { + throw new \Exception(); + } + } + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return $this->fail([500, '保存失败']); + } + + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/PlanController.php b/Xboard/app/Http/Controllers/V2/Admin/PlanController.php new file mode 100644 index 0000000..6a39f9f --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/PlanController.php @@ -0,0 +1,132 @@ +with([ + 'group:id,name' + ]) + ->withCount([ + 'users', + 'users as active_users_count' => function ($query) { + $query->where(function ($q) { + $q->where('expired_at', '>', time()) + ->orWhereNull('expired_at'); + }); + } + ]) + ->get(); + + return $this->success($plans); + } + + public function save(PlanSave $request) + { + $params = $request->validated(); + + if ($request->input('id')) { + $plan = Plan::find($request->input('id')); + if (!$plan) { + return $this->fail([400202, '该订阅不存在']); + } + + DB::beginTransaction(); + try { + if ($request->input('force_update')) { + User::where('plan_id', $plan->id)->update([ + 'group_id' => $params['group_id'], + 'transfer_enable' => $params['transfer_enable'] * 1073741824, + 'speed_limit' => $params['speed_limit'], + 'device_limit' => $params['device_limit'], + ]); + } + $plan->update($params); + DB::commit(); + return $this->success(true); + } catch (\Exception $e) { + DB::rollBack(); + Log::error($e); + return $this->fail([500, '保存失败']); + } + } + if (!Plan::create($params)) { + return $this->fail([500, '创建失败']); + } + return $this->success(true); + } + + public function drop(Request $request) + { + if (Order::where('plan_id', $request->input('id'))->first()) { + return $this->fail([400201, '该订阅下存在订单无法删除']); + } + if (User::where('plan_id', $request->input('id'))->first()) { + return $this->fail([400201, '该订阅下存在用户无法删除']); + } + + $plan = Plan::find($request->input('id')); + if (!$plan) { + return $this->fail([400202, '该订阅不存在']); + } + + return $this->success($plan->delete()); + } + + public function update(Request $request) + { + $updateData = $request->only([ + 'show', + 'renew', + 'sell' + ]); + + $plan = Plan::find($request->input('id')); + if (!$plan) { + return $this->fail([400202, '该订阅不存在']); + } + + try { + $plan->update($updateData); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '保存失败']); + } + + return $this->success(true); + } + + public function sort(Request $request) + { + $params = $request->validate([ + 'ids' => 'required|array' + ]); + + try { + DB::beginTransaction(); + foreach ($params['ids'] as $k => $v) { + if (!Plan::find($v)->update(['sort' => $k + 1])) { + throw new \Exception(); + } + } + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + Log::error($e); + return $this->fail([500, '保存失败']); + } + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/PluginController.php b/Xboard/app/Http/Controllers/V2/Admin/PluginController.php new file mode 100644 index 0000000..da41477 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/PluginController.php @@ -0,0 +1,333 @@ +pluginManager = $pluginManager; + $this->configService = $configService; + } + + /** + * 获取所有插件类型 + */ + public function types() + { + return response()->json([ + 'data' => [ + [ + 'value' => Plugin::TYPE_FEATURE, + 'label' => '功能', + 'description' => '提供功能扩展的插件,如Telegram登录、邮件通知等', + 'icon' => '🔧' + ], + [ + 'value' => Plugin::TYPE_PAYMENT, + 'label' => '支付方式', + 'description' => '提供支付接口的插件,如支付宝、微信支付等', + 'icon' => '💳' + ] + ] + ]); + } + + /** + * 获取插件列表 + */ + public function index(Request $request) + { + $type = $request->query('type'); + + $installedPlugins = Plugin::when($type, function ($query) use ($type) { + return $query->byType($type); + }) + ->get() + ->keyBy('code') + ->toArray(); + + $pluginPath = base_path('plugins'); + $plugins = []; + + if (File::exists($pluginPath)) { + $directories = File::directories($pluginPath); + foreach ($directories as $directory) { + $pluginName = basename($directory); + $configFile = $directory . '/config.json'; + if (File::exists($configFile)) { + $config = json_decode(File::get($configFile), true); + $code = $config['code']; + $pluginType = $config['type'] ?? Plugin::TYPE_FEATURE; + + // 如果指定了类型,过滤插件 + if ($type && $pluginType !== $type) { + continue; + } + + $installed = isset($installedPlugins[$code]); + $pluginConfig = $installed ? $this->configService->getConfig($code) : ($config['config'] ?? []); + $readmeFile = collect(['README.md', 'readme.md']) + ->map(fn($f) => $directory . '/' . $f) + ->first(fn($path) => File::exists($path)); + $readmeContent = $readmeFile ? File::get($readmeFile) : ''; + $needUpgrade = false; + if ($installed) { + $installedVersion = $installedPlugins[$code]['version'] ?? null; + $localVersion = $config['version'] ?? null; + if ($installedVersion && $localVersion && version_compare($localVersion, $installedVersion, '>')) { + $needUpgrade = true; + } + } + $plugins[] = [ + 'code' => $config['code'], + 'name' => $config['name'], + 'version' => $config['version'], + 'description' => $config['description'], + 'author' => $config['author'], + 'type' => $pluginType, + 'is_installed' => $installed, + 'is_enabled' => $installed ? $installedPlugins[$code]['is_enabled'] : false, + 'is_protected' => in_array($code, Plugin::PROTECTED_PLUGINS), + 'can_be_deleted' => !in_array($code, Plugin::PROTECTED_PLUGINS), + 'config' => $pluginConfig, + 'readme' => $readmeContent, + 'need_upgrade' => $needUpgrade, + ]; + } + } + } + + return response()->json([ + 'data' => $plugins + ]); + } + + /** + * 安装插件 + */ + public function install(Request $request) + { + $request->validate([ + 'code' => 'required|string' + ]); + + try { + $this->pluginManager->install($request->input('code')); + return response()->json([ + 'message' => '插件安装成功' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '插件安装失败:' . $e->getMessage() + ], 400); + } + } + + /** + * 卸载插件 + */ + public function uninstall(Request $request) + { + $request->validate([ + 'code' => 'required|string' + ]); + + $code = $request->input('code'); + $plugin = Plugin::where('code', $code)->first(); + if ($plugin && $plugin->is_enabled) { + return response()->json([ + 'message' => '请先禁用插件后再卸载' + ], 400); + } + + try { + $this->pluginManager->uninstall($code); + return response()->json([ + 'message' => '插件卸载成功' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '插件卸载失败:' . $e->getMessage() + ], 400); + } + } + + /** + * 升级插件 + */ + public function upgrade(Request $request) + { + $request->validate([ + 'code' => 'required|string', + ]); + try { + $this->pluginManager->update($request->input('code')); + return response()->json([ + 'message' => '插件升级成功' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '插件升级失败:' . $e->getMessage() + ], 400); + } + } + + /** + * 启用插件 + */ + public function enable(Request $request) + { + $request->validate([ + 'code' => 'required|string' + ]); + + try { + $this->pluginManager->enable($request->input('code')); + return response()->json([ + 'message' => '插件启用成功' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '插件启用失败:' . $e->getMessage() + ], 400); + } + } + + /** + * 禁用插件 + */ + public function disable(Request $request) + { + $request->validate([ + 'code' => 'required|string' + ]); + + $this->pluginManager->disable($request->input('code')); + return response()->json([ + 'message' => '插件禁用成功' + ]); + + } + + /** + * 获取插件配置 + */ + public function getConfig(Request $request) + { + $request->validate([ + 'code' => 'required|string' + ]); + + try { + $config = $this->configService->getConfig($request->input('code')); + return response()->json([ + 'data' => $config + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '获取配置失败:' . $e->getMessage() + ], 400); + } + } + + /** + * 更新插件配置 + */ + public function updateConfig(Request $request) + { + $request->validate([ + 'code' => 'required|string', + 'config' => 'required|array' + ]); + + try { + $this->configService->updateConfig( + $request->input('code'), + $request->input('config') + ); + + return response()->json([ + 'message' => '配置更新成功' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '配置更新失败:' . $e->getMessage() + ], 400); + } + } + + /** + * 上传插件 + */ + public function upload(Request $request) + { + $request->validate([ + 'file' => [ + 'required', + 'file', + 'mimes:zip', + 'max:10240', // 最大10MB + ] + ], [ + 'file.required' => '请选择插件包文件', + 'file.file' => '无效的文件类型', + 'file.mimes' => '插件包必须是zip格式', + 'file.max' => '插件包大小不能超过10MB' + ]); + + try { + $this->pluginManager->upload($request->file('file')); + return response()->json([ + 'message' => '插件上传成功' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '插件上传失败:' . $e->getMessage() + ], 400); + } + } + + /** + * 删除插件 + */ + public function delete(Request $request) + { + $request->validate([ + 'code' => 'required|string' + ]); + + $code = $request->input('code'); + + // 检查是否为受保护的插件 + if (in_array($code, Plugin::PROTECTED_PLUGINS)) { + return response()->json([ + 'message' => '该插件为系统默认插件,不允许删除' + ], 403); + } + + try { + $this->pluginManager->delete($code); + return response()->json([ + 'message' => '插件删除成功' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => '插件删除失败:' . $e->getMessage() + ], 400); + } + } +} \ No newline at end of file diff --git a/Xboard/app/Http/Controllers/V2/Admin/Server/GroupController.php b/Xboard/app/Http/Controllers/V2/Admin/Server/GroupController.php new file mode 100644 index 0000000..83a53ac --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/Server/GroupController.php @@ -0,0 +1,66 @@ +orderByDesc('id') + ->withCount('users') + ->get(); + + // 只在需要时手动加载server_count + $serverGroups->each(function ($group) { + $group->setAttribute('server_count', $group->server_count); + }); + + return $this->success($serverGroups); + } + + public function save(Request $request) + { + if (empty($request->input('name'))) { + return $this->fail([422, '组名不能为空']); + } + + if ($request->input('id')) { + $serverGroup = ServerGroup::find($request->input('id')); + } else { + $serverGroup = new ServerGroup(); + } + + $serverGroup->name = $request->input('name'); + return $this->success($serverGroup->save()); + } + + public function drop(Request $request) + { + $groupId = $request->input('id'); + + $serverGroup = ServerGroup::find($groupId); + if (!$serverGroup) { + return $this->fail([400202, '组不存在']); + } + if (Server::whereJsonContains('group_ids', $groupId)->exists()) { + return $this->fail([400, '该组已被节点所使用,无法删除']); + } + + if (Plan::where('group_id', $groupId)->exists()) { + return $this->fail([400, '该组已被订阅所使用,无法删除']); + } + if (User::where('group_id', $groupId)->exists()) { + return $this->fail([400, '该组已被用户所使用,无法删除']); + } + return $this->success($serverGroup->delete()); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/Server/ManageController.php b/Xboard/app/Http/Controllers/V2/Admin/Server/ManageController.php new file mode 100644 index 0000000..41a4bac --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/Server/ManageController.php @@ -0,0 +1,219 @@ +map(function ($item) { + $item['groups'] = ServerGroup::whereIn('id', $item['group_ids'])->get(['name', 'id']); + $item['parent'] = $item->parent; + return $item; + }); + return $this->success($servers); + } + + public function sort(Request $request) + { + ini_set('post_max_size', '1m'); + $params = $request->validate([ + '*.id' => 'numeric', + '*.order' => 'numeric' + ]); + + try { + DB::beginTransaction(); + collect($params)->each(function ($item) { + if (isset($item['id']) && isset($item['order'])) { + Server::where('id', $item['id'])->update(['sort' => $item['order']]); + } + }); + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + Log::error($e); + return $this->fail([500, '保存失败']); + + } + return $this->success(true); + } + + public function save(ServerSave $request) + { + $params = $request->validated(); + if ($request->input('id')) { + $server = Server::find($request->input('id')); + if (!$server) { + return $this->fail([400202, '服务器不存在']); + } + try { + $server->update($params); + return $this->success(true); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '保存失败']); + } + } + + try { + Server::create($params); + return $this->success(true); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '创建失败']); + } + + + } + + public function update(Request $request) + { + $request->validate([ + 'id' => 'required|integer', + 'show' => 'integer', + ]); + + $server = Server::find($request->id); + if (!$server) { + return $this->fail([400202, '服务器不存在']); + } + $server->show = (int) $request->show; + if (!$server->save()) { + return $this->fail([500, '保存失败']); + } + return $this->success(true); + } + + /** + * 删除 + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function drop(Request $request) + { + $request->validate([ + 'id' => 'required|integer', + ]); + if (Server::where('id', $request->id)->delete() === false) { + return $this->fail([500, '删除失败']); + } + return $this->success(true); + } + + /** + * 批量删除节点 + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function batchDelete(Request $request) + { + $request->validate([ + 'ids' => 'required|array', + 'ids.*' => 'integer', + ]); + + $ids = $request->input('ids'); + if (empty($ids)) { + return $this->fail([400, '请选择要删除的节点']); + } + + try { + $deleted = Server::whereIn('id', $ids)->delete(); + if ($deleted === false) { + return $this->fail([500, '批量删除失败']); + } + return $this->success(true); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '批量删除失败']); + } + } + + /** + * 重置节点流量 + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function resetTraffic(Request $request) + { + $request->validate([ + 'id' => 'required|integer', + ]); + + $server = Server::find($request->id); + if (!$server) { + return $this->fail([400202, '服务器不存在']); + } + + try { + $server->u = 0; + $server->d = 0; + $server->save(); + + Log::info("Server {$server->id} ({$server->name}) traffic reset by admin"); + return $this->success(true); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '重置失败']); + } + } + + /** + * 批量重置节点流量 + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function batchResetTraffic(Request $request) + { + $request->validate([ + 'ids' => 'required|array', + 'ids.*' => 'integer', + ]); + + $ids = $request->input('ids'); + if (empty($ids)) { + return $this->fail([400, '请选择要重置的节点']); + } + + try { + Server::whereIn('id', $ids)->update([ + 'u' => 0, + 'd' => 0, + ]); + + Log::info("Servers " . implode(',', $ids) . " traffic reset by admin"); + return $this->success(true); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '批量重置失败']); + } + } + + /** + * 复制节点 + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function copy(Request $request) + { + $server = Server::find($request->input('id')); + if (!$server) { + return $this->fail([400202, '服务器不存在']); + } + $server->show = 0; + $server->code = null; + Server::create($server->toArray()); + return $this->success(true); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/Server/RouteController.php b/Xboard/app/Http/Controllers/V2/Admin/Server/RouteController.php new file mode 100644 index 0000000..7f155ec --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/Server/RouteController.php @@ -0,0 +1,64 @@ + $routes + ]; + } + + public function save(Request $request) + { + $params = $request->validate([ + 'remarks' => 'required', + 'match' => 'required|array', + 'action' => 'required|in:block,direct,dns,proxy', + 'action_value' => 'nullable' + ], [ + 'remarks.required' => '备注不能为空', + 'match.required' => '匹配值不能为空', + 'action.required' => '动作类型不能为空', + 'action.in' => '动作类型参数有误' + ]); + $params['match'] = array_filter($params['match']); + // TODO: remove on 1.8.0 + if ($request->input('id')) { + try { + $route = ServerRoute::find($request->input('id')); + $route->update($params); + return $this->success(true); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500,'保存失败']); + } + } + try{ + ServerRoute::create($params); + return $this->success(true); + }catch(\Exception $e){ + Log::error($e); + return $this->fail([500,'创建失败']); + } + } + + public function drop(Request $request) + { + $route = ServerRoute::find($request->input('id')); + if (!$route) throw new ApiException('路由不存在'); + if (!$route->delete()) throw new ApiException('删除失败'); + return [ + 'data' => true + ]; + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/StatController.php b/Xboard/app/Http/Controllers/V2/Admin/StatController.php new file mode 100644 index 0000000..805fa94 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/StatController.php @@ -0,0 +1,508 @@ +service = $service; + } + public function getOverride(Request $request) + { + // 获取在线节点数 + $onlineNodes = Server::all()->filter(function ($server) { + return !!$server->is_online; + })->count(); + // 获取在线设备数和在线用户数 + $onlineDevices = User::where('t', '>=', time() - 600) + ->sum('online_count'); + $onlineUsers = User::where('t', '>=', time() - 600) + ->count(); + + // 获取今日流量统计 + $todayStart = strtotime('today'); + $todayTraffic = StatServer::where('record_at', '>=', $todayStart) + ->where('record_at', '<', time()) + ->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total') + ->first(); + + // 获取本月流量统计 + $monthStart = strtotime(date('Y-m-1')); + $monthTraffic = StatServer::where('record_at', '>=', $monthStart) + ->where('record_at', '<', time()) + ->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total') + ->first(); + + // 获取总流量统计 + $totalTraffic = StatServer::selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total') + ->first(); + + return [ + 'data' => [ + 'month_income' => Order::where('created_at', '>=', strtotime(date('Y-m-1'))) + ->where('created_at', '<', time()) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'), + 'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1'))) + ->where('created_at', '<', time()) + ->count(), + 'ticket_pending_total' => Ticket::where('status', 0) + ->count(), + 'commission_pending_total' => Order::where('commission_status', 0) + ->where('invite_user_id', '!=', NULL) + ->whereNotIn('status', [0, 2]) + ->where('commission_balance', '>', 0) + ->count(), + 'day_income' => Order::where('created_at', '>=', strtotime(date('Y-m-d'))) + ->where('created_at', '<', time()) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'), + 'last_month_income' => Order::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1')))) + ->where('created_at', '<', strtotime(date('Y-m-1'))) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'), + 'commission_month_payout' => CommissionLog::where('created_at', '>=', strtotime(date('Y-m-1'))) + ->where('created_at', '<', time()) + ->sum('get_amount'), + 'commission_last_month_payout' => CommissionLog::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1')))) + ->where('created_at', '<', strtotime(date('Y-m-1'))) + ->sum('get_amount'), + // 新增统计数据 + 'online_nodes' => $onlineNodes, + 'online_devices' => $onlineDevices, + 'online_users' => $onlineUsers, + 'today_traffic' => [ + 'upload' => $todayTraffic->upload ?? 0, + 'download' => $todayTraffic->download ?? 0, + 'total' => $todayTraffic->total ?? 0 + ], + 'month_traffic' => [ + 'upload' => $monthTraffic->upload ?? 0, + 'download' => $monthTraffic->download ?? 0, + 'total' => $monthTraffic->total ?? 0 + ], + 'total_traffic' => [ + 'upload' => $totalTraffic->upload ?? 0, + 'download' => $totalTraffic->download ?? 0, + 'total' => $totalTraffic->total ?? 0 + ] + ] + ]; + } + + /** + * Get order statistics with filtering and pagination + * + * @param Request $request + * @return array + */ + public function getOrder(Request $request) + { + $request->validate([ + 'start_date' => 'nullable|date_format:Y-m-d', + 'end_date' => 'nullable|date_format:Y-m-d', + 'type' => 'nullable|in:paid_total,paid_count,commission_total,commission_count', + ]); + + $query = Stat::where('record_type', 'd'); + + // Apply date filters + if ($request->input('start_date')) { + $query->where('record_at', '>=', strtotime($request->input('start_date'))); + } + if ($request->input('end_date')) { + $query->where('record_at', '<=', strtotime($request->input('end_date') . ' 23:59:59')); + } + + $statistics = $query->orderBy('record_at', 'DESC') + ->get(); + + $summary = [ + 'paid_total' => 0, + 'paid_count' => 0, + 'commission_total' => 0, + 'commission_count' => 0, + 'start_date' => $request->input('start_date', date('Y-m-d', $statistics->last()?->record_at)), + 'end_date' => $request->input('end_date', date('Y-m-d', $statistics->first()?->record_at)), + 'avg_paid_amount' => 0, + 'avg_commission_amount' => 0 + ]; + + $dailyStats = []; + foreach ($statistics as $statistic) { + $date = date('Y-m-d', $statistic['record_at']); + + // Update summary + $summary['paid_total'] += $statistic['paid_total']; + $summary['paid_count'] += $statistic['paid_count']; + $summary['commission_total'] += $statistic['commission_total']; + $summary['commission_count'] += $statistic['commission_count']; + + // Calculate daily stats + $dailyData = [ + 'date' => $date, + 'paid_total' => $statistic['paid_total'], + 'paid_count' => $statistic['paid_count'], + 'commission_total' => $statistic['commission_total'], + 'commission_count' => $statistic['commission_count'], + 'avg_order_amount' => $statistic['paid_count'] > 0 ? round($statistic['paid_total'] / $statistic['paid_count'], 2) : 0, + 'avg_commission_amount' => $statistic['commission_count'] > 0 ? round($statistic['commission_total'] / $statistic['commission_count'], 2) : 0 + ]; + + if ($request->input('type')) { + $dailyStats[] = [ + 'date' => $date, + 'value' => $statistic[$request->input('type')], + 'type' => $this->getTypeLabel($request->input('type')) + ]; + } else { + $dailyStats[] = $dailyData; + } + } + + // Calculate averages for summary + if ($summary['paid_count'] > 0) { + $summary['avg_paid_amount'] = round($summary['paid_total'] / $summary['paid_count'], 2); + } + if ($summary['commission_count'] > 0) { + $summary['avg_commission_amount'] = round($summary['commission_total'] / $summary['commission_count'], 2); + } + + // Add percentage calculations to summary + $summary['commission_rate'] = $summary['paid_total'] > 0 + ? round(($summary['commission_total'] / $summary['paid_total']) * 100, 2) + : 0; + + return [ + 'code' => 0, + 'message' => 'success', + 'data' => [ + 'list' => array_reverse($dailyStats), + 'summary' => $summary, + ] + ]; + } + + /** + * Get human readable label for statistic type + * + * @param string $type + * @return string + */ + private function getTypeLabel(string $type): string + { + return match ($type) { + 'paid_total' => '收款金额', + 'paid_count' => '收款笔数', + 'commission_total' => '佣金金额(已发放)', + 'commission_count' => '佣金笔数(已发放)', + default => $type + }; + } + + // 获取当日实时流量排行 + public function getServerLastRank() + { + $data = $this->service->getServerRank(); + return $this->success(data: $data); + } + // 获取昨日节点流量排行 + public function getServerYesterdayRank() + { + $data = $this->service->getServerRank('yesterday'); + return $this->success($data); + } + + public function getStatUser(Request $request) + { + $request->validate([ + 'user_id' => 'required|integer' + ]); + + $pageSize = $request->input('pageSize', 10); + $records = StatUser::orderBy('record_at', 'DESC') + ->where('user_id', $request->input('user_id')) + ->paginate($pageSize); + + $data = $records->items(); + return [ + 'data' => $data, + 'total' => $records->total(), + ]; + } + + public function getStatRecord(Request $request) + { + return [ + 'data' => $this->service->getStatRecord($request->input('type')) + ]; + } + + /** + * Get comprehensive statistics data including income, users, and growth rates + */ + public function getStats() + { + $currentMonthStart = strtotime(date('Y-m-01')); + $lastMonthStart = strtotime('-1 month', $currentMonthStart); + $twoMonthsAgoStart = strtotime('-2 month', $currentMonthStart); + + // Today's start timestamp + $todayStart = strtotime('today'); + $yesterdayStart = strtotime('-1 day', $todayStart); + + // 获取在线节点数 + $onlineNodes = Server::all()->filter(function ($server) { + return !!$server->is_online; + })->count(); + + // 获取在线设备数和在线用户数 + $onlineDevices = User::where('t', '>=', time() - 600) + ->sum('online_count'); + $onlineUsers = User::where('t', '>=', time() - 600) + ->count(); + + // 获取今日流量统计 + $todayTraffic = StatServer::where('record_at', '>=', $todayStart) + ->where('record_at', '<', time()) + ->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total') + ->first(); + + // 获取本月流量统计 + $monthTraffic = StatServer::where('record_at', '>=', $currentMonthStart) + ->where('record_at', '<', time()) + ->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total') + ->first(); + + // 获取总流量统计 + $totalTraffic = StatServer::selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total') + ->first(); + + // Today's income + $todayIncome = Order::where('created_at', '>=', $todayStart) + ->where('created_at', '<', time()) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'); + + // Yesterday's income for day growth calculation + $yesterdayIncome = Order::where('created_at', '>=', $yesterdayStart) + ->where('created_at', '<', $todayStart) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'); + + // Current month income + $currentMonthIncome = Order::where('created_at', '>=', $currentMonthStart) + ->where('created_at', '<', time()) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'); + + // Last month income + $lastMonthIncome = Order::where('created_at', '>=', $lastMonthStart) + ->where('created_at', '<', $currentMonthStart) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'); + + // Last month commission payout + $lastMonthCommissionPayout = CommissionLog::where('created_at', '>=', $lastMonthStart) + ->where('created_at', '<', $currentMonthStart) + ->sum('get_amount'); + + // Current month commission payout + $currentMonthCommissionPayout = CommissionLog::where('created_at', '>=', $currentMonthStart) + ->where('created_at', '<', time()) + ->sum('get_amount'); + + // Current month new users + $currentMonthNewUsers = User::where('created_at', '>=', $currentMonthStart) + ->where('created_at', '<', time()) + ->count(); + + // Total users + $totalUsers = User::count(); + + // Active users (users with valid subscription) + $activeUsers = User::where(function ($query) { + $query->where('expired_at', '>=', time()) + ->orWhere('expired_at', NULL); + })->count(); + + // Previous month income for growth calculation + $twoMonthsAgoIncome = Order::where('created_at', '>=', $twoMonthsAgoStart) + ->where('created_at', '<', $lastMonthStart) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'); + + // Previous month commission for growth calculation + $twoMonthsAgoCommission = CommissionLog::where('created_at', '>=', $twoMonthsAgoStart) + ->where('created_at', '<', $lastMonthStart) + ->sum('get_amount'); + + // Previous month users for growth calculation + $lastMonthNewUsers = User::where('created_at', '>=', $lastMonthStart) + ->where('created_at', '<', $currentMonthStart) + ->count(); + + // Calculate growth rates + $monthIncomeGrowth = $lastMonthIncome > 0 ? round(($currentMonthIncome - $lastMonthIncome) / $lastMonthIncome * 100, 1) : 0; + $lastMonthIncomeGrowth = $twoMonthsAgoIncome > 0 ? round(($lastMonthIncome - $twoMonthsAgoIncome) / $twoMonthsAgoIncome * 100, 1) : 0; + $commissionGrowth = $twoMonthsAgoCommission > 0 ? round(($lastMonthCommissionPayout - $twoMonthsAgoCommission) / $twoMonthsAgoCommission * 100, 1) : 0; + $userGrowth = $lastMonthNewUsers > 0 ? round(($currentMonthNewUsers - $lastMonthNewUsers) / $lastMonthNewUsers * 100, 1) : 0; + $dayIncomeGrowth = $yesterdayIncome > 0 ? round(($todayIncome - $yesterdayIncome) / $yesterdayIncome * 100, 1) : 0; + + // 获取待处理工单和佣金数据 + $ticketPendingTotal = Ticket::where('status', 0)->count(); + $commissionPendingTotal = Order::where('commission_status', 0) + ->where('invite_user_id', '!=', NULL) + ->whereIn('status', [Order::STATUS_COMPLETED]) + ->where('commission_balance', '>', 0) + ->count(); + + return [ + 'data' => [ + // 收入相关 + 'todayIncome' => $todayIncome, + 'dayIncomeGrowth' => $dayIncomeGrowth, + 'currentMonthIncome' => $currentMonthIncome, + 'lastMonthIncome' => $lastMonthIncome, + 'monthIncomeGrowth' => $monthIncomeGrowth, + 'lastMonthIncomeGrowth' => $lastMonthIncomeGrowth, + + // 佣金相关 + 'currentMonthCommissionPayout' => $currentMonthCommissionPayout, + 'lastMonthCommissionPayout' => $lastMonthCommissionPayout, + 'commissionGrowth' => $commissionGrowth, + 'commissionPendingTotal' => $commissionPendingTotal, + + // 用户相关 + 'currentMonthNewUsers' => $currentMonthNewUsers, + 'totalUsers' => $totalUsers, + 'activeUsers' => $activeUsers, + 'userGrowth' => $userGrowth, + 'onlineUsers' => $onlineUsers, + 'onlineDevices' => $onlineDevices, + + // 工单相关 + 'ticketPendingTotal' => $ticketPendingTotal, + + // 节点相关 + 'onlineNodes' => $onlineNodes, + + // 流量统计 + 'todayTraffic' => [ + 'upload' => $todayTraffic->upload ?? 0, + 'download' => $todayTraffic->download ?? 0, + 'total' => $todayTraffic->total ?? 0 + ], + 'monthTraffic' => [ + 'upload' => $monthTraffic->upload ?? 0, + 'download' => $monthTraffic->download ?? 0, + 'total' => $monthTraffic->total ?? 0 + ], + 'totalTraffic' => [ + 'upload' => $totalTraffic->upload ?? 0, + 'download' => $totalTraffic->download ?? 0, + 'total' => $totalTraffic->total ?? 0 + ] + ] + ]; + } + + /** + * Get traffic ranking data for nodes or users + * + * @param Request $request + * @return array + */ + public function getTrafficRank(Request $request) + { + $request->validate([ + 'type' => 'required|in:node,user', + 'start_time' => 'nullable|integer|min:1000000000|max:9999999999', + 'end_time' => 'nullable|integer|min:1000000000|max:9999999999' + ]); + + $type = $request->input('type'); + $startDate = $request->input('start_time', strtotime('-7 days')); + $endDate = $request->input('end_time', time()); + $previousStartDate = $startDate - ($endDate - $startDate); + $previousEndDate = $startDate; + + if ($type === 'node') { + // Get node traffic data + $currentData = StatServer::selectRaw('server_id as id, SUM(u + d) as value') + ->where('record_at', '>=', $startDate) + ->where('record_at', '<=', $endDate) + ->groupBy('server_id') + ->orderBy('value', 'DESC') + ->limit(10) + ->get(); + + // Get previous period data for comparison + $previousData = StatServer::selectRaw('server_id as id, SUM(u + d) as value') + ->where('record_at', '>=', $previousStartDate) + ->where('record_at', '<', $previousEndDate) + ->whereIn('server_id', $currentData->pluck('id')) + ->groupBy('server_id') + ->get() + ->keyBy('id'); + + } else { + // Get user traffic data + $currentData = StatUser::selectRaw('user_id as id, SUM(u + d) as value') + ->where('record_at', '>=', $startDate) + ->where('record_at', '<=', $endDate) + ->groupBy('user_id') + ->orderBy('value', 'DESC') + ->limit(10) + ->get(); + + // Get previous period data for comparison + $previousData = StatUser::selectRaw('user_id as id, SUM(u + d) as value') + ->where('record_at', '>=', $previousStartDate) + ->where('record_at', '<', $previousEndDate) + ->whereIn('user_id', $currentData->pluck('id')) + ->groupBy('user_id') + ->get() + ->keyBy('id'); + } + + $result = []; + $ids = $currentData->pluck('id'); + $names = $type === 'node' + ? Server::whereIn('id', $ids)->pluck('name', 'id') + : User::whereIn('id', $ids)->pluck('email', 'id'); + + foreach ($currentData as $data) { + $previousValue = isset($previousData[$data->id]) ? $previousData[$data->id]->value : 0; + $change = $previousValue > 0 ? round(($data->value - $previousValue) / $previousValue * 100, 1) : 0; + + $result[] = [ + 'id' => (string) $data->id, + 'name' => $names[$data->id] ?? ($type === 'node' ? "Node {$data->id}" : "User {$data->id}"), + 'value' => $data->value, + 'previousValue' => $previousValue, + 'change' => $change, + 'timestamp' => date('c', $endDate) + ]; + } + + return [ + 'timestamp' => date('c'), + 'data' => $result + ]; + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/SystemController.php b/Xboard/app/Http/Controllers/V2/Admin/SystemController.php new file mode 100644 index 0000000..2ba35b5 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/SystemController.php @@ -0,0 +1,144 @@ + $this->getScheduleStatus(), + 'horizon' => $this->getHorizonStatus(), + 'schedule_last_runtime' => Cache::get(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null)), + ]; + return $this->success($data); + } + + public function getQueueWorkload(WorkloadRepository $workload) + { + return $this->success(collect($workload->get())->sortBy('name')->values()->toArray()); + } + + protected function getScheduleStatus(): bool + { + return (time() - 120) < Cache::get(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null)); + } + + protected function getHorizonStatus(): bool + { + if (!$masters = app(MasterSupervisorRepository::class)->all()) { + return false; + } + + return collect($masters)->contains(function ($master) { + return $master->status === 'paused'; + }) ? false : true; + } + + public function getQueueStats() + { + $data = [ + 'failedJobs' => app(JobRepository::class)->countRecentlyFailed(), + 'jobsPerMinute' => app(MetricsRepository::class)->jobsProcessedPerMinute(), + 'pausedMasters' => $this->totalPausedMasters(), + 'periods' => [ + 'failedJobs' => config('horizon.trim.recent_failed', config('horizon.trim.failed')), + 'recentJobs' => config('horizon.trim.recent'), + ], + 'processes' => $this->totalProcessCount(), + 'queueWithMaxRuntime' => app(MetricsRepository::class)->queueWithMaximumRuntime(), + 'queueWithMaxThroughput' => app(MetricsRepository::class)->queueWithMaximumThroughput(), + 'recentJobs' => app(JobRepository::class)->countRecent(), + 'status' => $this->getHorizonStatus(), + 'wait' => collect(app(WaitTimeCalculator::class)->calculate())->take(1), + ]; + return $this->success($data); + } + + /** + * Get the total process count across all supervisors. + * + * @return int + */ + protected function totalProcessCount() + { + $supervisors = app(SupervisorRepository::class)->all(); + + return collect($supervisors)->reduce(function ($carry, $supervisor) { + return $carry + collect($supervisor->processes)->sum(); + }, 0); + } + + /** + * Get the number of master supervisors that are currently paused. + * + * @return int + */ + protected function totalPausedMasters() + { + if (!$masters = app(MasterSupervisorRepository::class)->all()) { + return 0; + } + + return collect($masters)->filter(function ($master) { + return $master->status === 'paused'; + })->count(); + } + + public function getAuditLog(Request $request) + { + $current = max(1, (int) $request->input('current', 1)); + $pageSize = max(10, (int) $request->input('page_size', 10)); + + $builder = AdminAuditLog::with('admin:id,email') + ->orderBy('id', 'DESC') + ->when($request->input('action'), fn($q, $v) => $q->where('action', $v)) + ->when($request->input('admin_id'), fn($q, $v) => $q->where('admin_id', $v)) + ->when($request->input('keyword'), function ($q, $keyword) { + $q->where(function ($q) use ($keyword) { + $q->where('uri', 'like', '%' . $keyword . '%') + ->orWhere('request_data', 'like', '%' . $keyword . '%'); + }); + }); + + $total = $builder->count(); + $res = $builder->forPage($current, $pageSize)->get(); + + return response(['data' => $res, 'total' => $total]); + } + + public function getHorizonFailedJobs(Request $request, JobRepository $jobRepository) + { + $current = max(1, (int) $request->input('current', 1)); + $pageSize = max(10, (int) $request->input('page_size', 20)); + $offset = ($current - 1) * $pageSize; + + $failedJobs = collect($jobRepository->getFailed()) + ->sortByDesc('failed_at') + ->slice($offset, $pageSize) + ->values(); + + $total = $jobRepository->countFailed(); + + return response()->json([ + 'data' => $failedJobs, + 'total' => $total, + 'current' => $current, + 'page_size' => $pageSize, + ]); + } + +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/ThemeController.php b/Xboard/app/Http/Controllers/V2/Admin/ThemeController.php new file mode 100644 index 0000000..727cd27 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/ThemeController.php @@ -0,0 +1,150 @@ +themeService = $themeService; + } + + /** + * 上传新主题 + * + * @throws ApiException + */ + public function upload(Request $request) + { + $request->validate([ + 'file' => [ + 'required', + 'file', + 'mimes:zip', + 'max:10240', // 最大10MB + ] + ], [ + 'file.required' => '请选择主题包文件', + 'file.file' => '无效的文件类型', + 'file.mimes' => '主题包必须是zip格式', + 'file.max' => '主题包大小不能超过10MB' + ]); + + try { + // 检查上传目录权限 + $uploadPath = storage_path('tmp'); + if (!File::exists($uploadPath)) { + File::makeDirectory($uploadPath, 0755, true); + } + + if (!is_writable($uploadPath)) { + throw new ApiException('上传目录无写入权限'); + } + + // 检查主题目录权限 + $themePath = base_path('theme'); + if (!is_writable($themePath)) { + throw new ApiException('主题目录无写入权限'); + } + + $file = $request->file('file'); + + // 检查文件MIME类型 + $mimeType = $file->getMimeType(); + if (!in_array($mimeType, ['application/zip', 'application/x-zip-compressed'])) { + throw new ApiException('无效的文件类型,仅支持ZIP格式'); + } + + // 检查文件名安全性 + $originalName = $file->getClientOriginalName(); + if (!preg_match('/^[a-zA-Z0-9\-\_\.]+\.zip$/', $originalName)) { + throw new ApiException('主题包文件名只能包含字母、数字、下划线、中划线和点'); + } + + $this->themeService->upload($file); + return $this->success(true); + + } catch (ApiException $e) { + throw $e; + } catch (\Exception $e) { + Log::error('Theme upload failed', [ + 'error' => $e->getMessage(), + 'file' => $request->file('file')?->getClientOriginalName() + ]); + throw new ApiException('主题上传失败:' . $e->getMessage()); + } + } + + /** + * 删除主题 + */ + public function delete(Request $request) + { + $payload = $request->validate([ + 'name' => 'required' + ]); + $this->themeService->delete($payload['name']); + return $this->success(true); + } + + /** + * 获取所有主题和其配置列 + * + * @return \Illuminate\Http\JsonResponse + */ + public function getThemes() + { + $data = [ + 'themes' => $this->themeService->getList(), + 'active' => admin_setting('frontend_theme', 'Xboard') + ]; + return $this->success($data); + } + + /** + * 切换主题 + */ + public function switchTheme(Request $request) + { + $payload = $request->validate([ + 'name' => 'required' + ]); + $this->themeService->switch($payload['name']); + return $this->success(true); + } + + /** + * 获取主题配置 + */ + public function getThemeConfig(Request $request) + { + $payload = $request->validate([ + 'name' => 'required' + ]); + $data = $this->themeService->getConfig($payload['name']); + return $this->success($data); + } + + /** + * 保存主题配置 + */ + public function saveThemeConfig(Request $request) + { + $payload = $request->validate([ + 'name' => 'required', + 'config' => 'required' + ]); + $this->themeService->updateConfig($payload['name'], $payload['config']); + $config = $this->themeService->getConfig($payload['name']); + return $this->success($config); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/TicketController.php b/Xboard/app/Http/Controllers/V2/Admin/TicketController.php new file mode 100644 index 0000000..ca6d8c4 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/TicketController.php @@ -0,0 +1,156 @@ +has('filter')) { + collect($request->input('filter'))->each(function ($filter) use ($builder) { + $key = $filter['id']; + $value = $filter['value']; + $builder->where(function ($query) use ($key, $value) { + if (is_array($value)) { + $query->whereIn($key, $value); + } else { + $query->where($key, 'like', "%{$value}%"); + } + }); + }); + } + + if ($request->has('sort')) { + collect($request->input('sort'))->each(function ($sort) use ($builder) { + $key = $sort['id']; + $value = $sort['desc'] ? 'DESC' : 'ASC'; + $builder->orderBy($key, $value); + }); + } + } + public function fetch(Request $request) + { + if ($request->input('id')) { + return $this->fetchTicketById($request); + } else { + return $this->fetchTickets($request); + } + } + + /** + * Summary of fetchTicketById + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + private function fetchTicketById(Request $request) + { + $ticket = Ticket::with('messages', 'user')->find($request->input('id')); + + if (!$ticket) { + return $this->fail([400202, '工单不存在']); + } + $result = $ticket->toArray(); + $result['user'] = UserController::transformUserData($ticket->user); + + return $this->success($result); + } + + /** + * Summary of fetchTickets + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response + */ + private function fetchTickets(Request $request) + { + $ticketModel = Ticket::with('user') + ->when($request->has('status'), function ($query) use ($request) { + $query->where('status', $request->input('status')); + }) + ->when($request->has('reply_status'), function ($query) use ($request) { + $query->whereIn('reply_status', $request->input('reply_status')); + }) + ->when($request->has('email'), function ($query) use ($request) { + $query->whereHas('user', function ($q) use ($request) { + $q->where('email', $request->input('email')); + }); + }); + + $this->applyFiltersAndSorts($request, $ticketModel); + $tickets = $ticketModel + ->latest('updated_at') + ->paginate( + perPage: $request->integer('pageSize', 10), + page: $request->integer('current', 1) + ); + + // 获取items然后映射转换 + $items = collect($tickets->items())->map(function ($ticket) { + $ticketData = $ticket->toArray(); + $ticketData['user'] = UserController::transformUserData($ticket->user); + return $ticketData; + })->all(); + + return response([ + 'data' => $items, + 'total' => $tickets->total() + ]); + } + + public function reply(Request $request) + { + $request->validate([ + 'id' => 'required|numeric', + 'message' => 'required|string' + ], [ + 'id.required' => '工单ID不能为空', + 'message.required' => '消息不能为空' + ]); + $ticketService = new TicketService(); + $ticketService->replyByAdmin( + $request->input('id'), + $request->input('message'), + $request->user()->id + ); + return $this->success(true); + } + + public function close(Request $request) + { + $request->validate([ + 'id' => 'required|numeric' + ], [ + 'id.required' => '工单ID不能为空' + ]); + try { + $ticket = Ticket::findOrFail($request->input('id')); + $ticket->status = Ticket::STATUS_CLOSED; + $ticket->save(); + return $this->success(true); + } catch (ModelNotFoundException $e) { + return $this->fail([400202, '工单不存在']); + } catch (\Exception $e) { + return $this->fail([500101, '关闭失败']); + } + } + + public function show($ticketId) + { + $ticket = Ticket::with([ + 'user', + 'messages' => function ($query) { + $query->with(['user']); // 如果需要用户信息 + } + ])->findOrFail($ticketId); + + // 自动包含 is_me 属性 + return response()->json([ + 'data' => $ticket + ]); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Admin/TrafficResetController.php b/Xboard/app/Http/Controllers/V2/Admin/TrafficResetController.php new file mode 100644 index 0000000..53e4f54 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/TrafficResetController.php @@ -0,0 +1,235 @@ +trafficResetService = $trafficResetService; + } + + /** + * 获取流量重置日志列表 + */ + public function logs(Request $request): JsonResponse + { + $request->validate([ + 'user_id' => 'nullable|integer', + 'user_email' => 'nullable|string', + 'reset_type' => 'nullable|string|in:' . implode(',', array_keys(TrafficResetLog::getResetTypeNames())), + 'trigger_source' => 'nullable|string|in:' . implode(',', array_keys(TrafficResetLog::getSourceNames())), + 'start_date' => 'nullable|date', + 'end_date' => 'nullable|date|after_or_equal:start_date', + 'per_page' => 'nullable|integer|min:1|max:10000', + 'page' => 'nullable|integer|min:1', + ]); + + $query = TrafficResetLog::with(['user:id,email']) + ->orderBy('reset_time', 'desc'); + + // 筛选条件 + if ($request->filled('user_id')) { + $query->where('user_id', $request->user_id); + } + + if ($request->filled('user_email')) { + $query->whereHas('user', function ($query) use ($request) { + $query->where('email', 'like', '%' . $request->user_email . '%'); + }); + } + + if ($request->filled('reset_type')) { + $query->where('reset_type', $request->reset_type); + } + + if ($request->filled('trigger_source')) { + $query->where('trigger_source', $request->trigger_source); + } + + if ($request->filled('start_date')) { + $query->where('reset_time', '>=', $request->start_date); + } + + if ($request->filled('end_date')) { + $query->where('reset_time', '<=', $request->end_date . ' 23:59:59'); + } + + $perPage = $request->get('per_page', 20); + $logs = $query->paginate($perPage); + + // 格式化数据 + $formattedLogs = $logs->getCollection()->map(function (TrafficResetLog $log) { + return [ + 'id' => $log->id, + 'user_id' => $log->user_id, + 'user_email' => $log->user->email ?? 'N/A', + 'reset_type' => $log->reset_type, + 'reset_type_name' => $log->getResetTypeName(), + 'reset_time' => $log->reset_time, + 'old_traffic' => [ + 'upload' => $log->old_upload, + 'download' => $log->old_download, + 'total' => $log->old_total, + 'formatted' => $log->formatTraffic($log->old_total), + ], + 'new_traffic' => [ + 'upload' => $log->new_upload, + 'download' => $log->new_download, + 'total' => $log->new_total, + 'formatted' => $log->formatTraffic($log->new_total), + ], + 'trigger_source' => $log->trigger_source, + 'trigger_source_name' => $log->getSourceName(), + 'metadata' => $log->metadata, + 'created_at' => $log->created_at, + ]; + }); + + return response()->json([ + 'data' => $formattedLogs->toArray(), + 'pagination' => [ + 'current_page' => $logs->currentPage(), + 'last_page' => $logs->lastPage(), + 'per_page' => $logs->perPage(), + 'total' => $logs->total(), + ], + ]); + } + + /** + * 获取流量重置统计信息 + */ + public function stats(Request $request): JsonResponse + { + $request->validate([ + 'days' => 'nullable|integer|min:1|max:365', + ]); + + $days = $request->get('days', 30); + $startDate = now()->subDays($days)->startOfDay(); + + $stats = [ + 'total_resets' => TrafficResetLog::where('reset_time', '>=', $startDate)->count(), + 'auto_resets' => TrafficResetLog::where('reset_time', '>=', $startDate) + ->where('trigger_source', TrafficResetLog::SOURCE_AUTO) + ->count(), + 'manual_resets' => TrafficResetLog::where('reset_time', '>=', $startDate) + ->where('trigger_source', TrafficResetLog::SOURCE_MANUAL) + ->count(), + 'cron_resets' => TrafficResetLog::where('reset_time', '>=', $startDate) + ->where('trigger_source', TrafficResetLog::SOURCE_CRON) + ->count(), + ]; + + return response()->json([ + 'data' => $stats + ]); + } + + /** + * 手动重置用户流量 + */ + public function resetUser(Request $request): JsonResponse + { + $request->validate([ + 'user_id' => 'required|integer|exists:v2_user,id', + 'reason' => 'nullable|string|max:255', + ]); + + $user = User::find($request->user_id); + + if (!$this->trafficResetService->canReset($user)) { + return response()->json([ + 'message' => __('traffic_reset.user_cannot_reset') + ], 400); + } + + $metadata = []; + if ($request->filled('reason')) { + $metadata['reason'] = $request->reason; + $metadata['admin_id'] = auth()->user()?->id; + } + + $success = $this->trafficResetService->manualReset($user, $metadata); + + if (!$success) { + return response()->json([ + 'message' => __('traffic_reset.reset_failed') + ], 500); + } + + return response()->json([ + 'message' => __('traffic_reset.reset_success'), + 'data' => [ + 'user_id' => $user->id, + 'email' => $user->email, + 'reset_time' => now(), + 'next_reset_at' => $user->fresh()->next_reset_at, + ] + ]); + } + + + + /** + * 获取用户重置历史 + */ + public function userHistory(Request $request, int $userId): JsonResponse + { + $request->validate([ + 'limit' => 'nullable|integer|min:1|max:50', + ]); + + $user = User::findOrFail($userId); + $limit = $request->get('limit', 10); + + $history = $this->trafficResetService->getUserResetHistory($user, $limit); + + /** @var \Illuminate\Database\Eloquent\Collection $history */ + $data = $history->map(function (TrafficResetLog $log) { + return [ + 'id' => $log->id, + 'reset_type' => $log->reset_type, + 'reset_type_name' => $log->getResetTypeName(), + 'reset_time' => $log->reset_time, + 'old_traffic' => [ + 'upload' => $log->old_upload, + 'download' => $log->old_download, + 'total' => $log->old_total, + 'formatted' => $log->formatTraffic($log->old_total), + ], + 'trigger_source' => $log->trigger_source, + 'trigger_source_name' => $log->getSourceName(), + 'metadata' => $log->metadata, + ]; + }); + + return response()->json([ + "data" => [ + 'user' => [ + 'id' => $user->id, + 'email' => $user->email, + 'reset_count' => $user->reset_count, + 'last_reset_at' => $user->last_reset_at, + 'next_reset_at' => $user->next_reset_at, + ], + 'history' => $data, + ] + ]); + } + + +} \ No newline at end of file diff --git a/Xboard/app/Http/Controllers/V2/Admin/UpdateController.php b/Xboard/app/Http/Controllers/V2/Admin/UpdateController.php new file mode 100644 index 0000000..d846466 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/UpdateController.php @@ -0,0 +1,28 @@ +updateService = $updateService; + } + + public function checkUpdate() + { + return $this->success($this->updateService->checkForUpdates()); + } + + public function executeUpdate() + { + $result = $this->updateService->executeUpdate(); + return $result['success'] ? $this->success($result) : $this->fail([500, $result['message']]); + } +} \ No newline at end of file diff --git a/Xboard/app/Http/Controllers/V2/Admin/UserController.php b/Xboard/app/Http/Controllers/V2/Admin/UserController.php new file mode 100644 index 0000000..b135270 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Admin/UserController.php @@ -0,0 +1,682 @@ +input('id')); + if (!$user) + return $this->fail([400202, '用户不存在']); + $user->token = Helper::guid(); + $user->uuid = Helper::guid(true); + return $this->success($user->save()); + } + + // Apply filters and sorts to the query builder. + private function applyFiltersAndSorts(Request $request, Builder|QueryBuilder $builder): void + { + $this->applyFilters($request, $builder); + $this->applySorting($request, $builder); + } + + // Apply filters to the query builder. + private function applyFilters(Request $request, Builder|QueryBuilder $builder): void + { + if (!$request->has('filter')) { + return; + } + + collect($request->input('filter'))->each(function ($filter) use ($builder) { + $field = $filter['id']; + $value = $filter['value']; + $logic = strtolower($filter['logic'] ?? 'and'); + + if ($logic === 'or') { + $builder->orWhere(function ($query) use ($field, $value) { + $this->buildFilterQuery($query, $field, $value); + }); + } else { + $builder->where(function ($query) use ($field, $value) { + $this->buildFilterQuery($query, $field, $value); + }); + } + }); + } + + // Build one filter query condition. + private function buildFilterQuery(Builder|QueryBuilder $query, string $field, mixed $value): void + { + // 处理关联查询 + if (str_contains($field, '.')) { + if (!method_exists($query, 'whereHas')) { + return; + } + [$relation, $relationField] = explode('.', $field); + $query->whereHas($relation, function ($q) use ($relationField, $value) { + if (is_array($value)) { + $q->whereIn($relationField, $value); + } else if (is_string($value) && str_contains($value, ':')) { + [$operator, $filterValue] = explode(':', $value, 2); + $this->applyQueryCondition($q, $relationField, $operator, $filterValue); + } else { + $q->where($relationField, 'like', "%{$value}%"); + } + }); + return; + } + + // 处理数组值的 'in' 操作 + if (is_array($value)) { + $query->whereIn($field === 'group_ids' ? 'group_id' : $field, $value); + return; + } + + // 处理基于运算符的过滤 + if (!is_string($value) || !str_contains($value, ':')) { + $query->where($field, 'like', "%{$value}%"); + return; + } + + [$operator, $filterValue] = explode(':', $value, 2); + + // 转换数字字符串为适当的类型 + if (is_numeric($filterValue)) { + $filterValue = strpos($filterValue, '.') !== false + ? (float) $filterValue + : (int) $filterValue; + } + + // 处理计算字段 + $queryField = match ($field) { + 'total_used' => DB::raw('(u + d)'), + default => $field + }; + + $this->applyQueryCondition($query, $queryField, $operator, $filterValue); + } + + // Apply sorting rules to the query builder. + private function applySorting(Request $request, Builder|QueryBuilder $builder): void + { + if (!$request->has('sort')) { + return; + } + + collect($request->input('sort'))->each(function ($sort) use ($builder) { + $field = $sort['id']; + $direction = $sort['desc'] ? 'DESC' : 'ASC'; + $builder->orderBy($field, $direction); + }); + } + + // Resolve bulk operation scope and normalize user_ids. + private function resolveScope(Request $request): array + { + $scope = $request->input('scope'); + $userIds = $request->input('user_ids'); + + $hasSelection = is_array($userIds) && count(array_filter($userIds, static fn($v) => is_numeric($v))) > 0; + $hasFilter = $request->has('filter') && !empty($request->input('filter')); + + if (!in_array($scope, ['selected', 'filtered', 'all'], true)) { + if ($hasSelection) { + $scope = 'selected'; + } elseif ($hasFilter) { + $scope = 'filtered'; + } else { + $scope = 'all'; + } + } + + $normalizedIds = []; + if ($scope === 'selected') { + $normalizedIds = is_array($userIds) ? $userIds : []; + $normalizedIds = array_values(array_unique(array_map(static function ($v) { + return is_numeric($v) ? (int) $v : null; + }, $normalizedIds))); + $normalizedIds = array_values(array_filter($normalizedIds, static fn($v) => is_int($v))); + } + + return [ + 'scope' => $scope, + 'user_ids' => $normalizedIds, + ]; + } + + // Fetch paginated user list (filters + sorting). + public function fetch(Request $request) + { + $current = $request->input('current', 1); + $pageSize = $request->input('pageSize', 10); + + $userModel = User::query() + ->with(['plan:id,name', 'invite_user:id,email', 'group:id,name']) + ->select((new User())->getTable() . '.*') + ->selectRaw('(u + d) as total_used'); + + $this->applyFiltersAndSorts($request, $userModel); + + $users = $userModel->orderBy('id', 'desc') + ->paginate($pageSize, ['*'], 'page', $current); + + $users->getCollection()->transform(function ($user): array { + return self::transformUserData($user); + }); + + return $this->paginate($users); + } + + // Transform user fields for API response. + public static function transformUserData(User $user): array + { + $user = $user->toArray(); + $user['balance'] = $user['balance'] / 100; + $user['commission_balance'] = $user['commission_balance'] / 100; + $user['subscribe_url'] = Helper::getSubscribeUrl($user['token']); + return $user; + } + + public function getUserInfoById(Request $request) + { + $request->validate([ + 'id' => 'required|numeric' + ], [ + 'id.required' => '用户ID不能为空' + ]); + $user = User::find($request->input('id'))->load('invite_user'); + return $this->success($user); + } + + public function update(UserUpdate $request) + { + $params = $request->validated(); + + $user = User::find($request->input('id')); + if (!$user) { + return $this->fail([400202, '用户不存在']); + } + if (isset($params['email'])) { + if (User::byEmail($params['email'])->first() && $user->email !== $params['email']) { + return $this->fail([400201, '邮箱已被使用']); + } + } + // 处理密码 + if (isset($params['password'])) { + $params['password'] = password_hash($params['password'], PASSWORD_DEFAULT); + $params['password_algo'] = NULL; + } else { + unset($params['password']); + } + // 处理订阅计划 + if (isset($params['plan_id'])) { + $plan = Plan::find($params['plan_id']); + if (!$plan) { + return $this->fail([400202, '订阅计划不存在']); + } + $params['group_id'] = $plan->group_id; + } + // 处理邀请用户 + if ($request->input('invite_user_email') && $inviteUser = User::byEmail($request->input('invite_user_email'))->first()) { + $params['invite_user_id'] = $inviteUser->id; + } else { + $params['invite_user_id'] = null; + } + + if (isset($params['banned']) && (int) $params['banned'] === 1) { + $authService = new AuthService($user); + $authService->removeAllSessions(); + } + if (isset($params['balance'])) { + $params['balance'] = $params['balance'] * 100; + } + if (isset($params['commission_balance'])) { + $params['commission_balance'] = $params['commission_balance'] * 100; + } + + try { + $user->update($params); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '保存失败']); + } + return $this->success(true); + } + + // Export users to CSV. + public function dumpCSV(Request $request) + { + ini_set('memory_limit', '-1'); + gc_enable(); // 启用垃圾回收 + + $scopeInfo = $this->resolveScope($request); + $scope = $scopeInfo['scope']; + $userIds = $scopeInfo['user_ids']; + + if ($scope === 'selected') { + if (empty($userIds)) { + return $this->fail([422, 'user_ids不能为空']); + } + } + + // 优化查询:使用with预加载plan关系,避免N+1问题 + $query = User::query() + ->with('plan:id,name') + ->orderBy('id', 'asc') + ->select([ + 'email', + 'balance', + 'commission_balance', + 'transfer_enable', + 'u', + 'd', + 'expired_at', + 'token', + 'plan_id' + ]); + + if ($scope === 'selected') { + $query->whereIn('id', $userIds); + } elseif ($scope === 'filtered') { + $this->applyFiltersAndSorts($request, $query); + } // all: ignore filter/sort + + $filename = 'users_' . date('Y-m-d_His') . '.csv'; + + return response()->streamDownload(function () use ($query) { + // 打开输出流 + $output = fopen('php://output', 'w'); + + // 添加BOM标记,确保Excel正确显示中文 + fprintf($output, chr(0xEF) . chr(0xBB) . chr(0xBF)); + + // 写入CSV头部 + fputcsv($output, [ + '邮箱', + '余额', + '推广佣金', + '总流量', + '剩余流量', + '套餐到期时间', + '订阅计划', + '订阅地址' + ]); + + // 分批处理数据以减少内存使用 + $query->chunk(500, function ($users) use ($output) { + foreach ($users as $user) { + try { + $row = [ + $user->email, + number_format($user->balance / 100, 2), + number_format($user->commission_balance / 100, 2), + Helper::trafficConvert($user->transfer_enable), + Helper::trafficConvert($user->transfer_enable - ($user->u + $user->d)), + $user->expired_at ? date('Y-m-d H:i:s', $user->expired_at) : '长期有效', + $user->plan ? $user->plan->name : '无订阅', + Helper::getSubscribeUrl($user->token) + ]; + fputcsv($output, $row); + } catch (\Exception $e) { + Log::error('CSV导出错误: ' . $e->getMessage(), [ + 'user_id' => $user->id, + 'email' => $user->email + ]); + continue; // 继续处理下一条记录 + } + } + + // 清理内存 + gc_collect_cycles(); + }); + + fclose($output); + }, $filename, [ + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Content-Disposition' => 'attachment; filename="' . $filename . '"' + ]); + } + + public function generate(UserGenerate $request) + { + if ($request->input('email_prefix')) { + // If generate_count is specified with email_prefix, generate multiple users with incremented emails + if ($request->input('generate_count')) { + return $this->multiGenerateWithPrefix($request); + } + + // Single user generation with email_prefix + $email = $request->input('email_prefix') . '@' . $request->input('email_suffix'); + + if (User::byEmail($email)->exists()) { + return $this->fail([400201, '邮箱已存在于系统中']); + } + + $userService = app(UserService::class); + $user = $userService->createUser([ + 'email' => $email, + 'password' => $request->input('password') ?? $email, + 'plan_id' => $request->input('plan_id'), + 'expired_at' => $request->input('expired_at'), + ]); + + if (!$user->save()) { + return $this->fail([500, '生成失败']); + } + return $this->success(true); + } + + if ($request->input('generate_count')) { + return $this->multiGenerate($request); + } + } + + private function multiGenerate(Request $request) + { + $userService = app(UserService::class); + $usersData = []; + + for ($i = 0; $i < $request->input('generate_count'); $i++) { + $email = Helper::randomChar(6) . '@' . $request->input('email_suffix'); + $usersData[] = [ + 'email' => $email, + 'password' => $request->input('password') ?? $email, + 'plan_id' => $request->input('plan_id'), + 'expired_at' => $request->input('expired_at'), + ]; + } + + + + try { + DB::beginTransaction(); + $users = []; + foreach ($usersData as $userData) { + $user = $userService->createUser($userData); + $user->save(); + $users[] = $user; + } + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return $this->fail([500, '生成失败']); + } + + // 判断是否导出 CSV + if ($request->input('download_csv')) { + $headers = [ + 'Content-Type' => 'text/csv', + 'Content-Disposition' => 'attachment; filename="users.csv"', + ]; + $callback = function () use ($users, $request) { + $handle = fopen('php://output', 'w'); + fputcsv($handle, ['账号', '密码', '过期时间', 'UUID', '创建时间', '订阅地址']); + foreach ($users as $user) { + $user = $user->refresh(); + $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']); + $createDate = date('Y-m-d H:i:s', $user['created_at']); + $password = $request->input('password') ?? $user['email']; + $subscribeUrl = Helper::getSubscribeUrl($user['token']); + fputcsv($handle, [$user['email'], $password, $expireDate, $user['uuid'], $createDate, $subscribeUrl]); + } + fclose($handle); + }; + return response()->streamDownload($callback, 'users.csv', $headers); + } + + // 默认返回 JSON + $data = collect($users)->map(function ($user) use ($request) { + return [ + 'email' => $user['email'], + 'password' => $request->input('password') ?? $user['email'], + 'expired_at' => $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']), + 'uuid' => $user['uuid'], + 'created_at' => date('Y-m-d H:i:s', $user['created_at']), + 'subscribe_url' => Helper::getSubscribeUrl($user['token']), + ]; + }); + return response()->json([ + 'code' => 0, + 'message' => '批量生成成功', + 'data' => $data, + ]); + } + + private function multiGenerateWithPrefix(Request $request) + { + $userService = app(UserService::class); + $usersData = []; + $emailPrefix = $request->input('email_prefix'); + $emailSuffix = $request->input('email_suffix'); + $generateCount = $request->input('generate_count'); + + // Check if any of the emails with prefix already exist + for ($i = 1; $i <= $generateCount; $i++) { + $email = $emailPrefix . '_' . $i . '@' . $emailSuffix; + if (User::where('email', $email)->exists()) { + return $this->fail([400201, '邮箱 ' . $email . ' 已存在于系统中']); + } + } + + // Generate user data for batch creation + for ($i = 1; $i <= $generateCount; $i++) { + $email = $emailPrefix . '_' . $i . '@' . $emailSuffix; + $usersData[] = [ + 'email' => $email, + 'password' => $request->input('password') ?? $email, + 'plan_id' => $request->input('plan_id'), + 'expired_at' => $request->input('expired_at'), + ]; + } + + try { + DB::beginTransaction(); + $users = []; + foreach ($usersData as $userData) { + $user = $userService->createUser($userData); + $user->save(); + $users[] = $user; + } + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + return $this->fail([500, '生成失败']); + } + + // 判断是否导出 CSV + if ($request->input('download_csv')) { + $headers = [ + 'Content-Type' => 'text/csv', + 'Content-Disposition' => 'attachment; filename="users.csv"', + ]; + $callback = function () use ($users, $request) { + $handle = fopen('php://output', 'w'); + fputcsv($handle, ['账号', '密码', '过期时间', 'UUID', '创建时间', '订阅地址']); + foreach ($users as $user) { + $user = $user->refresh(); + $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']); + $createDate = date('Y-m-d H:i:s', $user['created_at']); + $password = $request->input('password') ?? $user['email']; + $subscribeUrl = Helper::getSubscribeUrl($user['token']); + fputcsv($handle, [$user['email'], $password, $expireDate, $user['uuid'], $createDate, $subscribeUrl]); + } + fclose($handle); + }; + return response()->streamDownload($callback, 'users.csv', $headers); + } + + // 默认返回 JSON + $data = collect($users)->map(function ($user) use ($request) { + return [ + 'email' => $user['email'], + 'password' => $request->input('password') ?? $user['email'], + 'expired_at' => $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']), + 'uuid' => $user['uuid'], + 'created_at' => date('Y-m-d H:i:s', $user['created_at']), + 'subscribe_url' => Helper::getSubscribeUrl($user['token']), + ]; + }); + return response()->json([ + 'code' => 0, + 'message' => '批量生成成功', + 'data' => $data, + ]); + } + + public function sendMail(UserSendMail $request) + { + ini_set('memory_limit', '-1'); + $scopeInfo = $this->resolveScope($request); + $scope = $scopeInfo['scope']; + $userIds = $scopeInfo['user_ids']; + + if ($scope === 'selected') { + if (empty($userIds)) { + return $this->fail([422, 'user_ids不能为空']); + } + } + + $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC'; + $sort = $request->input('sort') ? $request->input('sort') : 'created_at'; + + $builder = User::query() + ->with('plan:id,name') + ->orderBy('id', 'desc'); + + if ($scope === 'filtered') { + // filtered: apply filters/sort + $builder->orderBy($sort, $sortType); + $this->applyFiltersAndSorts($request, $builder); + } elseif ($scope === 'selected') { + $builder->whereIn('id', $userIds); + } // all: ignore filter/sort + + $subject = $request->input('subject'); + $content = $request->input('content'); + $appName = admin_setting('app_name', 'XBoard'); + $appUrl = admin_setting('app_url'); + + $chunkSize = 1000; + + $builder->chunk($chunkSize, function ($users) use ($subject, $content, $appName, $appUrl) { + foreach ($users as $user) { + $vars = [ + 'app.name' => $appName, + 'app.url' => $appUrl, + 'now' => now()->format('Y-m-d H:i:s'), + 'user.id' => $user->id, + 'user.email' => $user->email, + 'user.uuid' => $user->uuid, + 'user.plan_name' => $user->plan?->name ?? '', + 'user.expired_at' => $user->expired_at ? date('Y-m-d H:i:s', $user->expired_at) : '', + 'user.transfer_enable' => (int) ($user->transfer_enable ?? 0), + 'user.transfer_used' => (int) (($user->u ?? 0) + ($user->d ?? 0)), + 'user.transfer_left' => (int) (($user->transfer_enable ?? 0) - (($user->u ?? 0) + ($user->d ?? 0))), + ]; + + $templateValue = [ + 'name' => $appName, + 'url' => $appUrl, + 'content' => $content, + 'vars' => $vars, + 'content_mode' => 'text', + ]; + + dispatch(new SendEmailJob([ + 'email' => $user->email, + 'subject' => $subject, + 'template_name' => 'notify', + 'template_value' => $templateValue + ], 'send_email_mass')); + } + }); + + return $this->success(true); + } + + public function ban(Request $request) + { + $scopeInfo = $this->resolveScope($request); + $scope = $scopeInfo['scope']; + $userIds = $scopeInfo['user_ids']; + + if ($scope === 'selected') { + if (empty($userIds)) { + return $this->fail([422, 'user_ids不能为空']); + } + } + + $sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC'; + $sort = $request->input('sort') ? $request->input('sort') : 'created_at'; + + $builder = User::query()->orderBy('id', 'desc'); + + if ($scope === 'filtered') { + // filtered: keep current semantics + $builder->orderBy($sort, $sortType); + $this->applyFiltersAndSorts($request, $builder); + } elseif ($scope === 'selected') { + $builder->whereIn('id', $userIds); + } // all: ignore filter/sort + + try { + $builder->update([ + 'banned' => 1 + ]); + } catch (\Exception $e) { + Log::error($e); + return $this->fail([500, '处理失败']); + } + // Full refresh not implemented. + return $this->success(true); + } + + // Delete user and related data. + public function destroy(Request $request) + { + $request->validate([ + 'id' => 'required|exists:App\Models\User,id' + ], [ + 'id.required' => '用户ID不能为空', + 'id.exists' => '用户不存在' + ]); + $user = User::find($request->input('id')); + try { + DB::beginTransaction(); + $user->orders()->delete(); + $user->codes()->delete(); + $user->stat()->delete(); + $user->tickets()->delete(); + $user->delete(); + DB::commit(); + return $this->success(true); + } catch (\Exception $e) { + DB::rollBack(); + Log::error($e); + return $this->fail([500, '删除失败']); + } + } +} diff --git a/Xboard/app/Http/Controllers/V2/Client/AppController.php b/Xboard/app/Http/Controllers/V2/Client/AppController.php new file mode 100644 index 0000000..85ec531 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Client/AppController.php @@ -0,0 +1,153 @@ + [ + 'app_name' => admin_setting('app_name', 'XB加速器'), // 应用名称 + 'app_description' => admin_setting('app_description', '专业的网络加速服务'), // 应用描述 + 'app_url' => admin_setting('app_url', 'https://app.example.com'), // 应用官网 URL + 'logo' => admin_setting('logo', 'https://example.com/logo.png'), // 应用 Logo URL + 'version' => admin_setting('app_version', '1.0.0'), // 应用版本号 + ], + 'features' => [ + 'enable_register' => (bool) admin_setting('app_enable_register', true), // 是否开启注册功能 + 'enable_invite_system' => (bool) admin_setting('app_enable_invite_system', true), // 是否开启邀请系统 + 'enable_telegram_bot' => (bool) admin_setting('telegram_bot_enable', false), // 是否开启 Telegram 机器人 + 'enable_ticket_system' => (bool) admin_setting('app_enable_ticket_system', true), // 是否开启工单系统 + 'ticket_must_wait_reply' => (bool) admin_setting('ticket_must_wait_reply', 0), // 工单是否需要等待管理员回复后才可继续发消息 + 'enable_commission_system' => (bool) admin_setting('app_enable_commission_system', true), // 是否开启佣金系统 + 'enable_traffic_log' => (bool) admin_setting('app_enable_traffic_log', true), // 是否开启流量日志 + 'enable_knowledge_base' => (bool) admin_setting('app_enable_knowledge_base', true), // 是否开启知识库 + 'enable_announcements' => (bool) admin_setting('app_enable_announcements', true), // 是否开启公告系统 + 'enable_auto_renewal' => (bool) admin_setting('app_enable_auto_renewal', false), // 是否开启自动续费 + 'enable_coupon_system' => (bool) admin_setting('app_enable_coupon_system', true), // 是否开启优惠券系统 + 'enable_speed_test' => (bool) admin_setting('app_enable_speed_test', true), // 是否开启测速功能 + 'enable_server_ping' => (bool) admin_setting('app_enable_server_ping', true), // 是否开启服务器延迟检测 + ], + 'ui_config' => [ + 'theme' => [ + 'primary_color' => admin_setting('app_primary_color', '#00C851'), // 主色调 (十六进制) + 'secondary_color' => admin_setting('app_secondary_color', '#007E33'), // 辅助色 (十六进制) + 'accent_color' => admin_setting('app_accent_color', '#FF6B35'), // 强调色 (十六进制) + 'background_color' => admin_setting('app_background_color', '#F5F5F5'), // 背景色 (十六进制) + 'text_color' => admin_setting('app_text_color', '#333333'), // 文字色 (十六进制) + ], + 'home_screen' => [ + 'show_speed_test' => (bool) admin_setting('app_show_speed_test', true), // 是否显示测速 + 'show_traffic_chart' => (bool) admin_setting('app_show_traffic_chart', true), // 是否显示流量图表 + 'show_server_ping' => (bool) admin_setting('app_show_server_ping', true), // 是否显示服务器延迟 + 'default_server_sort' => admin_setting('app_default_server_sort', 'ping'), // 默认服务器排序方式 + 'show_connection_status' => (bool) admin_setting('app_show_connection_status', true), // 是否显示连接状态 + ], + 'server_list' => [ + 'show_country_flags' => (bool) admin_setting('app_show_country_flags', true), // 是否显示国家旗帜 + 'show_ping_values' => (bool) admin_setting('app_show_ping_values', true), // 是否显示延迟值 + 'show_traffic_usage' => (bool) admin_setting('app_show_traffic_usage', true), // 是否显示流量使用 + 'group_by_country' => (bool) admin_setting('app_group_by_country', false), // 是否按国家分组 + 'show_server_status' => (bool) admin_setting('app_show_server_status', true), // 是否显示服务器状态 + ], + ], + 'business_rules' => [ + 'min_password_length' => (int) admin_setting('app_min_password_length', 8), // 最小密码长度 + 'max_login_attempts' => (int) admin_setting('app_max_login_attempts', 5), // 最大登录尝试次数 + 'session_timeout_minutes' => (int) admin_setting('app_session_timeout_minutes', 30), // 会话超时时间(分钟) + 'auto_disconnect_after_minutes' => (int) admin_setting('app_auto_disconnect_after_minutes', 60), // 自动断开连接时间(分钟) + 'max_concurrent_connections' => (int) admin_setting('app_max_concurrent_connections', 3), // 最大并发连接数 + 'traffic_warning_threshold' => (float) admin_setting('app_traffic_warning_threshold', 0.8), // 流量警告阈值(0-1) + 'subscription_reminder_days' => admin_setting('app_subscription_reminder_days', [7, 3, 1]), // 订阅到期提醒天数 + 'connection_timeout_seconds' => (int) admin_setting('app_connection_timeout_seconds', 10), // 连接超时时间(秒) + 'health_check_interval_seconds' => (int) admin_setting('app_health_check_interval_seconds', 30), // 健康检查间隔(秒) + ], + 'server_config' => [ + 'default_kernel' => admin_setting('app_default_kernel', 'clash'), // 默认内核 (clash/singbox) + 'auto_select_fastest' => (bool) admin_setting('app_auto_select_fastest', true), // 是否自动选择最快服务器 + 'fallback_servers' => admin_setting('app_fallback_servers', ['server1', 'server2']), // 备用服务器列表 + 'enable_auto_switch' => (bool) admin_setting('app_enable_auto_switch', true), // 是否开启自动切换 + 'switch_threshold_ms' => (int) admin_setting('app_switch_threshold_ms', 1000), // 切换阈值(毫秒) + ], + 'security_config' => [ + 'tos_url' => admin_setting('tos_url', 'https://example.com/tos'), // 服务条款 URL + 'privacy_policy_url' => admin_setting('app_privacy_policy_url', 'https://example.com/privacy'), // 隐私政策 URL + 'is_email_verify' => (int) admin_setting('email_verify', 1), // 是否开启邮箱验证 (0/1) + 'is_invite_force' => (int) admin_setting('invite_force', 0), // 是否强制邀请码 (0/1) + 'email_whitelist_suffix' => (int) admin_setting('email_whitelist_suffix', 0), // 邮箱白名单后缀 (0/1) + 'is_captcha' => (int) admin_setting('captcha_enable', 1), // 是否开启验证码 (0/1) + 'captcha_type' => admin_setting('captcha_type', 'recaptcha'), // 验证码类型 (recaptcha/turnstile) + 'recaptcha_site_key' => admin_setting('recaptcha_site_key', '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'), // reCAPTCHA 站点密钥 + 'recaptcha_v3_site_key' => admin_setting('recaptcha_v3_site_key', '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'), // reCAPTCHA v3 站点密钥 + 'recaptcha_v3_score_threshold' => (float) admin_setting('recaptcha_v3_score_threshold', 0.5), // reCAPTCHA v3 分数阈值 + 'turnstile_site_key' => admin_setting('turnstile_site_key', '0x4AAAAAAAABkMYinukE8nzUg'), // Turnstile 站点密钥 + ], + 'payment_config' => [ + 'currency' => admin_setting('currency', 'CNY'), // 货币类型 + 'currency_symbol' => admin_setting('currency_symbol', '¥'), // 货币符号 + 'withdraw_methods' => admin_setting('app_withdraw_methods', ['alipay', 'wechat', 'bank']), // 提现方式列表 + 'min_withdraw_amount' => (int) admin_setting('app_min_withdraw_amount', 100), // 最小提现金额(分) + 'withdraw_fee_rate' => (float) admin_setting('app_withdraw_fee_rate', 0.01), // 提现手续费率 + ], + 'notification_config' => [ + 'enable_push_notifications' => (bool) admin_setting('app_enable_push_notifications', true), // 是否开启推送通知 + 'enable_email_notifications' => (bool) admin_setting('app_enable_email_notifications', true), // 是否开启邮件通知 + 'enable_sms_notifications' => (bool) admin_setting('app_enable_sms_notifications', false), // 是否开启短信通知 + 'notification_schedule' => [ + 'traffic_warning' => (bool) admin_setting('app_notification_traffic_warning', true), // 流量警告通知 + 'subscription_expiry' => (bool) admin_setting('app_notification_subscription_expiry', true), // 订阅到期通知 + 'server_maintenance' => (bool) admin_setting('app_notification_server_maintenance', true), // 服务器维护通知 + 'promotional_offers' => (bool) admin_setting('app_notification_promotional_offers', false), // 促销优惠通知 + ], + ], + 'cache_config' => [ + 'config_cache_duration' => (int) admin_setting('app_config_cache_duration', 3600), // 配置缓存时长(秒) + 'server_list_cache_duration' => (int) admin_setting('app_server_list_cache_duration', 1800), // 服务器列表缓存时长(秒) + 'user_info_cache_duration' => (int) admin_setting('app_user_info_cache_duration', 900), // 用户信息缓存时长(秒) + ], + 'last_updated' => time(), // 最后更新时间戳 + ]; + $config['config_hash'] = md5(json_encode($config)); // 配置哈希值(用于校验) + + $config = $config ?? []; + return response()->json(['data' => $config]); + } + + public function getVersion(Request $request) + { + if ( + strpos($request->header('user-agent'), 'tidalab/4.0.0') !== false + || strpos($request->header('user-agent'), 'tunnelab/4.0.0') !== false + ) { + if (strpos($request->header('user-agent'), 'Win64') !== false) { + $data = [ + 'version' => admin_setting('windows_version'), + 'download_url' => admin_setting('windows_download_url') + ]; + } else { + $data = [ + 'version' => admin_setting('macos_version'), + 'download_url' => admin_setting('macos_download_url') + ]; + } + } else { + $data = [ + 'windows_version' => admin_setting('windows_version'), + 'windows_download_url' => admin_setting('windows_download_url'), + 'macos_version' => admin_setting('macos_version'), + 'macos_download_url' => admin_setting('macos_download_url'), + 'android_version' => admin_setting('android_version'), + 'android_download_url' => admin_setting('android_download_url') + ]; + } + return $this->success($data); + } +} diff --git a/Xboard/app/Http/Controllers/V2/Server/ServerController.php b/Xboard/app/Http/Controllers/V2/Server/ServerController.php new file mode 100644 index 0000000..ff58951 --- /dev/null +++ b/Xboard/app/Http/Controllers/V2/Server/ServerController.php @@ -0,0 +1,138 @@ + false]; + + if ((bool) admin_setting('server_ws_enable', 1)) { + $customUrl = trim((string) admin_setting('server_ws_url', '')); + + if ($customUrl !== '') { + $wsUrl = rtrim($customUrl, '/'); + } else { + $wsScheme = $request->isSecure() ? 'wss' : 'ws'; + $wsUrl = "{$wsScheme}://{$request->getHost()}:8076"; + } + + $websocket = [ + 'enabled' => true, + 'ws_url' => $wsUrl, + ]; + } + + return response()->json([ + 'websocket' => $websocket + ]); + } + + /** + * node report api - merge traffic + alive + status + * POST /api/v2/server/node/report + */ + public function report(Request $request): JsonResponse + { + $node = $request->attributes->get('node_info'); + $nodeType = $node->type; + $nodeId = $node->id; + + Cache::put(CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_CHECK_AT', $nodeId), time(), 3600); + + // hanle traffic data + $traffic = $request->input('traffic'); + if (is_array($traffic) && !empty($traffic)) { + $data = array_filter($traffic, function ($item) { + return is_array($item) + && count($item) === 2 + && is_numeric($item[0]) + && is_numeric($item[1]); + }); + + if (!empty($data)) { + Cache::put( + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_ONLINE_USER', $nodeId), + count($data), + 3600 + ); + Cache::put( + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_PUSH_AT', $nodeId), + time(), + 3600 + ); + $userService = new UserService(); + $userService->trafficFetch($node, $nodeType, $data); + } + } + + // handle alive data + $alive = $request->input('alive'); + if (is_array($alive) && !empty($alive)) { + $deviceStateService = app(DeviceStateService::class); + foreach ($alive as $uid => $ips) { + $deviceStateService->setDevices((int) $uid, $nodeId, (array) $ips); + } + } + + // handle active connections + $online = $request->input('online'); + if (is_array($online) && !empty($online)) { + $cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3); + foreach ($online as $uid => $conn) { + $cacheKey = CacheKey::get("USER_ONLINE_CONN_{$nodeType}_{$nodeId}", $uid); + Cache::put($cacheKey, (int) $conn, $cacheTime); + } + } + + // handle node status + $status = $request->input('status'); + if (is_array($status) && !empty($status)) { + $statusData = [ + 'cpu' => (float) ($status['cpu'] ?? 0), + 'mem' => [ + 'total' => (int) ($status['mem']['total'] ?? 0), + 'used' => (int) ($status['mem']['used'] ?? 0), + ], + 'swap' => [ + 'total' => (int) ($status['swap']['total'] ?? 0), + 'used' => (int) ($status['swap']['used'] ?? 0), + ], + 'disk' => [ + 'total' => (int) ($status['disk']['total'] ?? 0), + 'used' => (int) ($status['disk']['used'] ?? 0), + ], + 'updated_at' => now()->timestamp, + 'kernel_status' => $status['kernel_status'] ?? null, + ]; + + $cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3); + cache([ + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LOAD_STATUS', $nodeId) => $statusData, + CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_LOAD_AT', $nodeId) => now()->timestamp, + ], $cacheTime); + } + + // handle node metrics (Metrics) + $metrics = $request->input('metrics'); + if (is_array($metrics) && !empty($metrics)) { + ServerService::updateMetrics($node, $metrics); + } + + return response()->json(['data' => true]); + } +} diff --git a/Xboard/app/Http/Kernel.php b/Xboard/app/Http/Kernel.php new file mode 100644 index 0000000..6fd86ab --- /dev/null +++ b/Xboard/app/Http/Kernel.php @@ -0,0 +1,99 @@ + + */ + protected $middleware = [ + \Illuminate\Http\Middleware\HandleCors::class, + \App\Http\Middleware\TrustProxies::class, + \App\Http\Middleware\CheckForMaintenanceMode::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + \App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + \App\Http\Middleware\InitializePlugins::class, + ]; + + /** + * The application's route middleware groups. + * + * @var array> + */ + protected $middlewareGroups = [ + 'web' => [ + // \App\Http\Middleware\EncryptCookies::class, +// \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, +// \Illuminate\Session\Middleware\StartSession::class, + // \Illuminate\Session\Middleware\AuthenticateSession::class, +// \Illuminate\View\Middleware\ShareErrorsFromSession::class, +// \App\Http\Middleware\VerifyCsrfToken::class, +// \Illuminate\Routing\Middleware\SubstituteBindings::class, + \App\Http\Middleware\ApplyRuntimeSettings::class, + ], + + 'api' => [ + // \App\Http\Middleware\EncryptCookies::class, +// \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, +// \Illuminate\Session\Middleware\StartSession::class, + // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + // \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api', + // \Illuminate\Routing\Middleware\SubstituteBindings::class, + \App\Http\Middleware\ApplyRuntimeSettings::class, + \App\Http\Middleware\ForceJson::class, + \App\Http\Middleware\Language::class, + 'bindings', + ], + ]; + + /** + * The application's route middleware. + * + * These middleware may be assigned to groups or used individually. + * + * @var array + */ + protected $middlewareAliases = [ + 'auth' => \App\Http\Middleware\Authenticate::class, + 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, + 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, + 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, + 'can' => \Illuminate\Auth\Middleware\Authorize::class, + 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, + 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'user' => \App\Http\Middleware\User::class, + 'admin' => \App\Http\Middleware\Admin::class, + 'client' => \App\Http\Middleware\Client::class, + 'staff' => \App\Http\Middleware\Staff::class, + 'log' => \App\Http\Middleware\RequestLog::class, + 'server' => \App\Http\Middleware\Server::class, + 'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class, + 'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class, + ]; + + /** + * The priority-sorted list of middleware. + * + * This forces non-global middleware to always be in the given order. + * + * @var array + */ + protected $middlewarePriority = [ + \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\View\Middleware\ShareErrorsFromSession::class, + \Illuminate\Routing\Middleware\ThrottleRequests::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, + \Illuminate\Routing\Middleware\SubstituteBindings::class, + \Illuminate\Auth\Middleware\Authorize::class, + ]; +} diff --git a/Xboard/app/Http/Middleware/Admin.php b/Xboard/app/Http/Middleware/Admin.php new file mode 100644 index 0000000..1a63b4f --- /dev/null +++ b/Xboard/app/Http/Middleware/Admin.php @@ -0,0 +1,30 @@ +user(); + + if (!$user || !$user->is_admin) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + return $next($request); + } +} diff --git a/Xboard/app/Http/Middleware/ApplyRuntimeSettings.php b/Xboard/app/Http/Middleware/ApplyRuntimeSettings.php new file mode 100644 index 0000000..2c3e96a --- /dev/null +++ b/Xboard/app/Http/Middleware/ApplyRuntimeSettings.php @@ -0,0 +1,25 @@ +expectsJson() ? null : null; + } +} diff --git a/Xboard/app/Http/Middleware/CheckForMaintenanceMode.php b/Xboard/app/Http/Middleware/CheckForMaintenanceMode.php new file mode 100644 index 0000000..53fcdd5 --- /dev/null +++ b/Xboard/app/Http/Middleware/CheckForMaintenanceMode.php @@ -0,0 +1,18 @@ + + */ + protected $except = [ + // 示例: + // '/api/health-check', + // '/status' + ]; +} diff --git a/Xboard/app/Http/Middleware/Client.php b/Xboard/app/Http/Middleware/Client.php new file mode 100644 index 0000000..77645e0 --- /dev/null +++ b/Xboard/app/Http/Middleware/Client.php @@ -0,0 +1,33 @@ +input('token', $request->route('token')); + if (empty($token)) { + throw new ApiException('token is null',403); + } + $user = User::where('token', $token)->first(); + if (!$user) { + throw new ApiException('token is error',403); + } + + Auth::setUser($user); + return $next($request); + } +} diff --git a/Xboard/app/Http/Middleware/EncryptCookies.php b/Xboard/app/Http/Middleware/EncryptCookies.php new file mode 100644 index 0000000..31e9d1a --- /dev/null +++ b/Xboard/app/Http/Middleware/EncryptCookies.php @@ -0,0 +1,16 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/Xboard/app/Http/Middleware/EnsureTransactionState.php b/Xboard/app/Http/Middleware/EnsureTransactionState.php new file mode 100644 index 0000000..595dc50 --- /dev/null +++ b/Xboard/app/Http/Middleware/EnsureTransactionState.php @@ -0,0 +1,29 @@ + 0) { + DB::rollBack(); + } + } + } +} diff --git a/Xboard/app/Http/Middleware/ForceJson.php b/Xboard/app/Http/Middleware/ForceJson.php new file mode 100644 index 0000000..ef87160 --- /dev/null +++ b/Xboard/app/Http/Middleware/ForceJson.php @@ -0,0 +1,22 @@ +headers->set('accept', 'application/json'); + return $next($request); + } +} diff --git a/Xboard/app/Http/Middleware/InitializePlugins.php b/Xboard/app/Http/Middleware/InitializePlugins.php new file mode 100644 index 0000000..0c5ae8d --- /dev/null +++ b/Xboard/app/Http/Middleware/InitializePlugins.php @@ -0,0 +1,37 @@ +pluginManager = $pluginManager; + } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ + public function handle(Request $request, Closure $next) + { + // This single method call handles loading and booting all enabled plugins. + // It's safe to call multiple times, as it will only run once per request. + $this->pluginManager->initializeEnabledPlugins(); + + return $next($request); + } +} \ No newline at end of file diff --git a/Xboard/app/Http/Middleware/Language.php b/Xboard/app/Http/Middleware/Language.php new file mode 100644 index 0000000..8bb51e7 --- /dev/null +++ b/Xboard/app/Http/Middleware/Language.php @@ -0,0 +1,17 @@ +header('content-language')) { + App::setLocale($request->header('content-language')); + } + return $next($request); + } +} diff --git a/Xboard/app/Http/Middleware/RedirectIfAuthenticated.php b/Xboard/app/Http/Middleware/RedirectIfAuthenticated.php new file mode 100644 index 0000000..a7ef27c --- /dev/null +++ b/Xboard/app/Http/Middleware/RedirectIfAuthenticated.php @@ -0,0 +1,26 @@ +check()) { + return redirect('/home'); + } + + return $next($request); + } +} diff --git a/Xboard/app/Http/Middleware/RequestLog.php b/Xboard/app/Http/Middleware/RequestLog.php new file mode 100644 index 0000000..62dded3 --- /dev/null +++ b/Xboard/app/Http/Middleware/RequestLog.php @@ -0,0 +1,60 @@ +method() !== 'POST') { + return $next($request); + } + + $response = $next($request); + + try { + $admin = $request->user(); + if (!$admin || !$admin->is_admin) { + return $response; + } + + $action = $this->resolveAction($request->path()); + $data = collect($request->all())->except(self::SENSITIVE_KEYS)->toArray(); + + AdminAuditLog::insert([ + 'admin_id' => $admin->id, + 'action' => $action, + 'method' => $request->method(), + 'uri' => $request->getRequestUri(), + 'request_data' => json_encode($data, JSON_UNESCAPED_UNICODE), + 'ip' => $request->getClientIp(), + 'created_at' => time(), + 'updated_at' => time(), + ]); + } catch (\Throwable $e) { + \Log::warning('Audit log write failed: ' . $e->getMessage()); + } + + return $response; + } + + private function resolveAction(string $path): string + { + // api/v2/{secure_path}/user/update → user.update + $path = preg_replace('#^api/v[12]/[^/]+/#', '', $path); + // gift-card/create-template → gift_card.create_template + $path = str_replace('-', '_', $path); + // user/update → user.update, server/manage/sort → server_manage.sort + $segments = explode('/', $path); + $method = array_pop($segments); + $resource = implode('_', $segments); + + return $resource . '.' . $method; + } +} + diff --git a/Xboard/app/Http/Middleware/Server.php b/Xboard/app/Http/Middleware/Server.php new file mode 100644 index 0000000..15dd494 --- /dev/null +++ b/Xboard/app/Http/Middleware/Server.php @@ -0,0 +1,59 @@ +validateRequest($request); + $nodeType = $request->input('node_type', $nodeType); + $normalizedNodeType = ServerModel::normalizeType($nodeType); + $serverInfo = ServerService::getServer( + $request->input('node_id'), + $normalizedNodeType + ); + if (!$serverInfo) { + throw new ApiException('Server does not exist'); + } + + $request->attributes->set('node_info', $serverInfo); + return $next($request); + } + + private function validateRequest(Request $request): void + { + $request->validate([ + 'token' => [ + 'string', + 'required', + function ($attribute, $value, $fail) { + if ($value !== admin_setting('server_token')) { + $fail("Invalid {$attribute}"); + } + }, + ], + 'node_id' => 'required', + 'node_type' => [ + 'nullable', + function ($attribute, $value, $fail) use ($request) { + if ($value === "v2node") { + $value = null; + } + if (!ServerModel::isValidType($value)) { + $fail("Invalid node type specified"); + return; + } + $request->merge([$attribute => ServerModel::normalizeType($value)]); + }, + ] + ]); + } +} diff --git a/Xboard/app/Http/Middleware/Staff.php b/Xboard/app/Http/Middleware/Staff.php new file mode 100644 index 0000000..5c700c1 --- /dev/null +++ b/Xboard/app/Http/Middleware/Staff.php @@ -0,0 +1,30 @@ +input('auth_data') ?? $request->header('authorization'); + if (!$authorization) throw new ApiException( '未登录或登陆已过期', 403); + + $user = AuthService::decryptAuthData($authorization); + if (!$user || !$user['is_staff']) throw new ApiException('未登录或登陆已过期', 403); + $request->merge([ + 'user' => $user + ]); + return $next($request); + } +} diff --git a/Xboard/app/Http/Middleware/TrimStrings.php b/Xboard/app/Http/Middleware/TrimStrings.php new file mode 100644 index 0000000..fb507a4 --- /dev/null +++ b/Xboard/app/Http/Middleware/TrimStrings.php @@ -0,0 +1,19 @@ + + */ + protected $except = [ + 'password', + 'password_confirmation', + 'encrypted_data', + 'signature' + ]; +} diff --git a/Xboard/app/Http/Middleware/TrustProxies.php b/Xboard/app/Http/Middleware/TrustProxies.php new file mode 100644 index 0000000..83c5400 --- /dev/null +++ b/Xboard/app/Http/Middleware/TrustProxies.php @@ -0,0 +1,47 @@ +|string|null + */ + protected $proxies = [ + "173.245.48.0/20", + "103.21.244.0/22", + "103.22.200.0/22", + "103.31.4.0/22", + "141.101.64.0/18", + "108.162.192.0/18", + "190.93.240.0/20", + "188.114.96.0/20", + "197.234.240.0/22", + "198.41.128.0/17", + "162.158.0.0/15", + "104.16.0.0/13", + "104.24.0.0/14", + "172.64.0.0/13", + "131.0.72.0/22", + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "169.254.0.0/16", + "127.0.0.0/8", + ]; + + /** + * 代理头映射 + * @var int + */ + protected $headers = + Request::HEADER_X_FORWARDED_FOR | + Request::HEADER_X_FORWARDED_HOST | + Request::HEADER_X_FORWARDED_PORT | + Request::HEADER_X_FORWARDED_PROTO | + Request::HEADER_X_FORWARDED_AWS_ELB; +} diff --git a/Xboard/app/Http/Middleware/User.php b/Xboard/app/Http/Middleware/User.php new file mode 100644 index 0000000..14f3049 --- /dev/null +++ b/Xboard/app/Http/Middleware/User.php @@ -0,0 +1,27 @@ +check()) { + throw new ApiException('未登录或登陆已过期', 403); + } + return $next($request); + } +} diff --git a/Xboard/app/Http/Middleware/VerifyCsrfToken.php b/Xboard/app/Http/Middleware/VerifyCsrfToken.php new file mode 100644 index 0000000..9e7c0bd --- /dev/null +++ b/Xboard/app/Http/Middleware/VerifyCsrfToken.php @@ -0,0 +1,22 @@ + + */ + protected $except = [ + // + ]; +} diff --git a/Xboard/app/Http/Requests/Admin/ConfigSave.php b/Xboard/app/Http/Requests/Admin/ConfigSave.php new file mode 100644 index 0000000..bec69a6 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/ConfigSave.php @@ -0,0 +1,146 @@ + '', + 'invite_commission' => 'integer|nullable', + 'invite_gen_limit' => 'integer|nullable', + 'invite_never_expire' => '', + 'commission_first_time_enable' => '', + 'commission_auto_check_enable' => '', + 'commission_withdraw_limit' => 'nullable|numeric', + 'commission_withdraw_method' => 'nullable|array', + 'withdraw_close_enable' => '', + 'commission_distribution_enable' => '', + 'commission_distribution_l1' => 'nullable|numeric', + 'commission_distribution_l2' => 'nullable|numeric', + 'commission_distribution_l3' => 'nullable|numeric', + // site + 'logo' => 'nullable|url', + 'force_https' => '', + 'stop_register' => '', + 'app_name' => '', + 'app_description' => '', + 'app_url' => 'nullable|url', + 'subscribe_url' => 'nullable', + 'try_out_enable' => '', + 'try_out_plan_id' => 'integer', + 'try_out_hour' => 'numeric', + 'tos_url' => 'nullable|url', + 'currency' => '', + 'currency_symbol' => '', + 'ticket_must_wait_reply' => '', + // subscribe + 'plan_change_enable' => '', + 'reset_traffic_method' => 'in:0,1,2,3,4', + 'surplus_enable' => '', + 'new_order_event_id' => '', + 'renew_order_event_id' => '', + 'change_order_event_id' => '', + 'show_info_to_server_enable' => '', + 'show_protocol_to_server_enable' => '', + 'subscribe_path' => '', + // server + 'server_token' => 'nullable|min:16', + 'server_pull_interval' => 'integer', + 'server_push_interval' => 'integer', + 'device_limit_mode' => 'integer', + 'server_ws_enable' => 'boolean', + 'server_ws_url' => 'nullable|url', + // frontend + 'frontend_theme' => '', + 'frontend_theme_sidebar' => 'nullable|in:dark,light', + 'frontend_theme_header' => 'nullable|in:dark,light', + 'frontend_theme_color' => 'nullable|in:default,darkblue,black,green', + 'frontend_background_url' => 'nullable|url', + // email + 'email_template' => '', + 'email_host' => '', + 'email_port' => '', + 'email_username' => '', + 'email_password' => '', + 'email_encryption' => '', + 'email_from_address' => '', + 'remind_mail_enable' => '', + // telegram + 'telegram_bot_enable' => '', + 'telegram_bot_token' => '', + 'telegram_webhook_url' => 'nullable|url', + 'telegram_discuss_id' => '', + 'telegram_channel_id' => '', + 'telegram_discuss_link' => 'nullable|url', + // app + 'windows_version' => '', + 'windows_download_url' => '', + 'macos_version' => '', + 'macos_download_url' => '', + 'android_version' => '', + 'android_download_url' => '', + // safe + 'email_whitelist_enable' => 'boolean', + 'email_whitelist_suffix' => 'nullable|array', + 'email_gmail_limit_enable' => 'boolean', + 'captcha_enable' => 'boolean', + 'captcha_type' => 'in:recaptcha,turnstile,recaptcha-v3', + 'recaptcha_enable' => 'boolean', + 'recaptcha_key' => '', + 'recaptcha_site_key' => '', + 'recaptcha_v3_secret_key' => '', + 'recaptcha_v3_site_key' => '', + 'recaptcha_v3_score_threshold' => 'numeric|min:0|max:1', + 'turnstile_secret_key' => '', + 'turnstile_site_key' => '', + 'email_verify' => 'bool', + 'safe_mode_enable' => 'boolean', + 'register_limit_by_ip_enable' => 'boolean', + 'register_limit_count' => 'integer', + 'register_limit_expire' => 'integer', + 'secure_path' => 'min:8|regex:/^[\w-]*$/', + 'password_limit_enable' => 'boolean', + 'password_limit_count' => 'integer', + 'password_limit_expire' => 'integer', + 'default_remind_expire' => 'boolean', + 'default_remind_traffic' => 'boolean', + 'subscribe_template_singbox' => 'nullable', + 'subscribe_template_clash' => 'nullable', + 'subscribe_template_clashmeta' => 'nullable', + 'subscribe_template_stash' => 'nullable', + 'subscribe_template_surge' => 'nullable', + 'subscribe_template_surfboard' => 'nullable' + ]; + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return self::RULES; + } + + public function messages() + { + // illiteracy prompt + return [ + 'app_url.url' => '站点URL格式不正确,必须携带http(s)://', + 'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://', + 'server_token.min' => '通讯密钥长度必须大于16位', + 'tos_url.url' => '服务条款URL格式不正确,必须携带http(s)://', + 'telegram_webhook_url.url' => 'Telegram Webhook地址格式不正确,必须携带http(s)://', + 'telegram_discuss_link.url' => 'Telegram群组地址必须为URL格式,必须携带http(s)://', + 'logo.url' => 'LOGO URL格式不正确,必须携带https(s)://', + 'secure_path.min' => '后台路径长度最小为8位', + 'secure_path.regex' => '后台路径只能为字母或数字', + 'captcha_type.in' => '人机验证类型只能选择 recaptcha、turnstile 或 recaptcha-v3', + 'recaptcha_v3_score_threshold.numeric' => 'reCAPTCHA v3 分数阈值必须为数字', + 'recaptcha_v3_score_threshold.min' => 'reCAPTCHA v3 分数阈值不能小于0', + 'recaptcha_v3_score_threshold.max' => 'reCAPTCHA v3 分数阈值不能大于1' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/CouponGenerate.php b/Xboard/app/Http/Requests/Admin/CouponGenerate.php new file mode 100644 index 0000000..70f3efc --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/CouponGenerate.php @@ -0,0 +1,51 @@ + 'nullable|integer|max:500', + 'name' => 'required', + 'type' => 'required|in:1,2', + 'value' => 'required|integer', + 'started_at' => 'required|integer', + 'ended_at' => 'required|integer', + 'limit_use' => 'nullable|integer', + 'limit_use_with_user' => 'nullable|integer', + 'limit_plan_ids' => 'nullable|array', + 'limit_period' => 'nullable|array', + 'code' => '' + ]; + } + + public function messages() + { + return [ + 'generate_count.integer' => '生成数量必须为数字', + 'generate_count.max' => '生成数量最大为500个', + 'name.required' => '名称不能为空', + 'type.required' => '类型不能为空', + 'type.in' => '类型格式有误', + 'value.required' => '金额或比例不能为空', + 'value.integer' => '金额或比例格式有误', + 'started_at.required' => '开始时间不能为空', + 'started_at.integer' => '开始时间格式有误', + 'ended_at.required' => '结束时间不能为空', + 'ended_at.integer' => '结束时间格式有误', + 'limit_use.integer' => '最大使用次数格式有误', + 'limit_use_with_user.integer' => '限制用户使用次数格式有误', + 'limit_plan_ids.array' => '指定订阅格式有误', + 'limit_period.array' => '指定周期格式有误' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/KnowledgeCategorySave.php b/Xboard/app/Http/Requests/Admin/KnowledgeCategorySave.php new file mode 100644 index 0000000..9aabb7e --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/KnowledgeCategorySave.php @@ -0,0 +1,29 @@ + 'required', + 'language' => 'required' + ]; + } + + public function messages() + { + return [ + 'name.required' => '分类名称不能为空', + 'language.required' => '分类语言不能为空' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/KnowledgeCategorySort.php b/Xboard/app/Http/Requests/Admin/KnowledgeCategorySort.php new file mode 100644 index 0000000..c76f810 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/KnowledgeCategorySort.php @@ -0,0 +1,28 @@ + 'required|array' + ]; + } + + public function messages() + { + return [ + 'knowledge_category_ids.required' => '分类不能为空', + 'knowledge_category_ids.array' => '分类格式有误' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/KnowledgeSave.php b/Xboard/app/Http/Requests/Admin/KnowledgeSave.php new file mode 100644 index 0000000..296ddbb --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/KnowledgeSave.php @@ -0,0 +1,35 @@ + 'required', + 'language' => 'required', + 'title' => 'required', + 'body' => 'required', + 'show' => 'nullable|boolean' + ]; + } + + public function messages() + { + return [ + 'title.required' => '标题不能为空', + 'category.required' => '分类不能为空', + 'body.required' => '内容不能为空', + 'language.required' => '语言不能为空', + 'show.boolean' => '显示状态必须为布尔值' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/KnowledgeSort.php b/Xboard/app/Http/Requests/Admin/KnowledgeSort.php new file mode 100644 index 0000000..d29a899 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/KnowledgeSort.php @@ -0,0 +1,28 @@ + 'required|array' + ]; + } + + public function messages() + { + return [ + 'knowledge_ids.required' => '知识ID不能为空', + 'knowledge_ids.array' => '知识ID格式有误' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/MailSend.php b/Xboard/app/Http/Requests/Admin/MailSend.php new file mode 100644 index 0000000..86247a3 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/MailSend.php @@ -0,0 +1,34 @@ + 'required|in:1,2,3,4', + 'subject' => 'required', + 'content' => 'required', + 'receiver' => 'array' + ]; + } + + public function messages() + { + return [ + 'type.required' => '发送类型不能为空', + 'type.in' => '发送类型格式有误', + 'subject.required' => '主题不能为空', + 'content.required' => '内容不能为空', + 'receiver.array' => '收件人格式有误' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/NoticeSave.php b/Xboard/app/Http/Requests/Admin/NoticeSave.php new file mode 100644 index 0000000..0f6dc0b --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/NoticeSave.php @@ -0,0 +1,33 @@ + 'required', + 'content' => 'required', + 'img_url' => 'nullable|url', + 'tags' => 'nullable|array' + ]; + } + + public function messages() + { + return [ + 'title.required' => '标题不能为空', + 'content.required' => '内容不能为空', + 'img_url.url' => '图片URL格式不正确', + 'tags.array' => '标签格式不正确' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/OrderAssign.php b/Xboard/app/Http/Requests/Admin/OrderAssign.php new file mode 100644 index 0000000..4b259a0 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/OrderAssign.php @@ -0,0 +1,34 @@ + 'required', + 'email' => 'required', + 'total_amount' => 'required', + 'period' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price' + ]; + } + + public function messages() + { + return [ + 'plan_id.required' => '订阅不能为空', + 'email.required' => '邮箱不能为空', + 'total_amount.required' => '支付金额不能为空', + 'period.required' => '订阅周期不能为空', + 'period.in' => '订阅周期格式有误' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/OrderFetch.php b/Xboard/app/Http/Requests/Admin/OrderFetch.php new file mode 100644 index 0000000..9c4765b --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/OrderFetch.php @@ -0,0 +1,32 @@ + 'required|in:email,trade_no,status,commission_status,user_id,invite_user_id,callback_no,commission_balance', + 'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=', + 'filter.*.value' => '' + ]; + } + + public function messages() + { + return [ + 'filter.*.key.required' => '过滤键不能为空', + 'filter.*.key.in' => '过滤键参数有误', + 'filter.*.condition.required' => '过滤条件不能为空', + 'filter.*.condition.in' => '过滤条件参数有误', + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/OrderUpdate.php b/Xboard/app/Http/Requests/Admin/OrderUpdate.php new file mode 100644 index 0000000..8a38d10 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/OrderUpdate.php @@ -0,0 +1,29 @@ + 'in:0,1,2,3', + 'commission_status' => 'in:0,1,3' + ]; + } + + public function messages() + { + return [ + 'status.in' => '销售状态格式不正确', + 'commission_status.in' => '佣金状态格式不正确' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/PlanSave.php b/Xboard/app/Http/Requests/Admin/PlanSave.php new file mode 100644 index 0000000..c35e4a7 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/PlanSave.php @@ -0,0 +1,157 @@ + 'nullable|integer', + 'name' => 'required|string|max:255', + 'content' => 'nullable|string', + 'reset_traffic_method' => 'integer|nullable', + 'transfer_enable' => 'integer|required|min:1', + 'prices' => 'nullable|array', + 'prices.*' => 'nullable|numeric|min:0', + 'group_id' => 'integer|nullable', + 'speed_limit' => 'integer|nullable|min:0', + 'device_limit' => 'integer|nullable|min:0', + 'capacity_limit' => 'integer|nullable|min:0', + 'tags' => 'array|nullable', + ]; + } + + /** + * Configure the validator instance. + */ + public function withValidator(Validator $validator): void + { + $validator->after(function (Validator $validator) { + $this->validatePrices($validator); + }); + } + + /** + * 验证价格配置 + */ + protected function validatePrices(Validator $validator): void + { + $prices = $this->input('prices', []); + + if (empty($prices)) { + return; + } + + // 获取所有有效的周期 + $validPeriods = array_keys(Plan::getAvailablePeriods()); + + foreach ($prices as $period => $price) { + // 验证周期是否有效 + if (!in_array($period, $validPeriods)) { + $validator->errors()->add( + "prices.{$period}", + "不支持的订阅周期: {$period}" + ); + continue; + } + + // 价格可以为 null、空字符串或大于 0 的数字 + if ($price !== null && $price !== '') { + // 转换为数字进行验证 + $numericPrice = is_numeric($price) ? (float) $price : null; + + if ($numericPrice === null) { + $validator->errors()->add( + "prices.{$period}", + "价格必须是数字格式" + ); + } elseif ($numericPrice < 0) { + $validator->errors()->add( + "prices.{$period}", + "价格必须大于等于 0(如不需要此周期请留空)" + ); + } + } + } + } + + /** + * 处理验证后的数据 + */ + protected function passedValidation(): void + { + // 清理和格式化价格数据 + $prices = $this->input('prices', []); + $cleanedPrices = []; + + foreach ($prices as $period => $price) { + // 只保留有效的正数价格 + if ($price !== null && $price !== '' && is_numeric($price)) { + $numericPrice = (float) $price; + if ($numericPrice > 0) { + // 转换为浮点数并保留两位小数 + $cleanedPrices[$period] = round($numericPrice, 2); + } + } + } + + // 更新请求中的价格数据 + $this->merge(['prices' => $cleanedPrices]); + } + + /** + * Get custom error messages for validator errors. + */ + public function messages(): array + { + return [ + 'name.required' => '套餐名称不能为空', + 'name.max' => '套餐名称不能超过 255 个字符', + 'transfer_enable.required' => '流量配额不能为空', + 'transfer_enable.integer' => '流量配额必须是整数', + 'transfer_enable.min' => '流量配额必须大于 0', + 'prices.array' => '价格配置格式错误', + 'prices.*.numeric' => '价格必须是数字', + 'prices.*.min' => '价格不能为负数', + 'group_id.integer' => '权限组ID必须是整数', + 'speed_limit.integer' => '速度限制必须是整数', + 'speed_limit.min' => '速度限制不能为负数', + 'device_limit.integer' => '设备限制必须是整数', + 'device_limit.min' => '设备限制不能为负数', + 'capacity_limit.integer' => '容量限制必须是整数', + 'capacity_limit.min' => '容量限制不能为负数', + 'tags.array' => '标签格式必须是数组', + ]; + } + + /** + * Handle a failed validation attempt. + */ + protected function failedValidation(Validator $validator): void + { + throw new HttpResponseException( + response()->json([ + 'data' => false, + 'message' => $validator->errors()->first(), + 'errors' => $validator->errors()->toArray() + ], 422) + ); + } +} diff --git a/Xboard/app/Http/Requests/Admin/PlanSort.php b/Xboard/app/Http/Requests/Admin/PlanSort.php new file mode 100644 index 0000000..eb7987a --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/PlanSort.php @@ -0,0 +1,28 @@ + 'required|array' + ]; + } + + public function messages() + { + return [ + 'plan_ids.required' => '订阅计划ID不能为空', + 'plan_ids.array' => '订阅计划ID格式有误' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/PlanUpdate.php b/Xboard/app/Http/Requests/Admin/PlanUpdate.php new file mode 100644 index 0000000..d9463e2 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/PlanUpdate.php @@ -0,0 +1,29 @@ + 'in:0,1', + 'renew' => 'in:0,1' + ]; + } + + public function messages() + { + return [ + 'show.in' => '销售状态格式不正确', + 'renew.in' => '续费状态格式不正确' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/ServerSave.php b/Xboard/app/Http/Requests/Admin/ServerSave.php new file mode 100644 index 0000000..bd1f6b2 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/ServerSave.php @@ -0,0 +1,212 @@ + 'nullable|boolean', + 'utls.fingerprint' => 'nullable|string', + ]; + + private const MULTIPLEX_RULES = [ + 'multiplex.enabled' => 'nullable|boolean', + 'multiplex.protocol' => 'nullable|string', + 'multiplex.max_connections' => 'nullable|integer', + 'multiplex.min_streams' => 'nullable|integer', + 'multiplex.max_streams' => 'nullable|integer', + 'multiplex.padding' => 'nullable|boolean', + 'multiplex.brutal.enabled' => 'nullable|boolean', + 'multiplex.brutal.up_mbps' => 'nullable|integer', + 'multiplex.brutal.down_mbps' => 'nullable|integer', + ]; + + private const PROTOCOL_RULES = [ + 'shadowsocks' => [ + 'cipher' => 'required|string', + 'obfs' => 'nullable|string', + 'obfs_settings.path' => 'nullable|string', + 'obfs_settings.host' => 'nullable|string', + 'plugin' => 'nullable|string', + 'plugin_opts' => 'nullable|string', + ], + 'vmess' => [ + 'tls' => 'required|integer', + 'network' => 'required|string', + 'network_settings' => 'nullable|array', + 'tls_settings.server_name' => 'nullable|string', + 'tls_settings.allow_insecure' => 'nullable|boolean', + ], + 'trojan' => [ + 'tls' => 'nullable|integer', + 'network' => 'required|string', + 'network_settings' => 'nullable|array', + 'server_name' => 'nullable|string', + 'allow_insecure' => 'nullable|boolean', + 'reality_settings.allow_insecure' => 'nullable|boolean', + 'reality_settings.server_name' => 'nullable|string', + 'reality_settings.server_port' => 'nullable|integer', + 'reality_settings.public_key' => 'nullable|string', + 'reality_settings.private_key' => 'nullable|string', + 'reality_settings.short_id' => 'nullable|string', + ], + 'hysteria' => [ + 'version' => 'required|integer', + 'alpn' => 'nullable|string', + 'obfs.open' => 'nullable|boolean', + 'obfs.type' => 'string|nullable', + 'obfs.password' => 'string|nullable', + 'tls.server_name' => 'nullable|string', + 'tls.allow_insecure' => 'nullable|boolean', + 'bandwidth.up' => 'nullable|integer', + 'bandwidth.down' => 'nullable|integer', + 'hop_interval' => 'integer|nullable', + ], + 'vless' => [ + 'tls' => 'required|integer', + 'network' => 'required|string', + 'network_settings' => 'nullable|array', + 'flow' => 'nullable|string', + 'encryption' => 'nullable|array', + 'encryption.enabled' => 'nullable|boolean', + 'encryption.encryption' => 'nullable|string', + 'encryption.decryption' => 'nullable|string', + 'tls_settings.server_name' => 'nullable|string', + 'tls_settings.allow_insecure' => 'nullable|boolean', + 'reality_settings.allow_insecure' => 'nullable|boolean', + 'reality_settings.server_name' => 'nullable|string', + 'reality_settings.server_port' => 'nullable|integer', + 'reality_settings.public_key' => 'nullable|string', + 'reality_settings.private_key' => 'nullable|string', + 'reality_settings.short_id' => 'nullable|string', + ], + 'socks' => [ + ], + 'naive' => [ + 'tls' => 'required|integer', + 'tls_settings' => 'nullable|array', + ], + 'http' => [ + 'tls' => 'required|integer', + 'tls_settings' => 'nullable|array', + ], + 'mieru' => [ + 'transport' => 'required|string|in:TCP,UDP', + 'traffic_pattern' => 'string' + ], + 'anytls' => [ + 'tls' => 'nullable|array', + 'alpn' => 'nullable|string', + 'padding_scheme' => 'nullable|array', + ], + ]; + + private function getBaseRules(): array + { + return [ + 'type' => 'required|in:' . implode(',', Server::VALID_TYPES), + 'spectific_key' => 'nullable|string', + 'code' => 'nullable|string', + 'show' => '', + 'name' => 'required|string', + 'group_ids' => 'nullable|array', + 'route_ids' => 'nullable|array', + 'parent_id' => 'nullable|integer', + 'host' => 'required', + 'port' => 'required', + 'server_port' => 'required', + 'tags' => 'nullable|array', + 'excludes' => 'nullable|array', + 'ips' => 'nullable|array', + 'rate' => 'required|numeric', + 'rate_time_enable' => 'nullable|boolean', + 'rate_time_ranges' => 'nullable|array', + 'custom_outbounds' => 'nullable|array', + 'custom_routes' => 'nullable|array', + 'cert_config' => 'nullable|array', + 'rate_time_ranges.*.start' => 'required_with:rate_time_ranges|string|date_format:H:i', + 'rate_time_ranges.*.end' => 'required_with:rate_time_ranges|string|date_format:H:i', + 'rate_time_ranges.*.rate' => 'required_with:rate_time_ranges|numeric|min:0', + 'protocol_settings' => 'array', + 'transfer_enable' => 'nullable|integer|min:0', + ]; + } + + public function rules(): array + { + $type = $this->input('type'); + $rules = $this->getBaseRules(); + + $protocolRules = self::PROTOCOL_RULES[$type] ?? []; + if (in_array($type, ['vmess', 'vless', 'trojan', 'mieru'])) { + $protocolRules = array_merge($protocolRules, self::MULTIPLEX_RULES, self::UTLS_RULES); + } + + foreach ($protocolRules as $field => $rule) { + $rules['protocol_settings.' . $field] = $rule; + } + + return $rules; + } + + public function attributes(): array + { + return [ + 'protocol_settings.cipher' => '加密方式', + 'protocol_settings.obfs' => '混淆类型', + 'protocol_settings.network' => '传输协议', + 'protocol_settings.port_range' => '端口范围', + 'protocol_settings.traffic_pattern' => 'Traffic Pattern', + 'protocol_settings.transport' => '传输方式', + 'protocol_settings.version' => '协议版本', + 'protocol_settings.password' => '密码', + 'protocol_settings.handshake.server' => '握手服务器', + 'protocol_settings.handshake.server_port' => '握手端口', + 'protocol_settings.multiplex.enabled' => '多路复用', + 'protocol_settings.multiplex.protocol' => '复用协议', + 'protocol_settings.multiplex.max_connections' => '最大连接数', + 'protocol_settings.multiplex.min_streams' => '最小流数', + 'protocol_settings.multiplex.max_streams' => '最大流数', + 'protocol_settings.multiplex.padding' => '复用填充', + 'protocol_settings.multiplex.brutal.enabled' => 'Brutal加速', + 'protocol_settings.multiplex.brutal.up_mbps' => 'Brutal上行速率', + 'protocol_settings.multiplex.brutal.down_mbps' => 'Brutal下行速率', + 'protocol_settings.utls.enabled' => 'uTLS', + 'protocol_settings.utls.fingerprint' => 'uTLS指纹', + ]; + } + + public function messages() + { + return [ + 'name.required' => '节点名称不能为空', + 'group_ids.required' => '权限组不能为空', + 'group_ids.array' => '权限组格式不正确', + 'route_ids.array' => '路由组格式不正确', + 'parent_id.integer' => '父ID格式不正确', + 'host.required' => '节点地址不能为空', + 'port.required' => '连接端口不能为空', + 'server_port.required' => '后端服务端口不能为空', + 'tls.required' => 'TLS不能为空', + 'tags.array' => '标签格式不正确', + 'rate.required' => '倍率不能为空', + 'rate.numeric' => '倍率格式不正确', + 'network.required' => '传输协议不能为空', + 'network.in' => '传输协议格式不正确', + 'networkSettings.array' => '传输协议配置有误', + 'ruleSettings.array' => '规则配置有误', + 'tlsSettings.array' => 'tls配置有误', + 'dnsSettings.array' => 'dns配置有误', + 'protocol_settings.*.required' => ':attribute 不能为空', + 'protocol_settings.*.string' => ':attribute 必须是字符串', + 'protocol_settings.*.integer' => ':attribute 必须是整数', + 'protocol_settings.*.in' => ':attribute 的值不合法', + 'transfer_enable.integer' => '流量上限必须是整数', + 'transfer_enable.min' => '流量上限不能小于0', + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/UserFetch.php b/Xboard/app/Http/Requests/Admin/UserFetch.php new file mode 100644 index 0000000..899c6a9 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/UserFetch.php @@ -0,0 +1,33 @@ + 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned,remarks,is_admin', + 'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=', + 'filter.*.value' => 'required' + ]; + } + + public function messages() + { + return [ + 'filter.*.key.required' => '过滤键不能为空', + 'filter.*.key.in' => '过滤键参数有误', + 'filter.*.condition.required' => '过滤条件不能为空', + 'filter.*.condition.in' => '过滤条件参数有误', + 'filter.*.value.required' => '过滤值不能为空' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/UserGenerate.php b/Xboard/app/Http/Requests/Admin/UserGenerate.php new file mode 100644 index 0000000..41b0722 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/UserGenerate.php @@ -0,0 +1,33 @@ + 'nullable|integer|max:500', + 'expired_at' => 'nullable|integer', + 'plan_id' => 'nullable|integer', + 'email_prefix' => 'nullable', + 'email_suffix' => 'required', + 'password' => 'nullable' + ]; + } + + public function messages() + { + return [ + 'generate_count.integer' => '生成数量必须为数字', + 'generate_count.max' => '生成数量最大为500个' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/UserSendMail.php b/Xboard/app/Http/Requests/Admin/UserSendMail.php new file mode 100644 index 0000000..f885c36 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/UserSendMail.php @@ -0,0 +1,29 @@ + 'required', + 'content' => 'required', + ]; + } + + public function messages() + { + return [ + 'subject.required' => '主题不能为空', + 'content.required' => '发送内容不能为空' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Admin/UserUpdate.php b/Xboard/app/Http/Requests/Admin/UserUpdate.php new file mode 100644 index 0000000..afbf922 --- /dev/null +++ b/Xboard/app/Http/Requests/Admin/UserUpdate.php @@ -0,0 +1,69 @@ + 'required|integer', + 'email' => 'email:strict', + 'password' => 'nullable|min:8', + 'transfer_enable' => 'numeric', + 'expired_at' => 'nullable|integer', + 'banned' => 'bool', + 'plan_id' => 'nullable|integer', + 'commission_rate' => 'nullable|integer|min:0|max:100', + 'discount' => 'nullable|integer|min:0|max:100', + 'is_admin' => 'boolean', + 'is_staff' => 'boolean', + 'u' => 'integer', + 'd' => 'integer', + 'balance' => 'numeric', + 'commission_type' => 'integer', + 'commission_balance' => 'numeric', + 'remarks' => 'nullable', + 'speed_limit' => 'nullable|integer', + 'device_limit' => 'nullable|integer' + ]; + } + + public function messages() + { + return [ + 'email.required' => '邮箱不能为空', + 'email.email' => '邮箱格式不正确', + 'transfer_enable.numeric' => '流量格式不正确', + 'expired_at.integer' => '到期时间格式不正确', + 'banned.in' => '是否封禁格式不正确', + 'is_admin.required' => '是否管理员不能为空', + 'is_admin.in' => '是否管理员格式不正确', + 'is_staff.required' => '是否员工不能为空', + 'is_staff.in' => '是否员工格式不正确', + 'plan_id.integer' => '订阅计划格式不正确', + 'commission_rate.integer' => '推荐返利比例格式不正确', + 'commission_rate.nullable' => '推荐返利比例格式不正确', + 'commission_rate.min' => '推荐返利比例最小为0', + 'commission_rate.max' => '推荐返利比例最大为100', + 'discount.integer' => '专属折扣比例格式不正确', + 'discount.nullable' => '专属折扣比例格式不正确', + 'discount.min' => '专属折扣比例最小为0', + 'discount.max' => '专属折扣比例最大为100', + 'u.integer' => '上行流量格式不正确', + 'd.integer' => '下行流量格式不正确', + 'balance.integer' => '余额格式不正确', + 'commission_balance.integer' => '佣金格式不正确', + 'password.min' => '密码长度最小8位', + 'speed_limit.integer' => '限速格式不正确', + 'device_limit.integer' => '设备数量格式不正确' + ]; + } +} diff --git a/Xboard/app/Http/Requests/Passport/AuthForget.php b/Xboard/app/Http/Requests/Passport/AuthForget.php new file mode 100644 index 0000000..8106f28 --- /dev/null +++ b/Xboard/app/Http/Requests/Passport/AuthForget.php @@ -0,0 +1,33 @@ + 'required|email:strict', + 'password' => 'required|min:8', + 'email_code' => 'required' + ]; + } + + public function messages() + { + return [ + 'email.required' => __('Email can not be empty'), + 'email.email' => __('Email format is incorrect'), + 'password.required' => __('Password can not be empty'), + 'password.min' => __('Password must be greater than 8 digits'), + 'email_code.required' => __('Email verification code cannot be empty') + ]; + } +} diff --git a/Xboard/app/Http/Requests/Passport/AuthLogin.php b/Xboard/app/Http/Requests/Passport/AuthLogin.php new file mode 100644 index 0000000..6aa832c --- /dev/null +++ b/Xboard/app/Http/Requests/Passport/AuthLogin.php @@ -0,0 +1,31 @@ + 'required|email:strict', + 'password' => 'required|min:8' + ]; + } + + public function messages() + { + return [ + 'email.required' => __('Email can not be empty'), + 'email.email' => __('Email format is incorrect'), + 'password.required' => __('Password can not be empty'), + 'password.min' => __('Password must be greater than 8 digits') + ]; + } +} diff --git a/Xboard/app/Http/Requests/Passport/AuthRegister.php b/Xboard/app/Http/Requests/Passport/AuthRegister.php new file mode 100644 index 0000000..63e053a --- /dev/null +++ b/Xboard/app/Http/Requests/Passport/AuthRegister.php @@ -0,0 +1,31 @@ + 'required|email:strict', + 'password' => 'required|min:8' + ]; + } + + public function messages() + { + return [ + 'email.required' => __('Email can not be empty'), + 'email.email' => __('Email format is incorrect'), + 'password.required' => __('Password can not be empty'), + 'password.min' => __('Password must be greater than 8 digits') + ]; + } +} diff --git a/Xboard/app/Http/Requests/Passport/CommSendEmailVerify.php b/Xboard/app/Http/Requests/Passport/CommSendEmailVerify.php new file mode 100644 index 0000000..ff5ecdd --- /dev/null +++ b/Xboard/app/Http/Requests/Passport/CommSendEmailVerify.php @@ -0,0 +1,28 @@ + 'required|email:strict' + ]; + } + + public function messages() + { + return [ + 'email.required' => __('Email can not be empty'), + 'email.email' => __('Email format is incorrect') + ]; + } +} diff --git a/Xboard/app/Http/Requests/Staff/UserUpdate.php b/Xboard/app/Http/Requests/Staff/UserUpdate.php new file mode 100644 index 0000000..be22144 --- /dev/null +++ b/Xboard/app/Http/Requests/Staff/UserUpdate.php @@ -0,0 +1,56 @@ + 'required|email:strict', + 'password' => 'nullable', + 'transfer_enable' => 'numeric', + 'expired_at' => 'nullable|integer', + 'banned' => 'required|in:0,1', + 'plan_id' => 'nullable|integer', + 'commission_rate' => 'nullable|integer|min:0|max:100', + 'discount' => 'nullable|integer|min:0|max:100', + 'u' => 'integer', + 'd' => 'integer', + 'balance' => 'integer', + 'commission_balance' => 'integer' + ]; + } + + public function messages() + { + return [ + 'email.required' => '邮箱不能为空', + 'email.email' => '邮箱格式不正确', + 'transfer_enable.numeric' => '流量格式不正确', + 'expired_at.integer' => '到期时间格式不正确', + 'banned.required' => '是否封禁不能为空', + 'banned.in' => '是否封禁格式不正确', + 'plan_id.integer' => '订阅计划格式不正确', + 'commission_rate.integer' => '推荐返利比例格式不正确', + 'commission_rate.nullable' => '推荐返利比例格式不正确', + 'commission_rate.min' => '推荐返利比例最小为0', + 'commission_rate.max' => '推荐返利比例最大为100', + 'discount.integer' => '专属折扣比例格式不正确', + 'discount.nullable' => '专属折扣比例格式不正确', + 'discount.min' => '专属折扣比例最小为0', + 'discount.max' => '专属折扣比例最大为100', + 'u.integer' => '上行流量格式不正确', + 'd.integer' => '下行流量格式不正确', + 'balance.integer' => '余额格式不正确', + 'commission_balance.integer' => '佣金格式不正确' + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/GiftCardCheckRequest.php b/Xboard/app/Http/Requests/User/GiftCardCheckRequest.php new file mode 100644 index 0000000..ed0b5e3 --- /dev/null +++ b/Xboard/app/Http/Requests/User/GiftCardCheckRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/GiftCardRedeemRequest.php b/Xboard/app/Http/Requests/User/GiftCardRedeemRequest.php new file mode 100644 index 0000000..7feb4b4 --- /dev/null +++ b/Xboard/app/Http/Requests/User/GiftCardRedeemRequest.php @@ -0,0 +1,44 @@ + 'required|string|min:8|max:32', + ]; + } + + /** + * Get custom messages for validator errors. + * + * @return array + */ + public function messages() + { + return [ + 'code.required' => '请输入兑换码', + 'code.min' => '兑换码长度不能少于8位', + 'code.max' => '兑换码长度不能超过32位', + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/OrderSave.php b/Xboard/app/Http/Requests/User/OrderSave.php new file mode 100644 index 0000000..449bcaa --- /dev/null +++ b/Xboard/app/Http/Requests/User/OrderSave.php @@ -0,0 +1,30 @@ + 'required', + 'period' => 'required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_price' + ]; + } + + public function messages() + { + return [ + 'plan_id.required' => __('Plan ID cannot be empty'), + 'period.required' => __('Plan period cannot be empty'), + 'period.in' => __('Wrong plan period') + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/TicketSave.php b/Xboard/app/Http/Requests/User/TicketSave.php new file mode 100644 index 0000000..412778f --- /dev/null +++ b/Xboard/app/Http/Requests/User/TicketSave.php @@ -0,0 +1,32 @@ + 'required', + 'level' => 'required|in:0,1,2', + 'message' => 'required' + ]; + } + + public function messages() + { + return [ + 'subject.required' => __('Ticket subject cannot be empty'), + 'level.required' => __('Ticket level cannot be empty'), + 'level.in' => __('Incorrect ticket level format'), + 'message.required' => __('Message cannot be empty') + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/TicketWithdraw.php b/Xboard/app/Http/Requests/User/TicketWithdraw.php new file mode 100644 index 0000000..d0da905 --- /dev/null +++ b/Xboard/app/Http/Requests/User/TicketWithdraw.php @@ -0,0 +1,29 @@ + 'required', + 'withdraw_account' => 'required' + ]; + } + + public function messages() + { + return [ + 'withdraw_method.required' => __('The withdrawal method cannot be empty'), + 'withdraw_account.required' => __('The withdrawal account cannot be empty') + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/UserChangePassword.php b/Xboard/app/Http/Requests/User/UserChangePassword.php new file mode 100644 index 0000000..04e70c7 --- /dev/null +++ b/Xboard/app/Http/Requests/User/UserChangePassword.php @@ -0,0 +1,30 @@ + 'required', + 'new_password' => 'required|min:8' + ]; + } + + public function messages() + { + return [ + 'old_password.required' => __('Old password cannot be empty'), + 'new_password.required' => __('New password cannot be empty'), + 'new_password.min' => __('Password must be greater than 8 digits') + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/UserTransfer.php b/Xboard/app/Http/Requests/User/UserTransfer.php new file mode 100644 index 0000000..478c825 --- /dev/null +++ b/Xboard/app/Http/Requests/User/UserTransfer.php @@ -0,0 +1,29 @@ + 'required|integer|min:1' + ]; + } + + public function messages() + { + return [ + 'transfer_amount.required' => __('The transfer amount cannot be empty'), + 'transfer_amount.integer' => __('The transfer amount parameter is wrong'), + 'transfer_amount.min' => __('The transfer amount parameter is wrong') + ]; + } +} diff --git a/Xboard/app/Http/Requests/User/UserUpdate.php b/Xboard/app/Http/Requests/User/UserUpdate.php new file mode 100644 index 0000000..5ba6604 --- /dev/null +++ b/Xboard/app/Http/Requests/User/UserUpdate.php @@ -0,0 +1,29 @@ + 'in:0,1', + 'remind_traffic' => 'in:0,1' + ]; + } + + public function messages() + { + return [ + 'show.in' => __('Incorrect format of expiration reminder'), + 'renew.in' => __('Incorrect traffic alert format') + ]; + } +} diff --git a/Xboard/app/Http/Resources/ComissionLogResource.php b/Xboard/app/Http/Resources/ComissionLogResource.php new file mode 100644 index 0000000..8d86769 --- /dev/null +++ b/Xboard/app/Http/Resources/ComissionLogResource.php @@ -0,0 +1,25 @@ + + */ + public function toArray(Request $request): array + { + return [ + "id"=> $this['id'], + "order_amount" => $this['order_amount'], + "trade_no" => $this['trade_no'], + "get_amount" => $this['get_amount'], + "created_at" => $this['created_at'] + ]; + } +} diff --git a/Xboard/app/Http/Resources/CouponResource.php b/Xboard/app/Http/Resources/CouponResource.php new file mode 100644 index 0000000..049a61f --- /dev/null +++ b/Xboard/app/Http/Resources/CouponResource.php @@ -0,0 +1,38 @@ + 转换后的数组 + */ + public function toArray(Request $request): array + { + return [ + ...$this->resource->toArray(), + 'limit_plan_ids' => empty($this->limit_plan_ids) ? null : collect($this->limit_plan_ids) + ->map(fn(mixed $id): string => (string) $id) + ->values() + ->all(), + 'limit_period' => empty($this->limit_period) ? null : collect($this->limit_period) + ->map(fn(mixed $period): string => (string) PlanService::convertToLegacyPeriod($period)) + ->values() + ->all(), + ]; + } +} diff --git a/Xboard/app/Http/Resources/InviteCodeResource.php b/Xboard/app/Http/Resources/InviteCodeResource.php new file mode 100644 index 0000000..927c782 --- /dev/null +++ b/Xboard/app/Http/Resources/InviteCodeResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + $data = [ + "user_id" => $this['user_id'], + "code" => $this['code'], + "pv" => $this['pv'], + "status" => $this['status'], + "created_at" => $this['created_at'], + "updated_at" => $this['updated_at'] + ]; + if(!config('hidden_features.enable_exposed_user_count_fix')) $data['user_id']= $this['user_id']; + return $data; + } +} diff --git a/Xboard/app/Http/Resources/KnowledgeResource.php b/Xboard/app/Http/Resources/KnowledgeResource.php new file mode 100644 index 0000000..cda796c --- /dev/null +++ b/Xboard/app/Http/Resources/KnowledgeResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + $data = [ + 'id' => $this['id'], + 'category' => $this['category'], + 'title' => $this['title'], + 'body' => $this->when(isset($this['body']), $this['body']), + 'updated_at' => $this['updated_at'], + ]; + + return HookManager::filter('user.knowledge.resource', $data, $request, $this); + } +} diff --git a/Xboard/app/Http/Resources/MessageResource.php b/Xboard/app/Http/Resources/MessageResource.php new file mode 100644 index 0000000..9a72e1c --- /dev/null +++ b/Xboard/app/Http/Resources/MessageResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + "id" => $this['id'], + "ticket_id" => $this['ticket_id'], + "is_me" => $this['is_from_user'], + "message" => $this["message"], + "created_at" => $this['created_at'], + "updated_at" => $this['updated_at'] + ]; + } +} diff --git a/Xboard/app/Http/Resources/NodeResource.php b/Xboard/app/Http/Resources/NodeResource.php new file mode 100644 index 0000000..d7f90af --- /dev/null +++ b/Xboard/app/Http/Resources/NodeResource.php @@ -0,0 +1,29 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this['id'], + 'type' => $this['type'], + 'version' => $this['version'] ?? null, + 'name' => $this['name'], + 'rate' => $this['rate'], + 'tags' => $this['tags'], + 'is_online' => $this['is_online'], + 'cache_key' => $this['cache_key'], + 'last_check_at' => $this['last_check_at'] + ]; + } +} diff --git a/Xboard/app/Http/Resources/OrderResource.php b/Xboard/app/Http/Resources/OrderResource.php new file mode 100644 index 0000000..ae3e6e4 --- /dev/null +++ b/Xboard/app/Http/Resources/OrderResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + ...parent::toArray($request), + 'period' => PlanService::getLegacyPeriod((string)$this->period), + 'plan' => $this->whenLoaded('plan', fn() => PlanResource::make($this->plan)), + ]; + } +} diff --git a/Xboard/app/Http/Resources/PlanResource.php b/Xboard/app/Http/Resources/PlanResource.php new file mode 100644 index 0000000..b78583a --- /dev/null +++ b/Xboard/app/Http/Resources/PlanResource.php @@ -0,0 +1,122 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->resource['id'], + 'group_id' => $this->resource['group_id'], + 'name' => $this->resource['name'], + 'tags' => $this->resource['tags'], + 'content' => $this->formatContent(), + ...$this->getPeriodPrices(), + 'capacity_limit' => $this->getFormattedCapacityLimit(), + 'transfer_enable' => $this->resource['transfer_enable'], + 'speed_limit' => $this->resource['speed_limit'], + 'device_limit' => $this->resource['device_limit'], + 'show' => (bool) $this->resource['show'], + 'sell' => (bool) $this->resource['sell'], + 'renew' => (bool) $this->resource['renew'], + 'reset_traffic_method' => $this->resource['reset_traffic_method'], + 'sort' => $this->resource['sort'], + 'created_at' => $this->resource['created_at'], + 'updated_at' => $this->resource['updated_at'] + ]; + } + + /** + * Get transformed period prices using Plan mapping + * + * @return array + */ + protected function getPeriodPrices(): array + { + return collect(Plan::LEGACY_PERIOD_MAPPING) + ->mapWithKeys(function (string $newPeriod, string $legacyPeriod): array { + $price = $this->resource['prices'][$newPeriod] ?? null; + return [ + $legacyPeriod => $price !== null + ? (float) $price * self::PRICE_MULTIPLIER + : null + ]; + }) + ->all(); + } + + /** + * Get formatted capacity limit value + * + * @return int|string|null + */ + protected function getFormattedCapacityLimit(): int|string|null + { + $limit = $this->resource['capacity_limit']; + + return match (true) { + $limit === null => null, + $limit <= 0 => __('Sold out'), + default => (int) $limit, + }; + } + + /** + * Format content with template variables + * + * @return string + */ + protected function formatContent(): string + { + $content = $this->resource['content'] ?? ''; + + $replacements = [ + '{{transfer}}' => $this->resource['transfer_enable'], + '{{speed}}' => $this->resource['speed_limit'] === NULL ? __('No Limit') : $this->resource['speed_limit'], + '{{devices}}' => $this->resource['device_limit'] === NULL ? __('No Limit') : $this->resource['device_limit'], + '{{reset_method}}' => $this->getResetMethodText(), + ]; + + return str_replace( + array_keys($replacements), + array_values($replacements), + $content + ); + } + + /** + * Get reset method text + * + * @return string + */ + protected function getResetMethodText(): string + { + $method = $this->resource['reset_traffic_method']; + + if ($method === Plan::RESET_TRAFFIC_FOLLOW_SYSTEM) { + $method = admin_setting('reset_traffic_method', Plan::RESET_TRAFFIC_MONTHLY); + } + return match ($method) { + Plan::RESET_TRAFFIC_FIRST_DAY_MONTH => __('First Day of Month'), + Plan::RESET_TRAFFIC_MONTHLY => __('Monthly'), + Plan::RESET_TRAFFIC_NEVER => __('Never'), + Plan::RESET_TRAFFIC_FIRST_DAY_YEAR => __('First Day of Year'), + Plan::RESET_TRAFFIC_YEARLY => __('Yearly'), + default => __('Monthly') + }; + } +} \ No newline at end of file diff --git a/Xboard/app/Http/Resources/TicketResource.php b/Xboard/app/Http/Resources/TicketResource.php new file mode 100644 index 0000000..9b9a773 --- /dev/null +++ b/Xboard/app/Http/Resources/TicketResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + $data = [ + "id" => $this['id'], + "level" => $this['level'], + "reply_status" => $this['reply_status'], + "status" => $this['status'], + "subject" => $this['subject'], + "message" => array_key_exists('message',$this->additional) ? MessageResource::collection($this['message']) : null, + "created_at" => $this['created_at'], + "updated_at" => $this['updated_at'] + ]; + if(!config('hidden_features.enable_exposed_user_count_fix')) $data['user_id']= $this['user_id']; + return $data; + + } +} diff --git a/Xboard/app/Http/Resources/TrafficLogResource.php b/Xboard/app/Http/Resources/TrafficLogResource.php new file mode 100644 index 0000000..798ea7e --- /dev/null +++ b/Xboard/app/Http/Resources/TrafficLogResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + $data = [ + "d" => $this['d'], + "u" => $this['u'], + "record_at" => $this['record_at'], + "server_rate" => $this['server_rate'], + ]; + if(!config('hidden_features.enable_exposed_user_count_fix')) $data['user_id']= $this['user_id']; + return $data; + } +} diff --git a/Xboard/app/Http/Routes/V1/ClientRoute.php b/Xboard/app/Http/Routes/V1/ClientRoute.php new file mode 100644 index 0000000..ad13989 --- /dev/null +++ b/Xboard/app/Http/Routes/V1/ClientRoute.php @@ -0,0 +1,23 @@ +group([ + 'prefix' => 'client', + 'middleware' => 'client' + ], function ($router) { + // Client + $router->get('/subscribe', [ClientController::class, 'subscribe'])->name('client.subscribe.legacy'); + // App + $router->get('/app/getConfig', [AppController::class, 'getConfig']); + $router->get('/app/getVersion', [AppController::class, 'getVersion']); + }); + } +} diff --git a/Xboard/app/Http/Routes/V1/GuestRoute.php b/Xboard/app/Http/Routes/V1/GuestRoute.php new file mode 100644 index 0000000..3c4f571 --- /dev/null +++ b/Xboard/app/Http/Routes/V1/GuestRoute.php @@ -0,0 +1,27 @@ +group([ + 'prefix' => 'guest' + ], function ($router) { + // Plan + $router->get('/plan/fetch', [PlanController::class, 'fetch']); + // Telegram + $router->post('/telegram/webhook', [TelegramController::class, 'webhook']); + // Payment + $router->match(['get', 'post'], '/payment/notify/{method}/{uuid}', [PaymentController::class, 'notify']); + // Comm + $router->get('/comm/config', [CommController::class, 'config']); + }); + } +} diff --git a/Xboard/app/Http/Routes/V1/PassportRoute.php b/Xboard/app/Http/Routes/V1/PassportRoute.php new file mode 100644 index 0000000..3134b96 --- /dev/null +++ b/Xboard/app/Http/Routes/V1/PassportRoute.php @@ -0,0 +1,27 @@ +group([ + 'prefix' => 'passport' + ], function ($router) { + // Auth + $router->post('/auth/register', [AuthController::class, 'register']); + $router->post('/auth/login', [AuthController::class, 'login']); + $router->get('/auth/token2Login', [AuthController::class, 'token2Login']); + $router->post('/auth/forget', [AuthController::class, 'forget']); + $router->post('/auth/getQuickLoginUrl', [AuthController::class, 'getQuickLoginUrl']); + $router->post('/auth/loginWithMailLink', [AuthController::class, 'loginWithMailLink']); + // Comm + $router->post('/comm/sendEmailVerify', [CommController::class, 'sendEmailVerify']); + $router->post('/comm/pv', [CommController::class, 'pv']); + }); + } +} diff --git a/Xboard/app/Http/Routes/V1/ServerRoute.php b/Xboard/app/Http/Routes/V1/ServerRoute.php new file mode 100644 index 0000000..42f7b22 --- /dev/null +++ b/Xboard/app/Http/Routes/V1/ServerRoute.php @@ -0,0 +1,45 @@ +group([ + 'prefix' => 'server', + ], function ($router) { + $router->group([ + 'prefix' => 'UniProxy', + 'middleware' => 'server' + ], function ($route) { + $route->get('config', [UniProxyController::class, 'config']); + $route->get('user', [UniProxyController::class, 'user']); + $route->post('push', [UniProxyController::class, 'push']); + $route->post('alive', [UniProxyController::class, 'alive']); + $route->get('alivelist', [UniProxyController::class, 'alivelist']); + $route->post('status', [UniProxyController::class, 'status']); + }); + $router->group([ + 'prefix' => 'ShadowsocksTidalab', + 'middleware' => 'server:shadowsocks' + ], function ($route) { + $route->get('user', [ShadowsocksTidalabController::class, 'user']); + $route->post('submit', [ShadowsocksTidalabController::class, 'submit']); + }); + $router->group([ + 'prefix' => 'TrojanTidalab', + 'middleware' => 'server:trojan' + ], function ($route) { + $route->get('config', [TrojanTidalabController::class, 'config']); + $route->get('user', [TrojanTidalabController::class, 'user']); + $route->post('submit', [TrojanTidalabController::class, 'submit']); + }); + }); + } +} diff --git a/Xboard/app/Http/Routes/V1/UserRoute.php b/Xboard/app/Http/Routes/V1/UserRoute.php new file mode 100644 index 0000000..92c3605 --- /dev/null +++ b/Xboard/app/Http/Routes/V1/UserRoute.php @@ -0,0 +1,83 @@ +group([ + 'prefix' => 'user', + 'middleware' => 'user' + ], function ($router) { + // User + $router->get('/resetSecurity', [UserController::class, 'resetSecurity']); + $router->get('/info', [UserController::class, 'info']); + $router->post('/changePassword', [UserController::class, 'changePassword']); + $router->post('/update', [UserController::class, 'update']); + $router->get('/getSubscribe', [UserController::class, 'getSubscribe']); + $router->get('/getStat', [UserController::class, 'getStat']); + $router->get('/checkLogin', [UserController::class, 'checkLogin']); + $router->post('/transfer', [UserController::class, 'transfer']); + $router->post('/getQuickLoginUrl', [UserController::class, 'getQuickLoginUrl']); + $router->get('/getActiveSession', [UserController::class, 'getActiveSession']); + $router->post('/removeActiveSession', [UserController::class, 'removeActiveSession']); + // Order + $router->post('/order/save', [OrderController::class, 'save']); + $router->post('/order/checkout', [OrderController::class, 'checkout']); + $router->get('/order/check', [OrderController::class, 'check']); + $router->get('/order/detail', [OrderController::class, 'detail']); + $router->get('/order/fetch', [OrderController::class, 'fetch']); + $router->get('/order/getPaymentMethod', [OrderController::class, 'getPaymentMethod']); + $router->post('/order/cancel', [OrderController::class, 'cancel']); + // Plan + $router->get('/plan/fetch', [PlanController::class, 'fetch']); + // Invite + $router->get('/invite/save', [InviteController::class, 'save']); + $router->get('/invite/fetch', [InviteController::class, 'fetch']); + $router->get('/invite/details', [InviteController::class, 'details']); + // Notice + $router->get('/notice/fetch', [NoticeController::class, 'fetch']); + // Ticket + $router->post('/ticket/reply', [TicketController::class, 'reply']); + $router->post('/ticket/close', [TicketController::class, 'close']); + $router->post('/ticket/save', [TicketController::class, 'save']); + $router->get('/ticket/fetch', [TicketController::class, 'fetch']); + $router->post('/ticket/withdraw', [TicketController::class, 'withdraw']); + // Server + $router->get('/server/fetch', [ServerController::class, 'fetch']); + // Coupon + $router->post('/coupon/check', [CouponController::class, 'check']); + // Gift Card + $router->post('/gift-card/check', [GiftCardController::class, 'check']); + $router->post('/gift-card/redeem', [GiftCardController::class, 'redeem']); + $router->get('/gift-card/history', [GiftCardController::class, 'history']); + $router->get('/gift-card/detail', [GiftCardController::class, 'detail']); + $router->get('/gift-card/types', [GiftCardController::class, 'types']); + // Telegram + $router->get('/telegram/getBotInfo', [TelegramController::class, 'getBotInfo']); + // Comm + $router->get('/comm/config', [CommController::class, 'config']); + $router->Post('/comm/getStripePublicKey', [CommController::class, 'getStripePublicKey']); + // Knowledge + $router->get('/knowledge/fetch', [KnowledgeController::class, 'fetch']); + $router->get('/knowledge/getCategory', [KnowledgeController::class, 'getCategory']); + // Stat + $router->get('/stat/getTrafficLog', [StatController::class, 'getTrafficLog']); + }); + } +} diff --git a/Xboard/app/Http/Routes/V2/AdminRoute.php b/Xboard/app/Http/Routes/V2/AdminRoute.php new file mode 100644 index 0000000..3d8c910 --- /dev/null +++ b/Xboard/app/Http/Routes/V2/AdminRoute.php @@ -0,0 +1,276 @@ +group([ + 'prefix' => admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))), + 'middleware' => ['admin', 'log'], + ], function ($router) { + // Config + $router->group([ + 'prefix' => 'config' + ], function ($router) { + $router->get('/fetch', [ConfigController::class, 'fetch']); + $router->post('/save', [ConfigController::class, 'save']); + $router->get('/getEmailTemplate', [ConfigController::class, 'getEmailTemplate']); + $router->get('/getThemeTemplate', [ConfigController::class, 'getThemeTemplate']); + $router->post('/setTelegramWebhook', [ConfigController::class, 'setTelegramWebhook']); + $router->post('/testSendMail', [ConfigController::class, 'testSendMail']); + }); + + // Plan + $router->group([ + 'prefix' => 'plan' + ], function ($router) { + $router->get('/fetch', [PlanController::class, 'fetch']); + $router->post('/save', [PlanController::class, 'save']); + $router->post('/drop', [PlanController::class, 'drop']); + $router->post('/update', [PlanController::class, 'update']); + $router->post('/sort', [PlanController::class, 'sort']); + }); + + // Server + $router->group([ + 'prefix' => 'server/group' + ], function ($router) { + $router->get('/fetch', [GroupController::class, 'fetch']); + $router->post('/save', [GroupController::class, 'save']); + $router->post('/drop', [GroupController::class, 'drop']); + }); + $router->group([ + 'prefix' => 'server/route' + ], function ($router) { + $router->get('/fetch', [RouteController::class, 'fetch']); + $router->post('/save', [RouteController::class, 'save']); + $router->post('/drop', [RouteController::class, 'drop']); + }); + $router->group([ + 'prefix' => 'server/manage' + ], function ($router) { + $router->get('/getNodes', [ManageController::class, 'getNodes']); + $router->post('/sort', [ManageController::class, 'sort']); + }); + + // 节点更新接口 + $router->group([ + 'prefix' => 'server/manage' + ], function ($router) { + $router->post('/update', [ManageController::class, 'update']); + $router->post('/save', [ManageController::class, 'save']); + $router->post('/drop', [ManageController::class, 'drop']); + $router->post('/copy', [ManageController::class, 'copy']); + $router->post('/sort', [ManageController::class, 'sort']); + $router->post('/batchDelete', [ManageController::class, 'batchDelete']); + $router->post('/resetTraffic', [ManageController::class, 'resetTraffic']); + $router->post('/batchResetTraffic', [ManageController::class, 'batchResetTraffic']); + }); + + // Order + $router->group([ + 'prefix' => 'order' + ], function ($router) { + $router->any('/fetch', [OrderController::class, 'fetch']); + $router->post('/update', [OrderController::class, 'update']); + $router->post('/assign', [OrderController::class, 'assign']); + $router->post('/paid', [OrderController::class, 'paid']); + $router->post('/cancel', [OrderController::class, 'cancel']); + $router->post('/detail', [OrderController::class, 'detail']); + }); + + // User + $router->group([ + 'prefix' => 'user' + ], function ($router) { + $router->any('/fetch', [UserController::class, 'fetch']); + $router->post('/update', [UserController::class, 'update']); + $router->get('/getUserInfoById', [UserController::class, 'getUserInfoById']); + $router->post('/generate', [UserController::class, 'generate']); + $router->post('/dumpCSV', [UserController::class, 'dumpCSV']); + $router->post('/sendMail', [UserController::class, 'sendMail']); + $router->post('/ban', [UserController::class, 'ban']); + $router->post('/resetSecret', [UserController::class, 'resetSecret']); + $router->post('/setInviteUser', [UserController::class, 'setInviteUser']); + $router->post('/destroy', [UserController::class, 'destroy']); + }); + + // Stat + $router->group([ + 'prefix' => 'stat' + ], function ($router) { + $router->get('/getOverride', [StatController::class, 'getOverride']); + $router->get('/getStats', [StatController::class, 'getStats']); + $router->get('/getServerLastRank', [StatController::class, 'getServerLastRank']); + $router->get('/getServerYesterdayRank', [StatController::class, 'getServerYesterdayRank']); + $router->get('/getOrder', [StatController::class, 'getOrder']); + $router->any('/getStatUser', [StatController::class, 'getStatUser']); + $router->get('/getRanking', [StatController::class, 'getRanking']); + $router->get('/getStatRecord', [StatController::class, 'getStatRecord']); + $router->get('/getTrafficRank', [StatController::class, 'getTrafficRank']); + }); + + // Notice + $router->group([ + 'prefix' => 'notice' + ], function ($router) { + $router->get('/fetch', [NoticeController::class, 'fetch']); + $router->post('/save', [NoticeController::class, 'save']); + $router->post('/update', [NoticeController::class, 'update']); + $router->post('/drop', [NoticeController::class, 'drop']); + $router->post('/show', [NoticeController::class, 'show']); + $router->post('/sort', [NoticeController::class, 'sort']); + }); + + // Ticket + $router->group([ + 'prefix' => 'ticket' + ], function ($router) { + $router->any('/fetch', [TicketController::class, 'fetch']); + $router->post('/reply', [TicketController::class, 'reply']); + $router->post('/close', [TicketController::class, 'close']); + }); + + // Coupon + $router->group([ + 'prefix' => 'coupon' + ], function ($router) { + $router->any('/fetch', [CouponController::class, 'fetch']); + $router->post('/generate', [CouponController::class, 'generate']); + $router->post('/drop', [CouponController::class, 'drop']); + $router->post('/show', [CouponController::class, 'show']); + $router->post('/update', [CouponController::class, 'update']); + }); + + // Gift Card + $router->group([ + 'prefix' => 'gift-card' + ], function ($router) { + // Template management + $router->any('/templates', [GiftCardController::class, 'templates']); + $router->post('/create-template', [GiftCardController::class, 'createTemplate']); + $router->post('/update-template', [GiftCardController::class, 'updateTemplate']); + $router->post('/delete-template', [GiftCardController::class, 'deleteTemplate']); + + // Code management + $router->post('/generate-codes', [GiftCardController::class, 'generateCodes']); + $router->any('/codes', [GiftCardController::class, 'codes']); + $router->post('/toggle-code', [GiftCardController::class, 'toggleCode']); + $router->get('/export-codes', [GiftCardController::class, 'exportCodes']); + $router->post('/update-code', [GiftCardController::class, 'updateCode']); + $router->post('/delete-code', [GiftCardController::class, 'deleteCode']); + + // Usage records + $router->any('/usages', [GiftCardController::class, 'usages']); + + // Statistics + $router->any('/statistics', [GiftCardController::class, 'statistics']); + $router->get('/types', [GiftCardController::class, 'types']); + }); + + // Knowledge + $router->group([ + 'prefix' => 'knowledge' + ], function ($router) { + $router->get('/fetch', [KnowledgeController::class, 'fetch']); + $router->get('/getCategory', [KnowledgeController::class, 'getCategory']); + $router->post('/save', [KnowledgeController::class, 'save']); + $router->post('/show', [KnowledgeController::class, 'show']); + $router->post('/drop', [KnowledgeController::class, 'drop']); + $router->post('/sort', [KnowledgeController::class, 'sort']); + }); + + // Payment + $router->group([ + 'prefix' => 'payment' + ], function ($router) { + $router->get('/fetch', [PaymentController::class, 'fetch']); + $router->get('/getPaymentMethods', [PaymentController::class, 'getPaymentMethods']); + $router->post('/getPaymentForm', [PaymentController::class, 'getPaymentForm']); + $router->post('/save', [PaymentController::class, 'save']); + $router->post('/drop', [PaymentController::class, 'drop']); + $router->post('/show', [PaymentController::class, 'show']); + $router->post('/sort', [PaymentController::class, 'sort']); + }); + + // System + $router->group([ + 'prefix' => 'system' + ], function ($router) { + $router->get('/getSystemStatus', [SystemController::class, 'getSystemStatus']); + $router->get('/getQueueStats', [SystemController::class, 'getQueueStats']); + $router->get('/getQueueWorkload', [SystemController::class, 'getQueueWorkload']); + $router->get('/getQueueMasters', '\\Laravel\\Horizon\\Http\\Controllers\\MasterSupervisorController@index'); + $router->get('/getHorizonFailedJobs', [SystemController::class, 'getHorizonFailedJobs']); + $router->any('/getAuditLog', [SystemController::class, 'getAuditLog']); + }); + + // Update + // $router->group([ + // 'prefix' => 'update' + // ], function ($router) { + // $router->get('/check', [UpdateController::class, 'checkUpdate']); + // $router->post('/execute', [UpdateController::class, 'executeUpdate']); + // }); + + // Theme + $router->group([ + 'prefix' => 'theme' + ], function ($router) { + $router->get('/getThemes', [ThemeController::class, 'getThemes']); + $router->post('/upload', [ThemeController::class, 'upload']); + $router->post('/delete', [ThemeController::class, 'delete']); + $router->post('/saveThemeConfig', [ThemeController::class, 'saveThemeConfig']); + $router->post('/getThemeConfig', [ThemeController::class, 'getThemeConfig']); + }); + + // Plugin + $router->group([ + 'prefix' => 'plugin' + ], function ($router) { + $router->get('/types', [\App\Http\Controllers\V2\Admin\PluginController::class, 'types']); + $router->get('/getPlugins', [\App\Http\Controllers\V2\Admin\PluginController::class, 'index']); + $router->post('/upload', [\App\Http\Controllers\V2\Admin\PluginController::class, 'upload']); + $router->post('/delete', [\App\Http\Controllers\V2\Admin\PluginController::class, 'delete']); + $router->post('install', [\App\Http\Controllers\V2\Admin\PluginController::class, 'install']); + $router->post('uninstall', [\App\Http\Controllers\V2\Admin\PluginController::class, 'uninstall']); + $router->post('enable', [\App\Http\Controllers\V2\Admin\PluginController::class, 'enable']); + $router->post('disable', [\App\Http\Controllers\V2\Admin\PluginController::class, 'disable']); + $router->get('config', [\App\Http\Controllers\V2\Admin\PluginController::class, 'getConfig']); + $router->post('config', [\App\Http\Controllers\V2\Admin\PluginController::class, 'updateConfig']); + $router->post('upgrade', [\App\Http\Controllers\V2\Admin\PluginController::class, 'upgrade']); + }); + + // 流量重置管理 + $router->group([ + 'prefix' => 'traffic-reset' + ], function ($router) { + $router->get('logs', [TrafficResetController::class, 'logs']); + $router->get('stats', [TrafficResetController::class, 'stats']); + $router->get('user/{userId}/history', [TrafficResetController::class, 'userHistory']); + $router->post('reset-user', [TrafficResetController::class, 'resetUser']); + }); + }); + + } +} diff --git a/Xboard/app/Http/Routes/V2/ClientRoute.php b/Xboard/app/Http/Routes/V2/ClientRoute.php new file mode 100644 index 0000000..693a40d --- /dev/null +++ b/Xboard/app/Http/Routes/V2/ClientRoute.php @@ -0,0 +1,20 @@ +group([ + 'prefix' => 'client', + 'middleware' => 'client' + ], function ($router) { + // App + $router->get('/app/getConfig', [AppController::class, 'getConfig']); + $router->get('/app/getVersion', [AppController::class, 'getVersion']); + }); + } +} diff --git a/Xboard/app/Http/Routes/V2/PassportRoute.php b/Xboard/app/Http/Routes/V2/PassportRoute.php new file mode 100644 index 0000000..ed91d81 --- /dev/null +++ b/Xboard/app/Http/Routes/V2/PassportRoute.php @@ -0,0 +1,27 @@ +group([ + 'prefix' => 'passport' + ], function ($router) { + // Auth + $router->post('/auth/register', [AuthController::class, 'register']); + $router->post('/auth/login', [AuthController::class, 'login']); + $router->get ('/auth/token2Login', [AuthController::class, 'token2Login']); + $router->post('/auth/forget', [AuthController::class, 'forget']); + $router->post('/auth/getQuickLoginUrl', [AuthController::class, 'getQuickLoginUrl']); + $router->post('/auth/loginWithMailLink', [AuthController::class, 'loginWithMailLink']); + // Comm + $router->post('/comm/sendEmailVerify', [CommController::class, 'sendEmailVerify']); + $router->post('/comm/pv', [CommController::class, 'pv']); + }); + } +} diff --git a/Xboard/app/Http/Routes/V2/ServerRoute.php b/Xboard/app/Http/Routes/V2/ServerRoute.php new file mode 100644 index 0000000..9742d31 --- /dev/null +++ b/Xboard/app/Http/Routes/V2/ServerRoute.php @@ -0,0 +1,29 @@ +group([ + 'prefix' => 'server', + 'middleware' => 'server' + ], function ($route) { + $route->post('handshake', [ServerController::class, 'handshake']); + $route->post('report', [ServerController::class, 'report']); + $route->get('config', [UniProxyController::class, 'config']); + $route->get('user', [UniProxyController::class, 'user']); + $route->post('push', [UniProxyController::class, 'push']); + $route->post('alive', [UniProxyController::class, 'alive']); + $route->get('alivelist', [UniProxyController::class, 'alivelist']); + $route->post('status', [UniProxyController::class, 'status']); + }); + } +} diff --git a/Xboard/app/Http/Routes/V2/UserRoute.php b/Xboard/app/Http/Routes/V2/UserRoute.php new file mode 100644 index 0000000..38bc12f --- /dev/null +++ b/Xboard/app/Http/Routes/V2/UserRoute.php @@ -0,0 +1,20 @@ +group([ + 'prefix' => 'user', + 'middleware' => 'user' + ], function ($router) { + // User + $router->get('/resetSecurity', [UserController::class, 'resetSecurity']); + $router->get('/info', [UserController::class, 'info']); + }); + } +} diff --git a/Xboard/app/Jobs/NodeUserSyncJob.php b/Xboard/app/Jobs/NodeUserSyncJob.php new file mode 100644 index 0000000..f49e108 --- /dev/null +++ b/Xboard/app/Jobs/NodeUserSyncJob.php @@ -0,0 +1,45 @@ +onQueue('node_sync'); + } + + public function handle(): void + { + $user = User::find($this->userId); + + if ($this->action === 'updated' || $this->action === 'created') { + if ($this->oldGroupId) { + NodeSyncService::notifyUserRemovedFromGroup($this->userId, $this->oldGroupId); + } + if ($user) { + NodeSyncService::notifyUserChanged($user); + } + } elseif ($this->action === 'deleted') { + if ($this->oldGroupId) { + NodeSyncService::notifyUserRemovedFromGroup($this->userId, $this->oldGroupId); + } + } + } +} diff --git a/Xboard/app/Jobs/OrderHandleJob.php b/Xboard/app/Jobs/OrderHandleJob.php new file mode 100644 index 0000000..960f659 --- /dev/null +++ b/Xboard/app/Jobs/OrderHandleJob.php @@ -0,0 +1,56 @@ +onQueue('order_handle'); + $this->tradeNo = $tradeNo; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $order = Order::where('trade_no', $this->tradeNo) + ->lockForUpdate() + ->first(); + if (!$order) return; + $orderService = new OrderService($order); + switch ($order->status) { + // cancel + case Order::STATUS_PENDING: + if ($order->created_at <= (time() - 3600 * 2)) { + $orderService->cancel(); + } + break; + case Order::STATUS_PROCESSING: + $orderService->open(); + break; + } + } +} diff --git a/Xboard/app/Jobs/SendEmailJob.php b/Xboard/app/Jobs/SendEmailJob.php new file mode 100644 index 0000000..d998f41 --- /dev/null +++ b/Xboard/app/Jobs/SendEmailJob.php @@ -0,0 +1,42 @@ +onQueue($queue); + $this->params = $params; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $mailLog = MailService::sendEmail($this->params); + if ($mailLog['error']) { + $this->release(); //发送失败将触发重试 + } + } +} diff --git a/Xboard/app/Jobs/SendTelegramJob.php b/Xboard/app/Jobs/SendTelegramJob.php new file mode 100644 index 0000000..8d30344 --- /dev/null +++ b/Xboard/app/Jobs/SendTelegramJob.php @@ -0,0 +1,43 @@ +onQueue('send_telegram'); + $this->telegramId = $telegramId; + $this->text = $text; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + $telegramService = new TelegramService(); + $telegramService->sendMessage($this->telegramId, $this->text, 'markdown'); + } +} diff --git a/Xboard/app/Jobs/StatServerJob.php b/Xboard/app/Jobs/StatServerJob.php new file mode 100644 index 0000000..c055ca5 --- /dev/null +++ b/Xboard/app/Jobs/StatServerJob.php @@ -0,0 +1,174 @@ +onQueue('stat'); + $this->data = $data; + $this->server = $server; + $this->protocol = $protocol; + $this->recordType = $recordType; + } + + public function handle(): void + { + $recordAt = $this->recordType === 'm' + ? strtotime(date('Y-m-01')) + : strtotime(date('Y-m-d')); + + $u = $d = 0; + foreach ($this->data as $traffic) { + $u += $traffic[0]; + $d += $traffic[1]; + } + + try { + $this->processServerStat($u, $d, $recordAt); + $this->updateServerTraffic($u, $d); + } catch (\Exception $e) { + Log::error('StatServerJob failed for server ' . $this->server['id'] . ': ' . $e->getMessage()); + throw $e; + } + } + + protected function updateServerTraffic(int $u, int $d): void + { + DB::table('v2_server') + ->where('id', $this->server['id']) + ->incrementEach( + ['u' => $u, 'd' => $d], + ['updated_at' => Carbon::now()] + ); + } + + protected function processServerStat(int $u, int $d, int $recordAt): void + { + $driver = config('database.default'); + if ($driver === 'sqlite') { + $this->processServerStatForSqlite($u, $d, $recordAt); + } elseif ($driver === 'pgsql') { + $this->processServerStatForPostgres($u, $d, $recordAt); + } else { + $this->processServerStatForOtherDatabases($u, $d, $recordAt); + } + } + + protected function processServerStatForSqlite(int $u, int $d, int $recordAt): void + { + DB::transaction(function () use ($u, $d, $recordAt) { + $existingRecord = StatServer::where([ + 'record_at' => $recordAt, + 'server_id' => $this->server['id'], + 'server_type' => $this->protocol, + 'record_type' => $this->recordType, + ])->first(); + + if ($existingRecord) { + $existingRecord->update([ + 'u' => $existingRecord->u + $u, + 'd' => $existingRecord->d + $d, + 'updated_at' => time(), + ]); + } else { + StatServer::create([ + 'record_at' => $recordAt, + 'server_id' => $this->server['id'], + 'server_type' => $this->protocol, + 'record_type' => $this->recordType, + 'u' => $u, + 'd' => $d, + 'created_at' => time(), + 'updated_at' => time(), + ]); + } + }, 3); + } + + protected function processServerStatForOtherDatabases(int $u, int $d, int $recordAt): void + { + StatServer::upsert( + [ + 'record_at' => $recordAt, + 'server_id' => $this->server['id'], + 'server_type' => $this->protocol, + 'record_type' => $this->recordType, + 'u' => $u, + 'd' => $d, + 'created_at' => time(), + 'updated_at' => time(), + ], + ['server_id', 'server_type', 'record_at', 'record_type'], + [ + 'u' => DB::raw("u + VALUES(u)"), + 'd' => DB::raw("d + VALUES(d)"), + 'updated_at' => time(), + ] + ); + } + + /** + * PostgreSQL upsert with arithmetic increments using ON CONFLICT ... DO UPDATE + */ + protected function processServerStatForPostgres(int $u, int $d, int $recordAt): void + { + $table = (new StatServer())->getTable(); + $now = time(); + + // Use parameter binding to avoid SQL injection and keep maintainability + $sql = "INSERT INTO {$table} (record_at, server_id, server_type, record_type, u, d, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT (server_id, server_type, record_at) + DO UPDATE SET + u = {$table}.u + EXCLUDED.u, + d = {$table}.d + EXCLUDED.d, + updated_at = EXCLUDED.updated_at"; + + DB::statement($sql, [ + $recordAt, + $this->server['id'], + $this->protocol, + $this->recordType, + $u, + $d, + $now, + $now, + ]); + } +} diff --git a/Xboard/app/Jobs/StatUserJob.php b/Xboard/app/Jobs/StatUserJob.php new file mode 100644 index 0000000..620443c --- /dev/null +++ b/Xboard/app/Jobs/StatUserJob.php @@ -0,0 +1,158 @@ +onQueue('stat'); + $this->data = $data; + $this->server = $server; + $this->protocol = $protocol; + $this->recordType = $recordType; + } + + public function handle(): void + { + $recordAt = $this->recordType === 'm' + ? strtotime(date('Y-m-01')) + : strtotime(date('Y-m-d')); + + foreach ($this->data as $uid => $v) { + try { + $this->processUserStat($uid, $v, $recordAt); + } catch (\Exception $e) { + Log::error('StatUserJob failed for user ' . $uid . ': ' . $e->getMessage()); + throw $e; + } + } + } + + protected function processUserStat(int $uid, array $v, int $recordAt): void + { + $driver = config('database.default'); + if ($driver === 'sqlite') { + $this->processUserStatForSqlite($uid, $v, $recordAt); + } elseif ($driver === 'pgsql') { + $this->processUserStatForPostgres($uid, $v, $recordAt); + } else { + $this->processUserStatForOtherDatabases($uid, $v, $recordAt); + } + } + + protected function processUserStatForSqlite(int $uid, array $v, int $recordAt): void + { + DB::transaction(function () use ($uid, $v, $recordAt) { + $existingRecord = StatUser::where([ + 'user_id' => $uid, + 'server_rate' => $this->server['rate'], + 'record_at' => $recordAt, + 'record_type' => $this->recordType, + ])->first(); + + if ($existingRecord) { + $existingRecord->update([ + 'u' => $existingRecord->u + intval($v[0] * $this->server['rate']), + 'd' => $existingRecord->d + intval($v[1] * $this->server['rate']), + 'updated_at' => time(), + ]); + } else { + StatUser::create([ + 'user_id' => $uid, + 'server_rate' => $this->server['rate'], + 'record_at' => $recordAt, + 'record_type' => $this->recordType, + 'u' => intval($v[0] * $this->server['rate']), + 'd' => intval($v[1] * $this->server['rate']), + 'created_at' => time(), + 'updated_at' => time(), + ]); + } + }, 3); + } + + protected function processUserStatForOtherDatabases(int $uid, array $v, int $recordAt): void + { + StatUser::upsert( + [ + 'user_id' => $uid, + 'server_rate' => $this->server['rate'], + 'record_at' => $recordAt, + 'record_type' => $this->recordType, + 'u' => intval($v[0] * $this->server['rate']), + 'd' => intval($v[1] * $this->server['rate']), + 'created_at' => time(), + 'updated_at' => time(), + ], + ['user_id', 'server_rate', 'record_at', 'record_type'], + [ + 'u' => DB::raw("u + VALUES(u)"), + 'd' => DB::raw("d + VALUES(d)"), + 'updated_at' => time(), + ] + ); + } + + /** + * PostgreSQL upsert with arithmetic increments using ON CONFLICT ... DO UPDATE + */ + protected function processUserStatForPostgres(int $uid, array $v, int $recordAt): void + { + $table = (new StatUser())->getTable(); + $now = time(); + $u = intval($v[0] * $this->server['rate']); + $d = intval($v[1] * $this->server['rate']); + + $sql = "INSERT INTO {$table} (user_id, server_rate, record_at, record_type, u, d, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT (user_id, server_rate, record_at) + DO UPDATE SET + u = {$table}.u + EXCLUDED.u, + d = {$table}.d + EXCLUDED.d, + updated_at = EXCLUDED.updated_at"; + + DB::statement($sql, [ + $uid, + $this->server['rate'], + $recordAt, + $this->recordType, + $u, + $d, + $now, + $now, + ]); + } +} \ No newline at end of file diff --git a/Xboard/app/Jobs/TrafficFetchJob.php b/Xboard/app/Jobs/TrafficFetchJob.php new file mode 100644 index 0000000..1e398aa --- /dev/null +++ b/Xboard/app/Jobs/TrafficFetchJob.php @@ -0,0 +1,51 @@ +onQueue('traffic_fetch'); + $this->server = $server; + $this->data = $data; + $this->protocol = $protocol; + $this->timestamp = $timestamp; + } + + public function handle(): void + { + $userIds = array_keys($this->data); + + foreach ($this->data as $uid => $v) { + User::where('id', $uid) + ->incrementEach( + [ + 'u' => $v[0] * $this->server['rate'], + 'd' => $v[1] * $this->server['rate'], + ], + ['t' => time()] + ); + } + + if (!empty($userIds)) { + Redis::sadd('traffic:pending_check', ...$userIds); + } + } +} diff --git a/Xboard/app/Models/AdminAuditLog.php b/Xboard/app/Models/AdminAuditLog.php new file mode 100644 index 0000000..9e9404c --- /dev/null +++ b/Xboard/app/Models/AdminAuditLog.php @@ -0,0 +1,21 @@ + 'timestamp', + 'updated_at' => 'timestamp', + ]; + + public function admin() + { + return $this->belongsTo(User::class, 'admin_id'); + } +} diff --git a/Xboard/app/Models/CommissionLog.php b/Xboard/app/Models/CommissionLog.php new file mode 100644 index 0000000..744c5d5 --- /dev/null +++ b/Xboard/app/Models/CommissionLog.php @@ -0,0 +1,16 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; +} diff --git a/Xboard/app/Models/Coupon.php b/Xboard/app/Models/Coupon.php new file mode 100644 index 0000000..e6271b8 --- /dev/null +++ b/Xboard/app/Models/Coupon.php @@ -0,0 +1,28 @@ + 'timestamp', + 'updated_at' => 'timestamp', + 'limit_plan_ids' => 'array', + 'limit_period' => 'array', + 'show' => 'boolean', + ]; + + public function getLimitPeriodAttribute($value) + { + return collect(json_decode((string) $value, true))->map(function ($item) { + return PlanService::getPeriodKey($item); + })->toArray(); + } + +} diff --git a/Xboard/app/Models/GiftCardCode.php b/Xboard/app/Models/GiftCardCode.php new file mode 100644 index 0000000..dd09e8e --- /dev/null +++ b/Xboard/app/Models/GiftCardCode.php @@ -0,0 +1,260 @@ + 'timestamp', + 'updated_at' => 'timestamp', + 'used_at' => 'timestamp', + 'expires_at' => 'timestamp', + 'actual_rewards' => 'array', + 'metadata' => 'array' + ]; + + /** + * 获取状态映射 + */ + public static function getStatusMap(): array + { + return [ + self::STATUS_UNUSED => '未使用', + self::STATUS_USED => '已使用', + self::STATUS_EXPIRED => '已过期', + self::STATUS_DISABLED => '已禁用', + ]; + } + + /** + * 获取状态名称 + */ + public function getStatusNameAttribute(): string + { + return self::getStatusMap()[$this->status] ?? '未知状态'; + } + + /** + * 关联礼品卡模板 + */ + public function template(): BelongsTo + { + return $this->belongsTo(GiftCardTemplate::class, 'template_id'); + } + + /** + * 关联使用用户 + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * 关联使用记录 + */ + public function usages(): HasMany + { + return $this->hasMany(GiftCardUsage::class, 'code_id'); + } + + /** + * 检查是否可用 + */ + public function isAvailable(): bool + { + // 检查状态 + if (in_array($this->status, [self::STATUS_EXPIRED, self::STATUS_DISABLED])) { + return false; + } + + // 检查是否过期 + if ($this->expires_at && $this->expires_at < time()) { + return false; + } + + // 检查使用次数 + if ($this->usage_count >= $this->max_usage) { + return false; + } + + return true; + } + + /** + * 检查是否已过期 + */ + public function isExpired(): bool + { + return $this->expires_at && $this->expires_at < time(); + } + + /** + * 标记为已使用 + */ + public function markAsUsed(User $user): bool + { + $this->status = self::STATUS_USED; + $this->user_id = $user->id; + $this->used_at = time(); + $this->usage_count += 1; + + return $this->save(); + } + + /** + * 标记为已过期 + */ + public function markAsExpired(): bool + { + $this->status = self::STATUS_EXPIRED; + return $this->save(); + } + + /** + * 标记为已禁用 + */ + public function markAsDisabled(): bool + { + $this->status = self::STATUS_DISABLED; + return $this->save(); + } + + /** + * 生成兑换码 + */ + public static function generateCode(string $prefix = 'GC'): string + { + do { + $safePrefix = (string) $prefix; + $code = $safePrefix . strtoupper(substr(md5(uniqid($safePrefix . mt_rand(), true)), 0, 12)); + } while (self::where('code', $code)->exists()); + + return $code; + } + + /** + * 批量生成兑换码 + */ + public static function batchGenerate(int $templateId, int $count, array $options = []): string + { + $batchId = uniqid('batch_'); + $prefix = $options['prefix'] ?? 'GC'; + $expiresAt = $options['expires_at'] ?? null; + $maxUsage = $options['max_usage'] ?? 1; + + $codes = []; + for ($i = 0; $i < $count; $i++) { + $codes[] = [ + 'template_id' => $templateId, + 'code' => self::generateCode($prefix), + 'batch_id' => $batchId, + 'status' => self::STATUS_UNUSED, + 'expires_at' => $expiresAt, + 'max_usage' => $maxUsage, + 'created_at' => time(), + 'updated_at' => time(), + ]; + } + + self::insert($codes); + + return $batchId; + } + + /** + * 设置实际奖励(用于盲盒等) + */ + public function setActualRewards(array $rewards): bool + { + $this->actual_rewards = $rewards; + return $this->save(); + } + + /** + * 获取实际奖励 + */ + public function getActualRewards(): array + { + return $this->actual_rewards ?? $this->template->rewards ?? []; + } + + /** + * 检查兑换码格式 + */ + public static function validateCodeFormat(string $code): bool + { + // 基本格式验证:字母数字组合,长度8-32 + return preg_match('/^[A-Z0-9]{8,32}$/', $code); + } + + /** + * 根据批次ID获取兑换码 + */ + public static function getByBatchId(string $batchId) + { + return self::where('batch_id', $batchId)->get(); + } + + /** + * 清理过期兑换码 + */ + public static function cleanupExpired(): int + { + $count = self::where('status', self::STATUS_UNUSED) + ->where('expires_at', '<', time()) + ->count(); + + self::where('status', self::STATUS_UNUSED) + ->where('expires_at', '<', time()) + ->update(['status' => self::STATUS_EXPIRED]); + + return $count; + } +} \ No newline at end of file diff --git a/Xboard/app/Models/GiftCardTemplate.php b/Xboard/app/Models/GiftCardTemplate.php new file mode 100644 index 0000000..87a04de --- /dev/null +++ b/Xboard/app/Models/GiftCardTemplate.php @@ -0,0 +1,254 @@ + 'timestamp', + 'updated_at' => 'timestamp', + 'conditions' => 'array', + 'rewards' => 'array', + 'limits' => 'array', + 'special_config' => 'array', + 'status' => 'boolean' + ]; + + /** + * 获取卡片类型映射 + */ + public static function getTypeMap(): array + { + return [ + self::TYPE_GENERAL => '通用礼品卡', + self::TYPE_PLAN => '套餐礼品卡', + self::TYPE_MYSTERY => '盲盒礼品卡', + ]; + } + + /** + * 获取类型名称 + */ + public function getTypeNameAttribute(): string + { + return self::getTypeMap()[$this->type] ?? '未知类型'; + } + + /** + * 关联兑换码 + */ + public function codes(): HasMany + { + return $this->hasMany(GiftCardCode::class, 'template_id'); + } + + /** + * 关联使用记录 + */ + public function usages(): HasMany + { + return $this->hasMany(GiftCardUsage::class, 'template_id'); + } + + /** + * 关联统计数据 + */ + public function stats(): HasMany + { + return $this->hasMany(GiftCardUsage::class, 'template_id'); + } + + /** + * 检查是否可用 + */ + public function isAvailable(): bool + { + return $this->status; + } + + /** + * 检查用户是否满足使用条件 + */ + public function checkUserConditions(User $user): bool + { + switch ($this->type) { + case self::TYPE_GENERAL: + $rewards = $this->rewards ?? []; + if (isset($rewards['transfer_enable']) || isset($rewards['expire_days']) || isset($rewards['reset_package'])) { + if (!$user->plan_id) { + return false; + } + } + break; + case self::TYPE_PLAN: + if ($user->isActive()) { + return false; + } + break; + } + + $conditions = $this->conditions ?? []; + + // 检查新用户条件 + if (isset($conditions['new_user_only']) && $conditions['new_user_only']) { + $maxDays = $conditions['new_user_max_days'] ?? 7; + if ($user->created_at < (time() - ($maxDays * 86400))) { + return false; + } + } + + // 检查付费用户条件 + if (isset($conditions['paid_user_only']) && $conditions['paid_user_only']) { + $paidOrderExists = $user->orders()->where('status', Order::STATUS_COMPLETED)->exists(); + if (!$paidOrderExists) { + return false; + } + } + + // 检查允许的套餐 + if (isset($conditions['allowed_plans']) && $user->plan_id) { + if (!in_array($user->plan_id, $conditions['allowed_plans'])) { + return false; + } + } + + // 检查是否需要邀请人 + if (isset($conditions['require_invite']) && $conditions['require_invite']) { + if (!$user->invite_user_id) { + return false; + } + } + + return true; + } + + /** + * 计算实际奖励 + */ + public function calculateActualRewards(User $user): array + { + $baseRewards = $this->rewards; + $actualRewards = $baseRewards; + + // 处理盲盒随机奖励 + if ($this->type === self::TYPE_MYSTERY && isset($this->rewards['random_rewards'])) { + $randomRewards = $this->rewards['random_rewards']; + $totalWeight = array_sum(array_column($randomRewards, 'weight')); + $random = mt_rand(1, $totalWeight); + $currentWeight = 0; + + foreach ($randomRewards as $reward) { + $currentWeight += $reward['weight']; + if ($random <= $currentWeight) { + $actualRewards = array_merge($actualRewards, $reward); + unset($actualRewards['weight']); + break; + } + } + } + + // 处理节日等特殊奖励(通用逻辑) + if (isset($this->special_config['festival_bonus'])) { + $now = time(); + $festivalConfig = $this->special_config; + + if (isset($festivalConfig['start_time']) && isset($festivalConfig['end_time'])) { + if ($now >= $festivalConfig['start_time'] && $now <= $festivalConfig['end_time']) { + $bonus = data_get($festivalConfig, 'festival_bonus', 1.0); + if ($bonus > 1.0) { + foreach ($actualRewards as $key => &$value) { + if (is_numeric($value)) { + $value = intval($value * $bonus); + } + } + unset($value); // 解除引用 + } + } + } + } + + return $actualRewards; + } + + /** + * 检查使用频率限制 + */ + public function checkUsageLimit(User $user): bool + { + $limits = $this->limits ?? []; + + // 检查每用户最大使用次数 + if (isset($limits['max_use_per_user'])) { + $usedCount = $this->usages() + ->where('user_id', $user->id) + ->count(); + if ($usedCount >= $limits['max_use_per_user']) { + return false; + } + } + + // 检查冷却时间 + if (isset($limits['cooldown_hours'])) { + $lastUsage = $this->usages() + ->where('user_id', $user->id) + ->orderBy('created_at', 'desc') + ->first(); + + if ($lastUsage && isset($lastUsage->created_at)) { + $cooldownTime = $lastUsage->created_at + ($limits['cooldown_hours'] * 3600); + if (time() < $cooldownTime) { + return false; + } + } + } + + return true; + } +} \ No newline at end of file diff --git a/Xboard/app/Models/GiftCardUsage.php b/Xboard/app/Models/GiftCardUsage.php new file mode 100644 index 0000000..69337b9 --- /dev/null +++ b/Xboard/app/Models/GiftCardUsage.php @@ -0,0 +1,112 @@ + 'timestamp', + 'rewards_given' => 'array', + 'invite_rewards' => 'array', + 'multiplier_applied' => 'float' + ]; + + /** + * 关联兑换码 + */ + public function code(): BelongsTo + { + return $this->belongsTo(GiftCardCode::class, 'code_id'); + } + + /** + * 关联模板 + */ + public function template(): BelongsTo + { + return $this->belongsTo(GiftCardTemplate::class, 'template_id'); + } + + /** + * 关联使用用户 + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * 关联邀请人 + */ + public function inviteUser(): BelongsTo + { + return $this->belongsTo(User::class, 'invite_user_id'); + } + + /** + * 创建使用记录 + */ + public static function createRecord( + GiftCardCode $code, + User $user, + array $rewards, + array $options = [] + ): self { + return self::create([ + 'code_id' => $code->id, + 'template_id' => $code->template_id, + 'user_id' => $user->id, + 'invite_user_id' => $user->invite_user_id, + 'rewards_given' => $rewards, + 'invite_rewards' => $options['invite_rewards'] ?? null, + 'user_level_at_use' => $user->plan ? $user->plan->sort : null, + 'plan_id_at_use' => $user->plan_id, + 'multiplier_applied' => $options['multiplier'] ?? 1.0, + // 'ip_address' => $options['ip_address'] ?? null, + 'user_agent' => $options['user_agent'] ?? null, + 'notes' => $options['notes'] ?? null, + 'created_at' => time(), + ]); + } +} \ No newline at end of file diff --git a/Xboard/app/Models/InviteCode.php b/Xboard/app/Models/InviteCode.php new file mode 100644 index 0000000..7d2716b --- /dev/null +++ b/Xboard/app/Models/InviteCode.php @@ -0,0 +1,19 @@ + 'timestamp', + 'updated_at' => 'timestamp', + 'status' => 'boolean', + ]; + + const STATUS_UNUSED = 0; + const STATUS_USED = 1; +} diff --git a/Xboard/app/Models/Knowledge.php b/Xboard/app/Models/Knowledge.php new file mode 100644 index 0000000..91e7736 --- /dev/null +++ b/Xboard/app/Models/Knowledge.php @@ -0,0 +1,17 @@ + 'boolean', + 'created_at' => 'timestamp', + 'updated_at' => 'timestamp', + ]; +} diff --git a/Xboard/app/Models/MailLog.php b/Xboard/app/Models/MailLog.php new file mode 100644 index 0000000..0aef380 --- /dev/null +++ b/Xboard/app/Models/MailLog.php @@ -0,0 +1,16 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; +} diff --git a/Xboard/app/Models/Notice.php b/Xboard/app/Models/Notice.php new file mode 100644 index 0000000..3ae0cb0 --- /dev/null +++ b/Xboard/app/Models/Notice.php @@ -0,0 +1,18 @@ + 'timestamp', + 'updated_at' => 'timestamp', + 'tags' => 'array', + 'show' => 'boolean', + ]; +} diff --git a/Xboard/app/Models/Order.php b/Xboard/app/Models/Order.php new file mode 100644 index 0000000..dda0581 --- /dev/null +++ b/Xboard/app/Models/Order.php @@ -0,0 +1,120 @@ + $commission_log + */ +class Order extends Model +{ + protected $table = 'v2_order'; + protected $dateFormat = 'U'; + protected $guarded = ['id']; + protected $casts = [ + 'created_at' => 'timestamp', + 'updated_at' => 'timestamp', + 'surplus_order_ids' => 'array', + 'handling_amount' => 'integer' + ]; + + const STATUS_PENDING = 0; // 待支付 + const STATUS_PROCESSING = 1; // 开通中 + const STATUS_CANCELLED = 2; // 已取消 + const STATUS_COMPLETED = 3; // 已完成 + const STATUS_DISCOUNTED = 4; // 已折抵 + + public static $statusMap = [ + self::STATUS_PENDING => '待支付', + self::STATUS_PROCESSING => '开通中', + self::STATUS_CANCELLED => '已取消', + self::STATUS_COMPLETED => '已完成', + self::STATUS_DISCOUNTED => '已折抵', + ]; + + const TYPE_NEW_PURCHASE = 1; // 新购 + const TYPE_RENEWAL = 2; // 续费 + const TYPE_UPGRADE = 3; // 升级 + const TYPE_RESET_TRAFFIC = 4; //流量重置包 + public static $typeMap = [ + self::TYPE_NEW_PURCHASE => '新购', + self::TYPE_RENEWAL => '续费', + self::TYPE_UPGRADE => '升级', + self::TYPE_RESET_TRAFFIC => '流量重置', + ]; + + /** + * 获取与订单关联的支付方式 + */ + public function payment(): BelongsTo + { + return $this->belongsTo(Payment::class, 'payment_id', 'id'); + } + + /** + * 获取与订单关联的用户 + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + /** + * 获取邀请人 + */ + public function invite_user(): BelongsTo + { + return $this->belongsTo(User::class, 'invite_user_id', 'id'); + } + + /** + * 获取与订单关联的套餐 + */ + public function plan(): BelongsTo + { + return $this->belongsTo(Plan::class, 'plan_id', 'id'); + } + + /** + * 获取与订单关联的佣金记录 + */ + public function commission_log(): HasMany + { + return $this->hasMany(CommissionLog::class, 'trade_no', 'trade_no'); + } +} diff --git a/Xboard/app/Models/Payment.php b/Xboard/app/Models/Payment.php new file mode 100644 index 0000000..fec8b00 --- /dev/null +++ b/Xboard/app/Models/Payment.php @@ -0,0 +1,18 @@ + 'timestamp', + 'updated_at' => 'timestamp', + 'config' => 'array', + 'enable' => 'boolean' + ]; +} diff --git a/Xboard/app/Models/Plan.php b/Xboard/app/Models/Plan.php new file mode 100644 index 0000000..13d66f2 --- /dev/null +++ b/Xboard/app/Models/Plan.php @@ -0,0 +1,353 @@ + $order 关联的订单 + */ +class Plan extends Model +{ + use HasFactory; + + protected $table = 'v2_plan'; + protected $dateFormat = 'U'; + + // 定义流量重置方式 + public const RESET_TRAFFIC_FOLLOW_SYSTEM = null; // 跟随系统设置 + public const RESET_TRAFFIC_FIRST_DAY_MONTH = 0; // 每月1号 + public const RESET_TRAFFIC_MONTHLY = 1; // 按月重置 + public const RESET_TRAFFIC_NEVER = 2; // 不重置 + public const RESET_TRAFFIC_FIRST_DAY_YEAR = 3; // 每年1月1日 + public const RESET_TRAFFIC_YEARLY = 4; // 按年重置 + + // 定义价格类型 + public const PRICE_TYPE_RESET_TRAFFIC = 'reset_traffic'; // 重置流量价格 + + // 定义可用的订阅周期 + public const PERIOD_MONTHLY = 'monthly'; + public const PERIOD_QUARTERLY = 'quarterly'; + public const PERIOD_HALF_YEARLY = 'half_yearly'; + public const PERIOD_YEARLY = 'yearly'; + public const PERIOD_TWO_YEARLY = 'two_yearly'; + public const PERIOD_THREE_YEARLY = 'three_yearly'; + public const PERIOD_ONETIME = 'onetime'; + public const PERIOD_RESET_TRAFFIC = 'reset_traffic'; + + // 定义旧版周期映射 + public const LEGACY_PERIOD_MAPPING = [ + 'month_price' => self::PERIOD_MONTHLY, + 'quarter_price' => self::PERIOD_QUARTERLY, + 'half_year_price' => self::PERIOD_HALF_YEARLY, + 'year_price' => self::PERIOD_YEARLY, + 'two_year_price' => self::PERIOD_TWO_YEARLY, + 'three_year_price' => self::PERIOD_THREE_YEARLY, + 'onetime_price' => self::PERIOD_ONETIME, + 'reset_price' => self::PERIOD_RESET_TRAFFIC + ]; + + protected $fillable = [ + 'group_id', + 'transfer_enable', + 'name', + 'speed_limit', + 'show', + 'sort', + 'renew', + 'content', + 'prices', + 'reset_traffic_method', + 'capacity_limit', + 'sell', + 'device_limit', + 'tags' + ]; + + protected $casts = [ + 'show' => 'boolean', + 'renew' => 'boolean', + 'created_at' => 'timestamp', + 'updated_at' => 'timestamp', + 'group_id' => 'integer', + 'prices' => 'array', + 'tags' => 'array', + 'reset_traffic_method' => 'integer', + ]; + + /** + * 获取所有可用的流量重置方式 + * + * @return array + */ + public static function getResetTrafficMethods(): array + { + return [ + self::RESET_TRAFFIC_FOLLOW_SYSTEM => '跟随系统设置', + self::RESET_TRAFFIC_FIRST_DAY_MONTH => '每月1号', + self::RESET_TRAFFIC_MONTHLY => '按月重置', + self::RESET_TRAFFIC_NEVER => '不重置', + self::RESET_TRAFFIC_FIRST_DAY_YEAR => '每年1月1日', + self::RESET_TRAFFIC_YEARLY => '按年重置', + ]; + } + + /** + * 获取所有可用的订阅周期 + * + * @return array + */ + public static function getAvailablePeriods(): array + { + return [ + self::PERIOD_MONTHLY => [ + 'name' => '月付', + 'days' => 30, + 'value' => 1 + ], + self::PERIOD_QUARTERLY => [ + 'name' => '季付', + 'days' => 90, + 'value' => 3 + ], + self::PERIOD_HALF_YEARLY => [ + 'name' => '半年付', + 'days' => 180, + 'value' => 6 + ], + self::PERIOD_YEARLY => [ + 'name' => '年付', + 'days' => 365, + 'value' => 12 + ], + self::PERIOD_TWO_YEARLY => [ + 'name' => '两年付', + 'days' => 730, + 'value' => 24 + ], + self::PERIOD_THREE_YEARLY => [ + 'name' => '三年付', + 'days' => 1095, + 'value' => 36 + ], + self::PERIOD_ONETIME => [ + 'name' => '一次性', + 'days' => -1, + 'value' => -1 + ], + self::PERIOD_RESET_TRAFFIC => [ + 'name' => '重置流量', + 'days' => -1, + 'value' => -1 + ], + ]; + } + + /** + * 获取指定周期的价格 + * + * @param string $period + * @return int|null + */ + public function getPriceByPeriod(string $period): ?int + { + return $this->prices[$period] ?? null; + } + + /** + * 获取所有已设置价格的周期 + * + * @return array + */ + public function getActivePeriods(): array + { + return array_filter( + self::getAvailablePeriods(), + fn($period) => isset($this->prices[$period]) + && $this->prices[$period] > 0, + ARRAY_FILTER_USE_KEY + ); + } + + /** + * 设置指定周期的价格 + * + * @param string $period + * @param int $price + * @return void + * @throws InvalidArgumentException + */ + public function setPeriodPrice(string $period, int $price): void + { + if (!array_key_exists($period, self::getAvailablePeriods())) { + throw new InvalidArgumentException("Invalid period: {$period}"); + } + + $prices = $this->prices ?? []; + $prices[$period] = $price; + $this->prices = $prices; + } + + /** + * 移除指定周期的价格 + * + * @param string $period + * @return void + */ + public function removePeriodPrice(string $period): void + { + $prices = $this->prices ?? []; + unset($prices[$period]); + $this->prices = $prices; + } + + /** + * 获取所有价格及其对应的周期信息 + * + * @return array + */ + public function getPriceList(): array + { + $prices = $this->prices ?? []; + $periods = self::getAvailablePeriods(); + + $priceList = []; + foreach ($prices as $period => $price) { + if (isset($periods[$period]) && $price > 0) { + $priceList[$period] = [ + 'period' => $periods[$period], + 'price' => $price, + 'average_price' => $periods[$period]['value'] > 0 + ? round($price / $periods[$period]['value'], 2) + : $price + ]; + } + } + + return $priceList; + } + + /** + * 检查是否可以重置流量 + * + * @return bool + */ + public function canResetTraffic(): bool + { + return $this->reset_traffic_method !== self::RESET_TRAFFIC_NEVER + && $this->getResetTrafficPrice() > 0; + } + + /** + * 获取重置流量的价格 + * + * @return int + */ + public function getResetTrafficPrice(): int + { + return $this->prices[self::PRICE_TYPE_RESET_TRAFFIC] ?? 0; + } + + /** + * 计算指定周期的有效天数 + * + * @param string $period + * @return int -1表示永久有效 + * @throws InvalidArgumentException + */ + public static function getPeriodDays(string $period): int + { + $periods = self::getAvailablePeriods(); + if (!isset($periods[$period])) { + throw new InvalidArgumentException("Invalid period: {$period}"); + } + + return $periods[$period]['days']; + } + + /** + * 检查周期是否有效 + * + * @param string $period + * @return bool + */ + public static function isValidPeriod(string $period): bool + { + return array_key_exists($period, self::getAvailablePeriods()); + } + + public function users(): HasMany + { + return $this->hasMany(User::class); + } + + public function group(): HasOne + { + return $this->hasOne(ServerGroup::class, 'id', 'group_id'); + } + + public function orders(): HasMany + { + return $this->hasMany(Order::class); + } + + /** + * 设置流量重置方式 + * + * @param int $method + * @return void + * @throws InvalidArgumentException + */ + public function setResetTrafficMethod(int $method): void + { + if (!array_key_exists($method, self::getResetTrafficMethods())) { + throw new InvalidArgumentException("Invalid reset traffic method: {$method}"); + } + + $this->reset_traffic_method = $method; + } + + /** + * 设置重置流量价格 + * + * @param int $price + * @return void + */ + public function setResetTrafficPrice(int $price): void + { + $prices = $this->prices ?? []; + $prices[self::PRICE_TYPE_RESET_TRAFFIC] = max(0, $price); + $this->prices = $prices; + } + + public function order(): HasMany + { + return $this->hasMany(Order::class); + } +} \ No newline at end of file diff --git a/Xboard/app/Models/Plugin.php b/Xboard/app/Models/Plugin.php new file mode 100644 index 0000000..0425558 --- /dev/null +++ b/Xboard/app/Models/Plugin.php @@ -0,0 +1,77 @@ + 'boolean', + ]; + + public function scopeByType(Builder $query, string $type): Builder + { + return $query->where('type', $type); + } + + public function isFeaturePlugin(): bool + { + return $this->type === self::TYPE_FEATURE; + } + + public function isPaymentPlugin(): bool + { + return $this->type === self::TYPE_PAYMENT; + } + + public function isProtected(): bool + { + return in_array($this->code, self::PROTECTED_PLUGINS); + } + + public function canBeDeleted(): bool + { + return !$this->isProtected(); + } + +} diff --git a/Xboard/app/Models/Server.php b/Xboard/app/Models/Server.php new file mode 100644 index 0000000..19e2b5e --- /dev/null +++ b/Xboard/app/Models/Server.php @@ -0,0 +1,563 @@ + $stats 节点统计 + * + * @property-read int|null $last_check_at 最后检查时间(Unix时间戳) + * @property-read int|null $last_push_at 最后推送时间(Unix时间戳) + * @property-read int $online 在线用户数 + * @property-read int $online_conn 在线连接数 + * @property-read array|null $metrics 节点指标指标 + * @property-read int $is_online 是否在线(1在线 0离线) + * @property-read string $available_status 可用状态描述 + * @property-read string $cache_key 缓存键 + * @property string|null $ports 端口范围 + * @property string|null $password 密码 + * @property int|null $u 上行流量 + * @property int|null $d 下行流量 + * @property int|null $total 总流量 + * @property-read array|null $load_status 负载状态(包含CPU、内存、交换区、磁盘信息) + * + * @property int $transfer_enable 流量上限,0或者null表示不限制 + * @property int $u 当前上传流量 + * @property int $d 当前下载流量 + */ +class Server extends Model +{ + public const TYPE_HYSTERIA = 'hysteria'; + public const TYPE_VLESS = 'vless'; + public const TYPE_TROJAN = 'trojan'; + public const TYPE_VMESS = 'vmess'; + public const TYPE_TUIC = 'tuic'; + public const TYPE_SHADOWSOCKS = 'shadowsocks'; + public const TYPE_ANYTLS = 'anytls'; + public const TYPE_SOCKS = 'socks'; + public const TYPE_NAIVE = 'naive'; + public const TYPE_HTTP = 'http'; + public const TYPE_MIERU = 'mieru'; + public const STATUS_OFFLINE = 0; + public const STATUS_ONLINE_NO_PUSH = 1; + public const STATUS_ONLINE = 2; + + public const CHECK_INTERVAL = 300; // 5 minutes in seconds + + private const CIPHER_CONFIGURATIONS = [ + '2022-blake3-aes-128-gcm' => [ + 'serverKeySize' => 16, + 'userKeySize' => 16, + ], + '2022-blake3-aes-256-gcm' => [ + 'serverKeySize' => 32, + 'userKeySize' => 32, + ], + '2022-blake3-chacha20-poly1305' => [ + 'serverKeySize' => 32, + 'userKeySize' => 32, + ] + ]; + + public const TYPE_ALIASES = [ + 'v2ray' => self::TYPE_VMESS, + 'hysteria2' => self::TYPE_HYSTERIA, + ]; + + public const VALID_TYPES = [ + self::TYPE_HYSTERIA, + self::TYPE_VLESS, + self::TYPE_TROJAN, + self::TYPE_VMESS, + self::TYPE_TUIC, + self::TYPE_SHADOWSOCKS, + self::TYPE_ANYTLS, + self::TYPE_SOCKS, + self::TYPE_NAIVE, + self::TYPE_HTTP, + self::TYPE_MIERU, + ]; + + protected $table = 'v2_server'; + + protected $guarded = ['id']; + protected $casts = [ + 'group_ids' => 'array', + 'route_ids' => 'array', + 'tags' => 'array', + 'protocol_settings' => 'array', + 'custom_outbounds' => 'array', + 'custom_routes' => 'array', + 'cert_config' => 'array', + 'last_check_at' => 'integer', + 'last_push_at' => 'integer', + 'show' => 'boolean', + 'created_at' => 'timestamp', + 'updated_at' => 'timestamp', + 'rate_time_ranges' => 'array', + 'rate_time_enable' => 'boolean', + 'transfer_enable' => 'integer', + 'u' => 'integer', + 'd' => 'integer', + ]; + + private const MULTIPLEX_CONFIGURATION = [ + 'multiplex' => [ + 'type' => 'object', + 'fields' => [ + 'enabled' => ['type' => 'boolean', 'default' => false], + 'protocol' => ['type' => 'string', 'default' => 'yamux'], + 'max_connections' => ['type' => 'integer', 'default' => null], + // 'min_streams' => ['type' => 'integer', 'default' => null], + // 'max_streams' => ['type' => 'integer', 'default' => null], + 'padding' => ['type' => 'boolean', 'default' => false], + 'brutal' => [ + 'type' => 'object', + 'fields' => [ + 'enabled' => ['type' => 'boolean', 'default' => false], + 'up_mbps' => ['type' => 'integer', 'default' => null], + 'down_mbps' => ['type' => 'integer', 'default' => null], + ] + ] + ] + ] + ]; + + private const REALITY_CONFIGURATION = [ + 'reality_settings' => [ + 'type' => 'object', + 'fields' => [ + 'server_name' => ['type' => 'string', 'default' => null], + 'server_port' => ['type' => 'string', 'default' => null], + 'public_key' => ['type' => 'string', 'default' => null], + 'private_key' => ['type' => 'string', 'default' => null], + 'short_id' => ['type' => 'string', 'default' => null], + 'allow_insecure' => ['type' => 'boolean', 'default' => false], + ] + ] + ]; + + private const UTLS_CONFIGURATION = [ + 'utls' => [ + 'type' => 'object', + 'fields' => [ + 'enabled' => ['type' => 'boolean', 'default' => false], + 'fingerprint' => ['type' => 'string', 'default' => 'chrome'], + ] + ] + ]; + + private const PROTOCOL_CONFIGURATIONS = [ + self::TYPE_TROJAN => [ + 'tls' => ['type' => 'integer', 'default' => 1], + 'network' => ['type' => 'string', 'default' => null], + 'network_settings' => ['type' => 'array', 'default' => null], + 'server_name' => ['type' => 'string', 'default' => null], + 'allow_insecure' => ['type' => 'boolean', 'default' => false], + ...self::REALITY_CONFIGURATION, + ...self::MULTIPLEX_CONFIGURATION, + ...self::UTLS_CONFIGURATION + ], + self::TYPE_VMESS => [ + 'tls' => ['type' => 'integer', 'default' => 0], + 'network' => ['type' => 'string', 'default' => null], + 'rules' => ['type' => 'array', 'default' => null], + 'network_settings' => ['type' => 'array', 'default' => null], + 'tls_settings' => ['type' => 'array', 'default' => null], + ...self::MULTIPLEX_CONFIGURATION, + ...self::UTLS_CONFIGURATION + ], + self::TYPE_VLESS => [ + 'tls' => ['type' => 'integer', 'default' => 0], + 'tls_settings' => ['type' => 'array', 'default' => null], + 'flow' => ['type' => 'string', 'default' => null], + 'encryption' => [ + 'type' => 'object', + 'default' => null, + 'fields' => [ + 'enabled' => ['type' => 'boolean', 'default' => false], + 'encryption' => ['type' => 'string', 'default' => null], // 客户端公钥 + 'decryption' => ['type' => 'string', 'default' => null], // 服务端私钥 + ] + ], + 'network' => ['type' => 'string', 'default' => null], + 'network_settings' => ['type' => 'array', 'default' => null], + ...self::REALITY_CONFIGURATION, + ...self::MULTIPLEX_CONFIGURATION, + ...self::UTLS_CONFIGURATION + ], + self::TYPE_SHADOWSOCKS => [ + 'cipher' => ['type' => 'string', 'default' => null], + 'obfs' => ['type' => 'string', 'default' => null], + 'obfs_settings' => ['type' => 'array', 'default' => null], + 'plugin' => ['type' => 'string', 'default' => null], + 'plugin_opts' => ['type' => 'string', 'default' => null] + ], + self::TYPE_HYSTERIA => [ + 'version' => ['type' => 'integer', 'default' => 2], + 'bandwidth' => [ + 'type' => 'object', + 'fields' => [ + 'up' => ['type' => 'integer', 'default' => null], + 'down' => ['type' => 'integer', 'default' => null] + ] + ], + 'obfs' => [ + 'type' => 'object', + 'fields' => [ + 'open' => ['type' => 'boolean', 'default' => false], + 'type' => ['type' => 'string', 'default' => 'salamander'], + 'password' => ['type' => 'string', 'default' => null] + ] + ], + 'tls' => [ + 'type' => 'object', + 'fields' => [ + 'server_name' => ['type' => 'string', 'default' => null], + 'allow_insecure' => ['type' => 'boolean', 'default' => false] + ] + ], + 'hop_interval' => ['type' => 'integer', 'default' => null] + ], + self::TYPE_TUIC => [ + 'version' => ['type' => 'integer', 'default' => 5], + 'congestion_control' => ['type' => 'string', 'default' => 'cubic'], + 'alpn' => ['type' => 'array', 'default' => ['h3']], + 'udp_relay_mode' => ['type' => 'string', 'default' => 'native'], + 'tls' => [ + 'type' => 'object', + 'fields' => [ + 'server_name' => ['type' => 'string', 'default' => null], + 'allow_insecure' => ['type' => 'boolean', 'default' => false] + ] + ] + ], + self::TYPE_ANYTLS => [ + 'padding_scheme' => [ + 'type' => 'array', + 'default' => [ + "stop=8", + "0=30-30", + "1=100-400", + "2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000", + "3=9-9,500-1000", + "4=500-1000", + "5=500-1000", + "6=500-1000", + "7=500-1000" + ] + ], + 'tls' => [ + 'type' => 'object', + 'fields' => [ + 'server_name' => ['type' => 'string', 'default' => null], + 'allow_insecure' => ['type' => 'boolean', 'default' => false] + ] + ] + ], + self::TYPE_SOCKS => [ + 'tls' => ['type' => 'integer', 'default' => 0], + 'tls_settings' => [ + 'type' => 'object', + 'fields' => [ + 'allow_insecure' => ['type' => 'boolean', 'default' => false] + ] + ] + ], + self::TYPE_NAIVE => [ + 'tls' => ['type' => 'integer', 'default' => 0], + 'tls_settings' => ['type' => 'array', 'default' => null] + ], + self::TYPE_HTTP => [ + 'tls' => ['type' => 'integer', 'default' => 0], + 'tls_settings' => [ + 'type' => 'object', + 'fields' => [ + 'allow_insecure' => ['type' => 'boolean', 'default' => false], + 'server_name' => ['type' => 'string', 'default' => null] + ] + ] + ], + self::TYPE_MIERU => [ + 'transport' => ['type' => 'string', 'default' => 'TCP'], + 'traffic_pattern' => ['type' => 'string', 'default' => ''], + ...self::MULTIPLEX_CONFIGURATION, + ] + ]; + + private function castValueWithConfig($value, array $config) + { + if ($value === null && $config['type'] !== 'object') { + return $config['default'] ?? null; + } + + return match ($config['type']) { + 'integer' => (int) $value, + 'boolean' => (bool) $value, + 'string' => (string) $value, + 'array' => (array) $value, + 'object' => is_array($value) ? + $this->castSettingsWithConfig($value, $config['fields']) : + $config['default'] ?? null, + default => $value + }; + } + + private function castSettingsWithConfig(array $settings, array $configs): array + { + $result = []; + foreach ($configs as $key => $config) { + $value = $settings[$key] ?? null; + $result[$key] = $this->castValueWithConfig($value, $config); + } + return $result; + } + + public function getProtocolSettingsAttribute($value) + { + $settings = json_decode($value, true) ?? []; + $configs = self::PROTOCOL_CONFIGURATIONS[$this->type] ?? []; + return $this->castSettingsWithConfig($settings, $configs); + } + + public function setProtocolSettingsAttribute($value) + { + if (is_string($value)) { + $value = json_decode($value, true); + } + + $configs = self::PROTOCOL_CONFIGURATIONS[$this->type] ?? []; + $castedSettings = $this->castSettingsWithConfig($value ?? [], $configs); + + $this->attributes['protocol_settings'] = json_encode($castedSettings); + } + + public function generateServerPassword(User $user): string + { + if ($this->type !== self::TYPE_SHADOWSOCKS) { + return $user->uuid; + } + + + $cipher = data_get($this, 'protocol_settings.cipher'); + if (!$cipher || !isset(self::CIPHER_CONFIGURATIONS[$cipher])) { + return $user->uuid; + } + + $config = self::CIPHER_CONFIGURATIONS[$cipher]; + // Use parent's created_at if this is a child node + $serverCreatedAt = $this->parent_id ? $this->parent->created_at : $this->created_at; + $serverKey = Helper::getServerKey($serverCreatedAt, $config['serverKeySize']); + $userKey = Helper::uuidToBase64($user->uuid, $config['userKeySize']); + return "{$serverKey}:{$userKey}"; + } + + public static function normalizeType(?string $type): string | null + { + return $type ? strtolower(self::TYPE_ALIASES[$type] ?? $type) : null; + } + + public static function isValidType(?string $type): bool + { + return $type ? in_array(self::normalizeType($type), self::VALID_TYPES, true) : true; + } + + public function getAvailableStatusAttribute(): int + { + $now = time(); + if (!$this->last_check_at || ($now - self::CHECK_INTERVAL) >= $this->last_check_at) { + return self::STATUS_OFFLINE; + } + if (!$this->last_push_at || ($now - self::CHECK_INTERVAL) >= $this->last_push_at) { + return self::STATUS_ONLINE_NO_PUSH; + } + return self::STATUS_ONLINE; + } + + public function parent(): BelongsTo + { + return $this->belongsTo(self::class, 'parent_id', 'id'); + } + + public function stats(): HasMany + { + return $this->hasMany(StatServer::class, 'server_id', 'id'); + } + + public function groups() + { + return ServerGroup::whereIn('id', $this->group_ids)->get(); + } + + public function routes() + { + return ServerRoute::whereIn('id', $this->route_ids)->get(); + } + + /** + * 最后检查时间访问器 + */ + protected function lastCheckAt(): Attribute + { + return Attribute::make( + get: function () { + $type = strtoupper($this->type); + $serverId = $this->parent_id ?: $this->id; + return Cache::get(CacheKey::get("SERVER_{$type}_LAST_CHECK_AT", $serverId)); + } + ); + } + + /** + * 最后推送时间访问器 + */ + protected function lastPushAt(): Attribute + { + return Attribute::make( + get: function () { + $type = strtoupper($this->type); + $serverId = $this->parent_id ?: $this->id; + return Cache::get(CacheKey::get("SERVER_{$type}_LAST_PUSH_AT", $serverId)); + } + ); + } + + /** + * 在线用户数访问器 + */ + protected function online(): Attribute + { + return Attribute::make( + get: function () { + $type = strtoupper($this->type); + $serverId = $this->parent_id ?: $this->id; + return Cache::get(CacheKey::get("SERVER_{$type}_ONLINE_USER", $serverId)) ?? 0; + } + ); + } + + /** + * 是否在线访问器 + */ + protected function isOnline(): Attribute + { + return Attribute::make( + get: function () { + return (time() - 300 > $this->last_check_at) ? 0 : 1; + } + ); + } + + /** + * 缓存键访问器 + */ + protected function cacheKey(): Attribute + { + return Attribute::make( + get: function () { + return "{$this->type}-{$this->id}-{$this->updated_at}-{$this->is_online}"; + } + ); + } + + /** + * 服务器密钥访问器 + */ + protected function serverKey(): Attribute + { + return Attribute::make( + get: function () { + if ($this->type === self::TYPE_SHADOWSOCKS) { + return Helper::getServerKey($this->created_at, 16); + } + return null; + } + ); + } + + /** + * 指标指标访问器 + */ + protected function metrics(): Attribute + { + return Attribute::make( + get: function () { + $type = strtoupper($this->type); + $serverId = $this->parent_id ?: $this->id; + return Cache::get(CacheKey::get("SERVER_{$type}_METRICS", $serverId)); + } + ); + } + + /** + * 在线连接数访问器 + */ + protected function onlineConn(): Attribute + { + return Attribute::make( + get: function () { + return $this->metrics['active_connections'] ?? 0; + } + ); + } + + /** + * 负载状态访问器 + */ + protected function loadStatus(): Attribute + { + return Attribute::make( + get: function () { + $type = strtoupper($this->type); + $serverId = $this->parent_id ?: $this->id; + return Cache::get(CacheKey::get("SERVER_{$type}_LOAD_STATUS", $serverId)); + } + ); + } + + public function getCurrentRate(): float + { + if (!$this->rate_time_enable) { + return (float) $this->rate; + } + + $now = now()->format('H:i'); + $ranges = $this->rate_time_ranges ?? []; + $matchedRange = collect($ranges) + ->first(fn($range) => $now >= $range['start'] && $now <= $range['end']); + + return $matchedRange ? (float) $matchedRange['rate'] : (float) $this->rate; + } +} diff --git a/Xboard/app/Models/ServerGroup.php b/Xboard/app/Models/ServerGroup.php new file mode 100644 index 0000000..57f3514 --- /dev/null +++ b/Xboard/app/Models/ServerGroup.php @@ -0,0 +1,46 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; + + public function users(): HasMany + { + return $this->hasMany(User::class, 'group_id', 'id'); + } + + public function servers() + { + return Server::whereJsonContains('group_ids', (string) $this->id)->get(); + } + + /** + * 获取服务器数量 + */ + protected function serverCount(): Attribute + { + return Attribute::make( + get: fn () => Server::whereJsonContains('group_ids', (string) $this->id)->count(), + ); + } +} diff --git a/Xboard/app/Models/ServerLog.php b/Xboard/app/Models/ServerLog.php new file mode 100644 index 0000000..ef3590c --- /dev/null +++ b/Xboard/app/Models/ServerLog.php @@ -0,0 +1,16 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; +} diff --git a/Xboard/app/Models/ServerRoute.php b/Xboard/app/Models/ServerRoute.php new file mode 100644 index 0000000..024a7e4 --- /dev/null +++ b/Xboard/app/Models/ServerRoute.php @@ -0,0 +1,17 @@ + 'timestamp', + 'updated_at' => 'timestamp', + 'match' => 'array' + ]; +} diff --git a/Xboard/app/Models/ServerStat.php b/Xboard/app/Models/ServerStat.php new file mode 100644 index 0000000..5006ded --- /dev/null +++ b/Xboard/app/Models/ServerStat.php @@ -0,0 +1,16 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; +} diff --git a/Xboard/app/Models/Setting.php b/Xboard/app/Models/Setting.php new file mode 100644 index 0000000..b24a471 --- /dev/null +++ b/Xboard/app/Models/Setting.php @@ -0,0 +1,68 @@ + 'string', + 'value' => 'string', + ]; + + /** + * 获取实际内容值 + */ + public function getContentValue() + { + $rawValue = $this->attributes['value'] ?? null; + + if ($rawValue === null) { + return null; + } + + // 如果已经是数组,直接返回 + if (is_array($rawValue)) { + return $rawValue; + } + + // 如果是数字字符串,返回原值 + if (is_numeric($rawValue) && !preg_match('/[^\d.]/', $rawValue)) { + return $rawValue; + } + + // 尝试解析 JSON + if (is_string($rawValue)) { + $decodedValue = json_decode($rawValue, true); + if (json_last_error() === JSON_ERROR_NONE) { + return $decodedValue; + } + } + + return $rawValue; + } + + /** + * 兼容性:保持原有的 value 访问器 + */ + public function getValueAttribute($value) + { + return $this->getContentValue(); + } + + /** + * 创建或更新设置项 + */ + public static function createOrUpdate(string $name, $value): self + { + $processedValue = is_array($value) ? json_encode($value) : $value; + + return self::updateOrCreate( + ['name' => $name], + ['value' => $processedValue] + ); + } +} diff --git a/Xboard/app/Models/Stat.php b/Xboard/app/Models/Stat.php new file mode 100644 index 0000000..b0ed9ec --- /dev/null +++ b/Xboard/app/Models/Stat.php @@ -0,0 +1,16 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; +} diff --git a/Xboard/app/Models/StatServer.php b/Xboard/app/Models/StatServer.php new file mode 100644 index 0000000..efa1fc6 --- /dev/null +++ b/Xboard/app/Models/StatServer.php @@ -0,0 +1,33 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; + + public function server() + { + return $this->belongsTo(Server::class, 'server_id'); + } +} diff --git a/Xboard/app/Models/StatUser.php b/Xboard/app/Models/StatUser.php new file mode 100644 index 0000000..a956bd7 --- /dev/null +++ b/Xboard/app/Models/StatUser.php @@ -0,0 +1,28 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; +} diff --git a/Xboard/app/Models/SubscribeTemplate.php b/Xboard/app/Models/SubscribeTemplate.php new file mode 100644 index 0000000..fec76e0 --- /dev/null +++ b/Xboard/app/Models/SubscribeTemplate.php @@ -0,0 +1,46 @@ + 'string', + 'content' => 'string', + ]; + + private static string $cachePrefix = 'subscribe_template:'; + + public static function getContent(string $name): ?string + { + $cacheKey = self::$cachePrefix . $name; + + return Cache::store('redis')->remember($cacheKey, 3600, function () use ($name) { + return self::where('name', $name)->value('content'); + }); + } + + public static function setContent(string $name, ?string $content): void + { + self::updateOrCreate( + ['name' => $name], + ['content' => $content] + ); + Cache::store('redis')->forget(self::$cachePrefix . $name); + } + + public static function getAllContents(): array + { + return self::pluck('content', 'name')->toArray(); + } + + public static function flushCache(string $name): void + { + Cache::store('redis')->forget(self::$cachePrefix . $name); + } +} diff --git a/Xboard/app/Models/Ticket.php b/Xboard/app/Models/Ticket.php new file mode 100644 index 0000000..86ba9b6 --- /dev/null +++ b/Xboard/app/Models/Ticket.php @@ -0,0 +1,60 @@ + $messages 关联的工单消息 + */ +class Ticket extends Model +{ + protected $table = 'v2_ticket'; + protected $dateFormat = 'U'; + protected $guarded = ['id']; + protected $casts = [ + 'created_at' => 'timestamp', + 'updated_at' => 'timestamp' + ]; + + const STATUS_OPENING = 0; + const STATUS_CLOSED = 1; + public static $statusMap = [ + self::STATUS_OPENING => '开启', + self::STATUS_CLOSED => '关闭' + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + /** + * 关联的工单消息 + */ + public function messages(): HasMany + { + return $this->hasMany(TicketMessage::class, 'ticket_id', 'id'); + } + + // 即将删除 + public function message(): HasMany + { + return $this->hasMany(TicketMessage::class, 'ticket_id', 'id'); + } +} diff --git a/Xboard/app/Models/TicketMessage.php b/Xboard/app/Models/TicketMessage.php new file mode 100644 index 0000000..c9d45a8 --- /dev/null +++ b/Xboard/app/Models/TicketMessage.php @@ -0,0 +1,56 @@ + 'timestamp', + 'updated_at' => 'timestamp' + ]; + + protected $appends = ['is_from_user', 'is_from_admin']; + + /** + * 关联的工单 + */ + public function ticket(): BelongsTo + { + return $this->belongsTo(Ticket::class, 'ticket_id', 'id'); + } + + /** + * 判断消息是否由工单发起人发送 + */ + public function getIsFromUserAttribute(): bool + { + return $this->ticket->user_id === $this->user_id; + } + + /** + * 判断消息是否由管理员发送 + */ + public function getIsFromAdminAttribute(): bool + { + return $this->ticket->user_id !== $this->user_id; + } +} diff --git a/Xboard/app/Models/TrafficResetLog.php b/Xboard/app/Models/TrafficResetLog.php new file mode 100644 index 0000000..3b05a84 --- /dev/null +++ b/Xboard/app/Models/TrafficResetLog.php @@ -0,0 +1,149 @@ + 'datetime', + 'metadata' => 'array', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + // 重置类型常量 + public const TYPE_MONTHLY = 'monthly'; + public const TYPE_FIRST_DAY_MONTH = 'first_day_month'; + public const TYPE_YEARLY = 'yearly'; + public const TYPE_FIRST_DAY_YEAR = 'first_day_year'; + public const TYPE_MANUAL = 'manual'; + public const TYPE_PURCHASE = 'purchase'; + + // 触发来源常量 + public const SOURCE_AUTO = 'auto'; + public const SOURCE_MANUAL = 'manual'; + public const SOURCE_API = 'api'; + public const SOURCE_CRON = 'cron'; + public const SOURCE_USER_ACCESS = 'user_access'; + public const SOURCE_ORDER = 'order'; + public const SOURCE_GIFT_CARD = 'gift_card'; + + /** + * 获取重置类型的多语言名称 + */ + public static function getResetTypeNames(): array + { + return [ + self::TYPE_MONTHLY => __('traffic_reset.reset_type.monthly'), + self::TYPE_FIRST_DAY_MONTH => __('traffic_reset.reset_type.first_day_month'), + self::TYPE_YEARLY => __('traffic_reset.reset_type.yearly'), + self::TYPE_FIRST_DAY_YEAR => __('traffic_reset.reset_type.first_day_year'), + self::TYPE_MANUAL => __('traffic_reset.reset_type.manual'), + self::TYPE_PURCHASE => __('traffic_reset.reset_type.purchase'), + ]; + } + + /** + * 获取触发来源的多语言名称 + */ + public static function getSourceNames(): array + { + return [ + self::SOURCE_AUTO => __('traffic_reset.source.auto'), + self::SOURCE_MANUAL => __('traffic_reset.source.manual'), + self::SOURCE_API => __('traffic_reset.source.api'), + self::SOURCE_CRON => __('traffic_reset.source.cron'), + self::SOURCE_USER_ACCESS => __('traffic_reset.source.user_access'), + ]; + } + + /** + * 关联用户 + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id', 'id'); + } + + /** + * 获取重置类型名称 + */ + public function getResetTypeName(): string + { + return self::getResetTypeNames()[$this->reset_type] ?? $this->reset_type; + } + + /** + * 获取触发来源名称 + */ + public function getSourceName(): string + { + return self::getSourceNames()[$this->trigger_source] ?? $this->trigger_source; + } + + /** + * 获取重置的流量差值 + */ + public function getTrafficDiff(): array + { + return [ + 'upload_diff' => $this->new_upload - $this->old_upload, + 'download_diff' => $this->new_download - $this->old_download, + 'total_diff' => $this->new_total - $this->old_total, + ]; + } + + /** + * 格式化流量大小 + */ + public function formatTraffic(int $bytes): string + { + $units = ['B', 'KB', 'MB', 'GB', 'TB']; + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + + $bytes /= (1 << (10 * $pow)); + + return round($bytes, 2) . ' ' . $units[$pow]; + } +} \ No newline at end of file diff --git a/Xboard/app/Models/User.php b/Xboard/app/Models/User.php new file mode 100644 index 0000000..a826e4a --- /dev/null +++ b/Xboard/app/Models/User.php @@ -0,0 +1,215 @@ + $codes 邀请码列表 + * @property-read \Illuminate\Database\Eloquent\Collection $orders 订单列表 + * @property-read \Illuminate\Database\Eloquent\Collection $stat 统计信息 + * @property-read \Illuminate\Database\Eloquent\Collection $tickets 工单列表 + * @property-read \Illuminate\Database\Eloquent\Collection $trafficResetLogs 流量重置记录 + * @property-read User|null $parent 父账户 + * @property-read string $subscribe_url 订阅链接(动态生成) + */ +class User extends Authenticatable +{ + use HasApiTokens; + protected $table = 'v2_user'; + protected $dateFormat = 'U'; + protected $guarded = ['id']; + protected $casts = [ + 'created_at' => 'timestamp', + 'updated_at' => 'timestamp', + 'banned' => 'boolean', + 'is_admin' => 'boolean', + 'is_staff' => 'boolean', + 'remind_expire' => 'boolean', + 'remind_traffic' => 'boolean', + 'commission_auto_check' => 'boolean', + 'commission_rate' => 'float', + 'next_reset_at' => 'timestamp', + 'last_reset_at' => 'timestamp', + ]; + protected $hidden = ['password']; + + public const COMMISSION_TYPE_SYSTEM = 0; + public const COMMISSION_TYPE_PERIOD = 1; + public const COMMISSION_TYPE_ONETIME = 2; + protected function email(): Attribute + { + return Attribute::make( + set: fn (string $value) => strtolower(trim($value)), + ); + } + + /** + * 按邮箱查询(大小写不敏感,兼容所有数据库) + */ + public function scopeByEmail(Builder $query, string $email): Builder + { + return $query->where('email', strtolower(trim($email))); + } + + // 获取邀请人信息 + public function invite_user(): BelongsTo + { + return $this->belongsTo(self::class, 'invite_user_id', 'id'); + } + + /** + * 获取用户订阅计划 + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function plan(): BelongsTo + { + return $this->belongsTo(Plan::class, 'plan_id', 'id'); + } + + public function group(): BelongsTo + { + return $this->belongsTo(ServerGroup::class, 'group_id', 'id'); + } + + // 获取用户邀请码列表 + public function codes(): HasMany + { + return $this->hasMany(InviteCode::class, 'user_id', 'id'); + } + + public function orders(): HasMany + { + return $this->hasMany(Order::class, 'user_id', 'id'); + } + + public function stat(): HasMany + { + return $this->hasMany(StatUser::class, 'user_id', 'id'); + } + + // 关联工单列表 + public function tickets(): HasMany + { + return $this->hasMany(Ticket::class, 'user_id', 'id'); + } + + public function parent(): BelongsTo + { + return $this->belongsTo(self::class, 'parent_id', 'id'); + } + + /** + * 关联流量重置记录 + */ + public function trafficResetLogs(): HasMany + { + return $this->hasMany(TrafficResetLog::class, 'user_id', 'id'); + } + + /** + * 检查用户是否处于活跃状态 + */ + public function isActive(): bool + { + return !$this->banned && + ($this->expired_at === null || $this->expired_at > time()) && + $this->plan_id !== null; + } + + /** + * 检查用户是否可用节点流量且充足 + */ + public function isAvailable(): bool + { + return $this->isActive() && $this->getRemainingTraffic() > 0; + } + + /** + * 检查是否需要重置流量 + */ + public function shouldResetTraffic(): bool + { + return $this->isActive() && + $this->next_reset_at !== null && + $this->next_reset_at <= time(); + } + + /** + * 获取总使用流量 + */ + public function getTotalUsedTraffic(): int + { + return ($this->u ?? 0) + ($this->d ?? 0); + } + + /** + * 获取剩余流量 + */ + public function getRemainingTraffic(): int + { + $used = $this->getTotalUsedTraffic(); + $total = $this->transfer_enable ?? 0; + return max(0, $total - $used); + } + + /** + * 获取流量使用百分比 + */ + public function getTrafficUsagePercentage(): float + { + $total = $this->transfer_enable ?? 0; + if ($total <= 0) { + return 0; + } + + $used = $this->getTotalUsedTraffic(); + return min(100, ($used / $total) * 100); + } +} diff --git a/Xboard/app/Observers/PlanObserver.php b/Xboard/app/Observers/PlanObserver.php new file mode 100644 index 0000000..19dcef4 --- /dev/null +++ b/Xboard/app/Observers/PlanObserver.php @@ -0,0 +1,35 @@ +isDirty('reset_traffic_method')) { + return; + } + $trafficResetService = app(TrafficResetService::class); + User::where('plan_id', $plan->id) + ->where('banned', 0) + ->where(function ($query) { + $query->where('expired_at', '>', time()) + ->orWhereNull('expired_at'); + }) + ->lazyById(500) + ->each(function (User $user) use ($trafficResetService) { + $nextResetTime = $trafficResetService->calculateNextResetTime($user); + $user->update([ + 'next_reset_at' => $nextResetTime?->timestamp, + ]); + }); + } +} + diff --git a/Xboard/app/Observers/ServerObserver.php b/Xboard/app/Observers/ServerObserver.php new file mode 100644 index 0000000..0f1ce9d --- /dev/null +++ b/Xboard/app/Observers/ServerObserver.php @@ -0,0 +1,37 @@ +isDirty([ + 'group_ids', + ]) + ) { + NodeSyncService::notifyUsersUpdatedByGroup($server->id); + } else if ( + $server->isDirty([ + 'server_port', + 'protocol_settings', + 'type', + 'route_ids', + 'custom_outbounds', + 'custom_routes', + 'cert_config', + ]) + ) { + NodeSyncService::notifyConfigUpdated($server->id); + } + } + + public function deleted(Server $server): void + { + NodeSyncService::notifyConfigUpdated($server->id); + } +} diff --git a/Xboard/app/Observers/ServerRouteObserver.php b/Xboard/app/Observers/ServerRouteObserver.php new file mode 100644 index 0000000..f8457d7 --- /dev/null +++ b/Xboard/app/Observers/ServerRouteObserver.php @@ -0,0 +1,31 @@ +notifyAffectedNodes($route->id); + } + + public function deleted(ServerRoute $route): void + { + $this->notifyAffectedNodes($route->id); + } + + private function notifyAffectedNodes(int $routeId): void + { + $servers = Server::where('show', 1)->get()->filter( + fn ($s) => in_array($routeId, $s->route_ids ?? []) + ); + + foreach ($servers as $server) { + NodeSyncService::notifyConfigUpdated($server->id); + } + } +} diff --git a/Xboard/app/Observers/UserObserver.php b/Xboard/app/Observers/UserObserver.php new file mode 100644 index 0000000..ffa74d0 --- /dev/null +++ b/Xboard/app/Observers/UserObserver.php @@ -0,0 +1,53 @@ +isDirty(['plan_id', 'expired_at'])) { + $this->recalculateNextResetAt($user); + } + + if ($user->isDirty(['group_id', 'uuid', 'speed_limit', 'device_limit', 'banned', 'expired_at', 'transfer_enable', 'u', 'd', 'plan_id'])) { + $oldGroupId = $user->isDirty('group_id') ? $user->getOriginal('group_id') : null; + NodeUserSyncJob::dispatch($user->id, 'updated', $oldGroupId); + } + } + + public function created(User $user): void + { + $this->recalculateNextResetAt($user); + NodeUserSyncJob::dispatch($user->id, 'created'); + } + + public function deleted(User $user): void + { + if ($user->group_id) { + NodeUserSyncJob::dispatch($user->id, 'deleted', $user->group_id); + } + } + + /** + * 根据当前用户状态重新计算 next_reset_at + */ + private function recalculateNextResetAt(User $user): void + { + $user->refresh(); + User::withoutEvents(function () use ($user) { + $nextResetTime = $this->trafficResetService->calculateNextResetTime($user); + $user->next_reset_at = $nextResetTime?->timestamp; + $user->save(); + }); + } +} \ No newline at end of file diff --git a/Xboard/app/Protocols/Clash.php b/Xboard/app/Protocols/Clash.php new file mode 100644 index 0000000..f84d99f --- /dev/null +++ b/Xboard/app/Protocols/Clash.php @@ -0,0 +1,332 @@ +servers; + $user = $this->user; + $appName = admin_setting('app_name', 'XBoard'); + + // 优先从数据库配置中获取模板 + $template = subscribe_template('clash'); + + $config = Yaml::parse($template); + $proxy = []; + $proxies = []; + + foreach ($servers as $item) { + + if ( + $item['type'] === Server::TYPE_SHADOWSOCKS + && in_array(data_get($item['protocol_settings'], 'cipher'), [ + 'aes-128-gcm', + 'aes-192-gcm', + 'aes-256-gcm', + 'chacha20-ietf-poly1305' + ]) + ) { + array_push($proxy, self::buildShadowsocks($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_VMESS) { + array_push($proxy, self::buildVmess($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_TROJAN) { + array_push($proxy, self::buildTrojan($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_SOCKS) { + array_push($proxy, self::buildSocks5($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_HTTP) { + array_push($proxy, self::buildHttp($item['password'], $item)); + array_push($proxies, $item['name']); + } + } + + $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy); + foreach ($config['proxy-groups'] as $k => $v) { + if (!is_array($config['proxy-groups'][$k]['proxies'])) + $config['proxy-groups'][$k]['proxies'] = []; + $isFilter = false; + foreach ($config['proxy-groups'][$k]['proxies'] as $src) { + foreach ($proxies as $dst) { + if (!$this->isRegex($src)) + continue; + $isFilter = true; + $config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src])); + if ($this->isMatch($src, $dst)) { + array_push($config['proxy-groups'][$k]['proxies'], $dst); + } + } + if ($isFilter) + continue; + } + if ($isFilter) + continue; + $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies); + } + + $config['proxy-groups'] = array_filter($config['proxy-groups'], function ($group) { + return $group['proxies']; + }); + $config['proxy-groups'] = array_values($config['proxy-groups']); + + $config = $this->buildRules($config); + + + $yaml = Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + $yaml = str_replace('$app_name', admin_setting('app_name', 'XBoard'), $yaml); + return response($yaml) + ->header('content-type', 'text/yaml') + ->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}") + ->header('profile-update-interval', '24') + ->header('content-disposition', 'attachment;filename*=UTF-8\'\'' . rawurlencode($appName)) + ->header('profile-web-page-url', admin_setting('app_url')); + } + + /** + * Build the rules for Clash. + */ + public function buildRules($config) + { + // Force the current subscription domain to be a direct rule + $subsDomain = request()->header('Host'); + if ($subsDomain) { + array_unshift($config['rules'], "DOMAIN,{$subsDomain},DIRECT"); + } + + return $config; + } + + public static function buildShadowsocks($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'ss'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['cipher'] = data_get($protocol_settings, 'cipher'); + $array['password'] = $uuid; + $array['udp'] = true; + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $plugin = data_get($protocol_settings, 'plugin'); + $pluginOpts = data_get($protocol_settings, 'plugin_opts', ''); + $array['plugin'] = $plugin; + + // 解析插件选项 + $parsedOpts = collect(explode(';', $pluginOpts)) + ->filter() + ->mapWithKeys(function ($pair) { + if (!str_contains($pair, '=')) { + return []; + } + [$key, $value] = explode('=', $pair, 2); + return [trim($key) => trim($value)]; + }) + ->all(); + + // 根据插件类型进行字段映射 + switch ($plugin) { + case 'obfs': + $array['plugin-opts'] = [ + 'mode' => $parsedOpts['obfs'] ?? data_get($protocol_settings, 'obfs', 'http'), + 'host' => $parsedOpts['obfs-host'] ?? data_get($protocol_settings, 'obfs_settings.host', ''), + ]; + + if (isset($parsedOpts['path'])) { + $array['plugin-opts']['path'] = $parsedOpts['path']; + } + break; + case 'v2ray-plugin': + $array['plugin-opts'] = [ + 'mode' => $parsedOpts['mode'] ?? 'websocket', + 'tls' => isset($parsedOpts['tls']) && $parsedOpts['tls'] == 'true', + 'host' => $parsedOpts['host'] ?? '', + 'path' => $parsedOpts['path'] ?? '/', + ]; + break; + default: + // 对于其他插件,直接使用解析出的键值对 + $array['plugin-opts'] = $parsedOpts; + } + } + return $array; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'vmess'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['uuid'] = $uuid; + $array['alterId'] = 0; + $array['cipher'] = 'auto'; + $array['udp'] = true; + + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure'); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['servername'] = $serverName; + } + } + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $headerType = data_get($protocol_settings, 'network_settings.header.type', 'none'); + $array['network'] = ($headerType === 'http') ? 'http' : 'tcp'; + if ($headerType === 'http') { + if ($httpOpts = array_filter([ + 'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'), + 'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/']) + ])) { + $array['http-opts'] = $httpOpts; + } + } + break; + case 'ws': + $array['network'] = 'ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + case 'grpc': + $array['network'] = 'grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $array['grpc-opts']['grpc-service-name'] = $serviceName; + break; + default: + break; + } + return $array; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'trojan'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['password'] = $password; + $array['udp'] = true; + if ($serverName = data_get($protocol_settings, 'server_name')) { + $array['sni'] = $serverName; + } + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'allow_insecure'); + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $array['network'] = 'tcp'; + break; + case 'ws': + $array['network'] = 'ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + case 'grpc': + $array['network'] = 'grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $array['grpc-opts']['grpc-service-name'] = $serviceName; + break; + default: + $array['network'] = 'tcp'; + break; + } + return $array; + } + + public static function buildSocks5($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'socks5'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['udp'] = true; + + $array['username'] = $password; + $array['password'] = $password; + + // TLS 配置 + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + } + + return $array; + } + + public static function buildHttp($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'http'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + + $array['username'] = $password; + $array['password'] = $password; + + // TLS 配置 + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + } + + return $array; + } + + private function isMatch($exp, $str) + { + try { + return preg_match($exp, $str) === 1; + } catch (\Exception $e) { + return false; + } + } + + private function isRegex($exp) + { + if (empty($exp)) { + return false; + } + try { + return preg_match($exp, '') !== false; + } catch (\Exception $e) { + return false; + } + } +} diff --git a/Xboard/app/Protocols/ClashMeta.php b/Xboard/app/Protocols/ClashMeta.php new file mode 100644 index 0000000..feda7d0 --- /dev/null +++ b/Xboard/app/Protocols/ClashMeta.php @@ -0,0 +1,708 @@ + [ + 'whitelist' => [ + 'tcp' => '0.0.0', + 'ws' => '0.0.0', + 'grpc' => '0.0.0', + 'http' => '0.0.0', + 'h2' => '0.0.0', + 'httpupgrade' => '0.0.0', + ], + 'strict' => true, + ], + 'nekobox.hysteria.protocol_settings.version' => [ + 1 => '0.0.0', + 2 => '1.2.7', + ], + 'clashmetaforandroid.hysteria.protocol_settings.version' => [ + 2 => '2.9.0', + ], + 'nekoray.hysteria.protocol_settings.version' => [ + 2 => '3.24', + ], + 'verge.hysteria.protocol_settings.version' => [ + 2 => '1.3.8', + ], + 'ClashX Meta.hysteria.protocol_settings.version' => [ + 2 => '1.3.5', + ], + 'flclash.hysteria.protocol_settings.version' => [ + 2 => '0.8.0', + ], + ]; + + public function handle() + { + $servers = $this->servers; + $user = $this->user; + $appName = admin_setting('app_name', 'XBoard'); + + $template = subscribe_template('clashmeta'); + + $config = Yaml::parse($template); + $proxy = []; + $proxies = []; + + foreach ($servers as $item) { + if ($item['type'] === Server::TYPE_SHADOWSOCKS) { + array_push($proxy, self::buildShadowsocks($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_VMESS) { + array_push($proxy, self::buildVmess($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_TROJAN) { + array_push($proxy, self::buildTrojan($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_VLESS) { + array_push($proxy, self::buildVless($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_HYSTERIA) { + array_push($proxy, self::buildHysteria($item['password'], $item, $user)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_TUIC) { + array_push($proxy, self::buildTuic($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_ANYTLS) { + array_push($proxy, self::buildAnyTLS($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_SOCKS) { + array_push($proxy, self::buildSocks5($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_HTTP) { + array_push($proxy, self::buildHttp($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_MIERU) { + array_push($proxy, self::buildMieru($item['password'], $item)); + array_push($proxies, $item['name']); + } + } + + $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy); + foreach ($config['proxy-groups'] as $k => $v) { + if (!is_array($config['proxy-groups'][$k]['proxies'])) + $config['proxy-groups'][$k]['proxies'] = []; + $isFilter = false; + foreach ($config['proxy-groups'][$k]['proxies'] as $src) { + foreach ($proxies as $dst) { + if (!$this->isRegex($src)) + continue; + $isFilter = true; + $config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src])); + if ($this->isMatch($src, $dst)) { + array_push($config['proxy-groups'][$k]['proxies'], $dst); + } + } + if ($isFilter) + continue; + } + if ($isFilter) + continue; + $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies); + } + $config['proxy-groups'] = array_filter($config['proxy-groups'], function ($group) { + return $group['proxies']; + }); + $config['proxy-groups'] = array_values($config['proxy-groups']); + $config = $this->buildRules($config); + + $yaml = Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + $yaml = str_replace('$app_name', admin_setting('app_name', 'XBoard'), $yaml); + return response($yaml) + ->header('content-type', 'text/yaml') + ->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}") + ->header('profile-update-interval', '24') + ->header('content-disposition', 'attachment;filename*=UTF-8\'\'' . rawurlencode($appName)); + } + + /** + * Build the rules for Clash. + */ + public function buildRules($config) + { + // Force the current subscription domain to be a direct rule + $subsDomain = request()->header('Host'); + if ($subsDomain) { + array_unshift($config['rules'], "DOMAIN,{$subsDomain},DIRECT"); + } + // // Force the nodes ip to be a direct rule + // collect($this->servers)->pluck('host')->map(function ($host) { + // $host = trim($host); + // return filter_var($host, FILTER_VALIDATE_IP) ? [$host] : Helper::getIpByDomainName($host); + // })->flatten()->unique()->each(function ($nodeIP) use (&$config) { + // array_unshift($config['rules'], "IP-CIDR,{$nodeIP}/32,DIRECT,no-resolve"); + // }); + + return $config; + } + + public static function buildShadowsocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'ss'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['cipher'] = data_get($server['protocol_settings'], 'cipher'); + $array['password'] = data_get($server, 'password', $password); + $array['udp'] = true; + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $plugin = data_get($protocol_settings, 'plugin'); + $pluginOpts = data_get($protocol_settings, 'plugin_opts', ''); + $array['plugin'] = $plugin; + + // 解析插件选项 + $parsedOpts = collect(explode(';', $pluginOpts)) + ->filter() + ->mapWithKeys(function ($pair) { + if (!str_contains($pair, '=')) { + return [trim($pair) => true]; + } + [$key, $value] = explode('=', $pair, 2); + return [trim($key) => trim($value)]; + }) + ->all(); + + // 根据插件类型进行字段映射 + switch ($plugin) { + case 'obfs': + case 'obfs-local': + $array['plugin'] = 'obfs'; + $array['plugin-opts'] = array_filter([ + 'mode' => $parsedOpts['obfs'] ?? ($parsedOpts['mode'] ?? 'http'), + 'host' => $parsedOpts['obfs-host'] ?? ($parsedOpts['host'] ?? 'www.bing.com'), + ]); + break; + + case 'v2ray-plugin': + $array['plugin-opts'] = array_filter([ + 'mode' => $parsedOpts['mode'] ?? 'websocket', + 'tls' => isset($parsedOpts['tls']) || isset($parsedOpts['server']), + 'host' => $parsedOpts['host'] ?? null, + 'path' => $parsedOpts['path'] ?? '/', + 'mux' => isset($parsedOpts['mux']) ? true : null, + 'headers' => isset($parsedOpts['host']) ? ['Host' => $parsedOpts['host']] : null + ], fn($v) => $v !== null); + break; + + case 'shadow-tls': + $array['plugin-opts'] = array_filter([ + 'host' => $parsedOpts['host'] ?? null, + 'password' => $parsedOpts['password'] ?? null, + 'version' => isset($parsedOpts['version']) ? (int) $parsedOpts['version'] : 2 + ], fn($v) => $v !== null); + break; + + case 'restls': + $array['plugin-opts'] = array_filter([ + 'host' => $parsedOpts['host'] ?? null, + 'password' => $parsedOpts['password'] ?? null, + 'restls-script' => $parsedOpts['restls-script'] ?? '123' + ], fn($v) => $v !== null); + break; + + default: + $array['plugin-opts'] = $parsedOpts; + } + } + return $array; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'vmess', + 'server' => $server['host'], + 'port' => $server['port'], + 'uuid' => $uuid, + 'alterId' => 0, + 'cipher' => 'auto', + 'udp' => true + ]; + + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = (bool) data_get($protocol_settings, 'tls'); + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + $array['servername'] = data_get($protocol_settings, 'tls_settings.server_name'); + } + + self::appendUtls($array, $protocol_settings); + self::appendMultiplex($array, $protocol_settings); + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $headerType = data_get($protocol_settings, 'network_settings.header.type', 'none'); + $array['network'] = ($headerType === 'http') ? 'http' : 'tcp'; + if ($headerType === 'http') { + if ( + $httpOpts = array_filter([ + 'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'), + 'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/']) + ]) + ) { + $array['http-opts'] = $httpOpts; + } + } + break; + case 'ws': + $array['network'] = 'ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + case 'grpc': + $array['network'] = 'grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $array['grpc-opts']['grpc-service-name'] = $serviceName; + break; + case 'h2': + $array['network'] = 'h2'; + $array['h2-opts'] = []; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['h2-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['h2-opts']['host'] = is_array($host) ? $host : [$host]; + break; + case 'httpupgrade': + $array['network'] = 'ws'; + $array['ws-opts'] = ['v2ray-http-upgrade' => true]; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + default: + break; + } + + return $array; + } + + public static function buildVless($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'vless', + 'server' => $server['host'], + 'port' => $server['port'], + 'uuid' => $password, + 'alterId' => 0, + 'cipher' => 'auto', + 'udp' => true, + 'flow' => data_get($protocol_settings, 'flow'), + 'encryption' => match (data_get($protocol_settings, 'encryption.enabled')) { + true => data_get($protocol_settings, 'encryption.encryption', 'none'), + default => 'none' + }, + 'tls' => false + ]; + + switch (data_get($protocol_settings, 'tls')) { + case 1: + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['servername'] = $serverName; + } + self::appendUtls($array, $protocol_settings); + break; + case 2: + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'reality_settings.allow_insecure', false); + $array['servername'] = data_get($protocol_settings, 'reality_settings.server_name'); + $array['reality-opts'] = [ + 'public-key' => data_get($protocol_settings, 'reality_settings.public_key'), + 'short-id' => data_get($protocol_settings, 'reality_settings.short_id') + ]; + self::appendUtls($array, $protocol_settings); + break; + default: + break; + } + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $array['network'] = 'tcp'; + $headerType = data_get($protocol_settings, 'network_settings.header.type', 'none'); + if ($headerType === 'http') { + $array['network'] = 'http'; + if ( + $httpOpts = array_filter([ + 'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'), + 'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/']) + ]) + ) { + $array['http-opts'] = $httpOpts; + } + } + break; + case 'ws': + $array['network'] = 'ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + case 'grpc': + $array['network'] = 'grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $array['grpc-opts']['grpc-service-name'] = $serviceName; + break; + case 'h2': + $array['network'] = 'h2'; + $array['h2-opts'] = []; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['h2-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['h2-opts']['host'] = is_array($host) ? $host : [$host]; + break; + case 'httpupgrade': + $array['network'] = 'ws'; + $array['ws-opts'] = ['v2ray-http-upgrade' => true]; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + default: + break; + } + + self::appendMultiplex($array, $protocol_settings); + + return $array; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'trojan', + 'server' => $server['host'], + 'port' => $server['port'], + 'password' => $password, + 'udp' => true, + ]; + + $tlsMode = (int) data_get($protocol_settings, 'tls', 1); + switch ($tlsMode) { + case 2: // Reality + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'reality_settings.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'reality_settings.server_name')) { + $array['sni'] = $serverName; + } + $array['reality-opts'] = [ + 'public-key' => data_get($protocol_settings, 'reality_settings.public_key'), + 'short-id' => data_get($protocol_settings, 'reality_settings.short_id'), + ]; + break; + default: // Standard TLS + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'server_name')) { + $array['sni'] = $serverName; + } + break; + } + + self::appendUtls($array, $protocol_settings); + self::appendMultiplex($array, $protocol_settings); + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $array['network'] = 'tcp'; + break; + case 'ws': + $array['network'] = 'ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + case 'grpc': + $array['network'] = 'grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $array['grpc-opts']['grpc-service-name'] = $serviceName; + break; + case 'h2': + $array['network'] = 'h2'; + $array['h2-opts'] = []; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['h2-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['h2-opts']['host'] = is_array($host) ? $host : [$host]; + break; + case 'httpupgrade': + $array['network'] = 'ws'; + $array['ws-opts'] = ['v2ray-http-upgrade' => true]; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['ws-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['ws-opts']['headers'] = ['Host' => $host]; + break; + default: + $array['network'] = 'tcp'; + break; + } + + return $array; + } + + public static function buildHysteria($password, $server, $user) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'server' => $server['host'], + 'port' => $server['port'], + 'sni' => data_get($protocol_settings, 'tls.server_name'), + 'up' => data_get($protocol_settings, 'bandwidth.up'), + 'down' => data_get($protocol_settings, 'bandwidth.down'), + 'skip-cert-verify' => (bool) data_get($protocol_settings, 'tls.allow_insecure', false), + ]; + if (isset($server['ports'])) { + $array['ports'] = $server['ports']; + } + if ($hopInterval = data_get($protocol_settings, 'hop_interval')) { + $array['hop-interval'] = (int) $hopInterval; + } + switch (data_get($protocol_settings, 'version')) { + case 1: + $array['type'] = 'hysteria'; + $array['auth_str'] = $password; + $array['protocol'] = 'udp'; // 支持 udp/wechat-video/faketcp + if (data_get($protocol_settings, 'obfs.open')) { + $array['obfs'] = data_get($protocol_settings, 'obfs.password'); + } + $array['fast-open'] = true; + $array['disable_mtu_discovery'] = true; + break; + case 2: + $array['type'] = 'hysteria2'; + $array['password'] = $password; + if (data_get($protocol_settings, 'obfs.open')) { + $array['obfs'] = data_get($protocol_settings, 'obfs.type'); + $array['obfs-password'] = data_get($protocol_settings, 'obfs.password'); + } + break; + } + + return $array; + } + + public static function buildTuic($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'tuic', + 'server' => $server['host'], + 'port' => $server['port'], + 'udp' => true, + ]; + + if (data_get($protocol_settings, 'version') === 4) { + $array['token'] = $password; + } else { + $array['uuid'] = $password; + $array['password'] = $password; + } + + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $array['sni'] = $serverName; + } + + if ($alpn = data_get($protocol_settings, 'alpn')) { + $array['alpn'] = $alpn; + } + + $array['congestion-controller'] = data_get($protocol_settings, 'congestion_control', 'cubic'); + $array['udp-relay-mode'] = data_get($protocol_settings, 'udp_relay_mode', 'native'); + + return $array; + } + + public static function buildAnyTLS($password, $server) + { + + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'anytls', + 'server' => $server['host'], + 'port' => $server['port'], + 'password' => $password, + 'udp' => true, + ]; + + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $array['sni'] = $serverName; + } + if ($allowInsecure = data_get($protocol_settings, 'tls.allow_insecure')) { + $array['skip-cert-verify'] = (bool) $allowInsecure; + } + + return $array; + } + + public static function buildMieru($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'mieru', + 'server' => $server['host'], + 'port' => $server['port'], + 'username' => $password, + 'password' => $password, + 'transport' => strtoupper(data_get($protocol_settings, 'transport', 'TCP')) + ]; + + // 如果配置了端口范围 + if (isset($server['ports'])) { + $array['port-range'] = $server['ports']; + } + + return $array; + } + + public static function buildSocks5($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'socks5'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['udp'] = true; + + $array['username'] = $password; + $array['password'] = $password; + + // TLS 配置 + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + } + + return $array; + } + + public static function buildHttp($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'http'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + + $array['username'] = $password; + $array['password'] = $password; + + // TLS 配置 + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + } + + return $array; + } + + private function isMatch($exp, $str) + { + try { + return preg_match($exp, $str) === 1; + } catch (\Exception $e) { + return false; + } + } + + private function isRegex($exp) + { + if (empty($exp)) { + return false; + } + try { + return preg_match($exp, '') !== false; + } catch (\Exception $e) { + return false; + } + } + + protected static function appendMultiplex(&$array, $protocol_settings) + { + if ($multiplex = data_get($protocol_settings, 'multiplex')) { + if (data_get($multiplex, 'enabled')) { + $array['smux'] = array_filter([ + 'enabled' => true, + 'protocol' => data_get($multiplex, 'protocol', 'yamux'), + 'max-connections' => data_get($multiplex, 'max_connections'), + // 'min-streams' => data_get($multiplex, 'min_streams'), + // 'max-streams' => data_get($multiplex, 'max_streams'), + 'padding' => data_get($multiplex, 'padding') ? true : null, + ]); + + if (data_get($multiplex, 'brutal.enabled')) { + $array['smux']['brutal-opts'] = [ + 'enabled' => true, + 'up' => data_get($multiplex, 'brutal.up_mbps'), + 'down' => data_get($multiplex, 'brutal.down_mbps'), + ]; + } + } + } + } + + protected static function appendUtls(&$array, $protocol_settings) + { + if ($utls = data_get($protocol_settings, 'utls')) { + if (data_get($utls, 'enabled')) { + $array['client-fingerprint'] = Helper::getTlsFingerprint($utls); + } + } + } +} \ No newline at end of file diff --git a/Xboard/app/Protocols/General.php b/Xboard/app/Protocols/General.php new file mode 100644 index 0000000..5071027 --- /dev/null +++ b/Xboard/app/Protocols/General.php @@ -0,0 +1,448 @@ + [2 => '1.9.5'], + 'v2rayn.hysteria.protocol_settings.version' => [2 => '6.31'], + ]; + + public function handle() + { + $servers = $this->servers; + $user = $this->user; + $uri = ''; + + foreach ($servers as $item) { + $uri .= match ($item['type']) { + Server::TYPE_VMESS => self::buildVmess($item['password'], $item), + Server::TYPE_VLESS => self::buildVless($item['password'], $item), + Server::TYPE_SHADOWSOCKS => self::buildShadowsocks($item['password'], $item), + Server::TYPE_TROJAN => self::buildTrojan($item['password'], $item), + Server::TYPE_HYSTERIA => self::buildHysteria($item['password'], $item), + Server::TYPE_ANYTLS => self::buildAnyTLS($item['password'], $item), + Server::TYPE_SOCKS => self::buildSocks($item['password'], $item), + Server::TYPE_TUIC => self::buildTuic($item['password'], $item), + Server::TYPE_HTTP => self::buildHttp($item['password'], $item), + default => '', + }; + } + return response(base64_encode($uri)) + ->header('content-type', 'text/plain') + ->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}"); + } + + public static function buildShadowsocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $password = data_get($server, 'password', $password); + $str = str_replace( + ['+', '/', '='], + ['-', '_', ''], + base64_encode(data_get($protocol_settings, 'cipher') . ":{$password}") + ); + $addr = Helper::wrapIPv6($server['host']); + $plugin = data_get($protocol_settings, 'plugin'); + $plugin_opts = data_get($protocol_settings, 'plugin_opts'); + $url = "ss://{$str}@{$addr}:{$server['port']}"; + if ($plugin && $plugin_opts) { + $url .= '/?' . 'plugin=' . rawurlencode($plugin . ';' . $plugin_opts); + } + $url .= "#{$name}\r\n"; + return $url; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "v" => "2", + "ps" => $server['name'], + "add" => $server['host'], + "port" => (string) $server['port'], + "id" => $uuid, + "aid" => '0', + "net" => data_get($server, 'protocol_settings.network'), + "type" => "none", + "host" => "", + "path" => "", + "tls" => data_get($protocol_settings, 'tls') ? "tls" : "", + ]; + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $config['sni'] = $serverName; + } + if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $config['fp'] = $fp; + } + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + if (data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none') { + $config['type'] = data_get($protocol_settings, 'network_settings.header.type', 'http'); + $config['path'] = Arr::random(data_get($protocol_settings, 'network_settings.header.request.path', ['/'])); + $config['host'] = + data_get($protocol_settings, 'network_settings.header.request.headers.Host') + ? Arr::random(data_get($protocol_settings, 'network_settings.header.request.headers.Host', ['/']), ) + : null; + } + break; + case 'ws': + $config['type'] = 'ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $config['host'] = $host; + break; + case 'grpc': + $config['type'] = 'grpc'; + if ($path = data_get($protocol_settings, 'network_settings.serviceName')) + $config['path'] = $path; + break; + case 'h2': + $config['net'] = 'h2'; + $config['type'] = 'h2'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $config['host'] = is_array($host) ? implode(',', $host) : $host; + break; + case 'httpupgrade': + $config['net'] = 'httpupgrade'; + $config['type'] = 'httpupgrade'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config['path'] = $path; + $config['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']); + break; + default: + break; + } + return "vmess://" . base64_encode(json_encode($config)) . "\r\n"; + } + + public static function buildVless($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $host = $server['host']; //节点地址 + $port = $server['port']; //节点端口 + $name = $server['name']; //节点名称 + + $config = [ + 'mode' => 'multi', //grpc传输模式 + 'security' => '', //传输层安全 tls/reality + 'encryption' => match (data_get($protocol_settings, 'encryption.enabled')) { + true => data_get($protocol_settings, 'encryption.encryption', 'none'), + default => 'none' + }, + 'type' => data_get($server, 'protocol_settings.network'), //传输协议 + 'flow' => data_get($protocol_settings, 'flow'), + ]; + // 处理TLS + switch (data_get($server, 'protocol_settings.tls')) { + case 1: + $config['security'] = "tls"; + if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $config['fp'] = $fp; + } + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $config['sni'] = $serverName; + } + if (data_get($protocol_settings, 'tls_settings.allow_insecure')) { + $config['allowInsecure'] = '1'; + } + break; + case 2: //reality + $config['security'] = "reality"; + $config['pbk'] = data_get($protocol_settings, 'reality_settings.public_key'); + $config['sid'] = data_get($protocol_settings, 'reality_settings.short_id'); + $config['sni'] = data_get($protocol_settings, 'reality_settings.server_name'); + $config['servername'] = data_get($protocol_settings, 'reality_settings.server_name'); + $config['spx'] = "/"; + if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $config['fp'] = $fp; + } + break; + default: + break; + } + // 处理传输协议 + switch (data_get($server, 'protocol_settings.network')) { + case 'ws': + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config['path'] = $path; + if ($wsHost = data_get($protocol_settings, 'network_settings.headers.Host')) + $config['host'] = $wsHost; + break; + case 'grpc': + if ($path = data_get($protocol_settings, 'network_settings.serviceName')) + $config['serviceName'] = $path; + break; + case 'h2': + $config['type'] = 'http'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config['path'] = $path; + if ($h2Host = data_get($protocol_settings, 'network_settings.host')) + $config['host'] = is_array($h2Host) ? implode(',', $h2Host) : $h2Host; + break; + case 'kcp': + if ($path = data_get($protocol_settings, 'network_settings.seed')) + $config['path'] = $path; + $config['type'] = data_get($protocol_settings, 'network_settings.header.type', 'none'); + break; + case 'httpupgrade': + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config['path'] = $path; + $config['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']); + break; + case 'xhttp': + $config['path'] = data_get($protocol_settings, 'network_settings.path'); + $config['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']); + $config['mode'] = data_get($protocol_settings, 'network_settings.mode', 'auto'); + $config['extra'] = json_encode(data_get($protocol_settings, 'network_settings.extra')); + break; + } + + $user = $uuid . '@' . Helper::wrapIPv6($host) . ':' . $port; + $query = http_build_query($config); + $fragment = urlencode($name); + $link = sprintf("vless://%s?%s#%s\r\n", $user, $query, $fragment); + return $link; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $array = []; + $tlsMode = (int) data_get($protocol_settings, 'tls', 1); + + switch ($tlsMode) { + case 2: // Reality + $array['security'] = 'reality'; + $array['pbk'] = data_get($protocol_settings, 'reality_settings.public_key'); + $array['sid'] = data_get($protocol_settings, 'reality_settings.short_id'); + $array['sni'] = data_get($protocol_settings, 'reality_settings.server_name'); + if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $array['fp'] = $fp; + } + break; + default: // Standard TLS + $array['allowInsecure'] = data_get($protocol_settings, 'allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'server_name')) { + $array['peer'] = $serverName; + $array['sni'] = $serverName; + } + if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $array['fp'] = $fp; + } + break; + } + + switch (data_get($server, 'protocol_settings.network')) { + case 'ws': + $array['type'] = 'ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $array['host'] = $host; + break; + case 'grpc': + // Follow V2rayN family standards + $array['type'] = 'grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $array['serviceName'] = $serviceName; + break; + case 'h2': + $array['type'] = 'http'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['host'] = is_array($host) ? implode(',', $host) : $host; + break; + case 'httpupgrade': + $array['type'] = 'httpupgrade'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['path'] = $path; + $array['host'] = data_get($protocol_settings, 'network_settings.host', $server['host']); + break; + default: + break; + } + $query = http_build_query($array); + $addr = Helper::wrapIPv6($server['host']); + + $uri = "trojan://{$password}@{$addr}:{$server['port']}?{$query}#{$name}"; + $uri .= "\r\n"; + return $uri; + } + + public static function buildHysteria($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $params = []; + $version = data_get($protocol_settings, 'version', 2); + + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $params['sni'] = $serverName; + } + $params['insecure'] = data_get($protocol_settings, 'tls.allow_insecure') ? '1' : '0'; + + $name = rawurlencode($server['name']); + $addr = Helper::wrapIPv6($server['host']); + + if ($version === 2) { + if (data_get($protocol_settings, 'obfs.open')) { + $params['obfs'] = 'salamander'; + $params['obfs-password'] = data_get($protocol_settings, 'obfs.password'); + } + if (isset($server['ports'])) { + $params['mport'] = $server['ports']; + } + + $query = http_build_query($params); + $uri = "hysteria2://{$password}@{$addr}:{$server['port']}?{$query}#{$name}"; + } else { + $params['protocol'] = 'udp'; + $params['auth'] = $password; + if ($upMbps = data_get($protocol_settings, 'bandwidth.up')) + $params['upmbps'] = $upMbps; + if ($downMbps = data_get($protocol_settings, 'bandwidth.down')) + $params['downmbps'] = $downMbps; + if (data_get($protocol_settings, 'obfs.open') && ($obfsPassword = data_get($protocol_settings, 'obfs.password'))) { + $params['obfs'] = 'xplus'; + $params['obfsParam'] = $obfsPassword; + } + + $query = http_build_query($params); + $uri = "hysteria://{$addr}:{$server['port']}?{$query}#{$name}"; + } + $uri .= "\r\n"; + + return $uri; + } + + + public static function buildTuic($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $name = rawurlencode($server['name']); + $addr = Helper::wrapIPv6($server['host']); + $port = $server['port']; + $uuid = $password; // v2rayN格式里,uuid和password都是密码部分 + $pass = $password; + + $queryParams = []; + + // 填充sni参数 + if ($sni = data_get($protocol_settings, 'tls.server_name')) { + $queryParams['sni'] = $sni; + } + + // alpn参数,支持多值时用逗号连接 + if ($alpn = data_get($protocol_settings, 'alpn')) { + if (is_array($alpn)) { + $queryParams['alpn'] = implode(',', $alpn); + } else { + $queryParams['alpn'] = $alpn; + } + } + + // congestion_controller参数,默认cubic + $congestion = data_get($protocol_settings, 'congestion_control', 'cubic'); + $queryParams['congestion_control'] = $congestion; + + // udp_relay_mode参数,默认native + $udpRelay = data_get($protocol_settings, 'udp_relay_mode', 'native'); + $queryParams['udp-relay-mode'] = $udpRelay; + + if (data_get($protocol_settings, 'tls.allow_insecure')) { + $queryParams['insecure'] = '1'; + } + + $query = http_build_query($queryParams); + + // 构造完整URI,格式: + // Tuic://uuid:password@host:port?sni=xxx&alpn=xxx&congestion_controller=xxx&udp_relay_mode=xxx#别名 + $uri = "tuic://{$uuid}:{$pass}@{$addr}:{$port}"; + + if (!empty($query)) { + $uri .= "?{$query}"; + } + + $uri .= "#{$name}\r\n"; + + return $uri; + } + + + + + + public static function buildAnyTLS($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $params = [ + 'sni' => data_get($protocol_settings, 'tls.server_name'), + 'insecure' => data_get($protocol_settings, 'tls.allow_insecure') + ]; + $query = http_build_query($params); + $addr = Helper::wrapIPv6($server['host']); + $uri = "anytls://{$password}@{$addr}:{$server['port']}?{$query}#{$name}"; + $uri .= "\r\n"; + return $uri; + } + + public static function buildSocks($password, $server) + { + $name = rawurlencode($server['name']); + $credentials = base64_encode("{$password}:{$password}"); + $addr = Helper::wrapIPv6($server['host']); + return "socks://{$credentials}@{$addr}:{$server['port']}#{$name}\r\n"; + } + + public static function buildHttp($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $name = rawurlencode($server['name']); + $addr = Helper::wrapIPv6($server['host']); + $credentials = base64_encode("{$password}:{$password}"); + + $params = []; + if (data_get($protocol_settings, 'tls')) { + $params['security'] = 'tls'; + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $params['sni'] = $serverName; + } + $params['allowInsecure'] = data_get($protocol_settings, 'tls_settings.allow_insecure') ? '1' : '0'; + } + + $uri = "http://{$credentials}@{$addr}:{$server['port']}"; + if (!empty($params)) { + $uri .= '?' . http_build_query($params); + } + $uri .= "#{$name}\r\n"; + return $uri; + } +} diff --git a/Xboard/app/Protocols/Loon.php b/Xboard/app/Protocols/Loon.php new file mode 100644 index 0000000..56df854 --- /dev/null +++ b/Xboard/app/Protocols/Loon.php @@ -0,0 +1,357 @@ + [2 => '637'], + 'loon.trojan.protocol_settings.tls' => [0 => '3.2.1', 1 => '3.2.1',2 => '999.9.9'], + ]; + + public function handle() + { + $servers = $this->servers; + $user = $this->user; + + $uri = ''; + + foreach ($servers as $item) { + if ( + $item['type'] === Server::TYPE_SHADOWSOCKS + ) { + $uri .= self::buildShadowsocks($item['password'], $item); + } + if ($item['type'] === Server::TYPE_VMESS) { + $uri .= self::buildVmess($item['password'], $item); + } + if ($item['type'] === Server::TYPE_TROJAN) { + $uri .= self::buildTrojan($item['password'], $item); + } + if ($item['type'] === Server::TYPE_HYSTERIA) { + $uri .= self::buildHysteria($item['password'], $item, $user); + } + if ($item['type'] === Server::TYPE_VLESS) { + $uri .= self::buildVless($item['password'], $item); + } + if ($item['type'] === Server::TYPE_ANYTLS) { + $uri .= self::buildAnyTLS($item['password'], $item); + } + } + return response($uri) + ->header('content-type', 'text/plain') + ->header('Subscription-Userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}"); + } + + + public static function buildShadowsocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $cipher = data_get($protocol_settings, 'cipher'); + + $config = [ + "{$server['name']}=Shadowsocks", + "{$server['host']}", + "{$server['port']}", + "{$cipher}", + "{$password}", + 'fast-open=false', + 'udp=true' + ]; + + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $plugin = data_get($protocol_settings, 'plugin'); + $pluginOpts = data_get($protocol_settings, 'plugin_opts', ''); + // 解析插件选项 + $parsedOpts = collect(explode(';', $pluginOpts)) + ->filter() + ->mapWithKeys(function ($pair) { + if (!str_contains($pair, '=')) { + return []; + } + [$key, $value] = explode('=', $pair, 2); + return [trim($key) => trim($value)]; + }) + ->all(); + switch ($plugin) { + case 'obfs': + $config[] = "obfs-name={$parsedOpts['obfs']}"; + if (isset($parsedOpts['obfs-host'])) { + $config[] = "obfs-host={$parsedOpts['obfs-host']}"; + } + if (isset($parsedOpts['path'])) { + $config[] = "obfs-uri={$parsedOpts['path']}"; + } + break; + } + } + + $config = array_filter($config); + $uri = implode(',', $config) . "\r\n"; + return $uri; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']}=vmess", + "{$server['host']}", + "{$server['port']}", + 'auto', + "{$uuid}", + 'fast-open=false', + 'udp=true', + "alterId=0" + ]; + + if (data_get($protocol_settings, 'tls')) { + $config[] = 'over-tls=true'; + if (data_get($protocol_settings, 'tls_settings')) { + $tls_settings = data_get($protocol_settings, 'tls_settings'); + $config[] = 'skip-cert-verify=' . (data_get($tls_settings, 'allow_insecure') ? 'true' : 'false'); + if (data_get($tls_settings, 'server_name')) + $config[] = "tls-name={$tls_settings['server_name']}"; + } + } + + switch (data_get($server['protocol_settings'], 'network')) { + case 'tcp': + $config[] = 'transport=tcp'; + $tcpSettings = data_get($protocol_settings, 'network_settings'); + if (data_get($tcpSettings, 'header.type')) + $config = str_replace('transport=tcp', "transport={$tcpSettings['header']['type']}", $config); + if (data_get($tcpSettings, key: 'header.request.path')) { + $paths = data_get($tcpSettings, key: 'header.request.path'); + $path = $paths[array_rand($paths)]; + $config[] = "path={$path}"; + } + if (data_get($tcpSettings, key: 'header.request.headers.Host')) { + $hosts = data_get($tcpSettings, key: 'header.request.headers.Host'); + $host = $hosts[array_rand($hosts)]; + $config[] = "host={$host}"; + } + break; + case 'ws': + $config[] = 'transport=ws'; + $wsSettings = data_get($protocol_settings, 'network_settings'); + if (data_get($wsSettings, key: 'path')) + $config[] = "path={$wsSettings['path']}"; + if (data_get($wsSettings, key: 'headers.Host')) + $config[] = "host={$wsSettings['headers']['Host']}"; + break; + case 'grpc': + $config[] = 'transport=grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $config[] = "grpc-service-name={$serviceName}"; + break; + case 'h2': + $config[] = 'transport=h2'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config[] = "path={$path}"; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $config[] = "host=" . (is_array($host) ? $host[0] : $host); + break; + case 'httpupgrade': + $config[] = 'transport=httpupgrade'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config[] = "path={$path}"; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $config[] = "host={$host}"; + break; + } + + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']}=trojan", + "{$server['host']}", + "{$server['port']}", + "{$password}", + ]; + + $tlsMode = (int) data_get($protocol_settings, 'tls', 1); + switch ($tlsMode) { + case 2: // Reality + if ($serverName = data_get($protocol_settings, 'reality_settings.server_name')) { + $config[] = "tls-name={$serverName}"; + } + if ($pubkey = data_get($protocol_settings, 'reality_settings.public_key')) { + $config[] = "public-key={$pubkey}"; + } + if ($shortid = data_get($protocol_settings, 'reality_settings.short_id')) { + $config[] = "short-id={$shortid}"; + } + $config[] = 'skip-cert-verify=' . (data_get($protocol_settings, 'reality_settings.allow_insecure', false) ? 'true' : 'false'); + break; + default: // Standard TLS + if ($serverName = data_get($protocol_settings, 'server_name')) { + $config[] = "tls-name={$serverName}"; + } + $config[] = 'skip-cert-verify=' . (data_get($protocol_settings, 'allow_insecure') ? 'true' : 'false'); + break; + } + + switch (data_get($protocol_settings, 'network', 'tcp')) { + case 'ws': + $config[] = 'transport=ws'; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $config[] = "path={$path}"; + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) + $config[] = "host={$host}"; + break; + case 'grpc': + $config[] = 'transport=grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $config[] = "grpc-service-name={$serviceName}"; + break; + } + + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildVless($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + + $config = [ + "{$server['name']}=VLESS", + "{$server['host']}", + "{$server['port']}", + "{$password}", + "alterId=0", + "udp=true" + ]; + + // flow + if ($flow = data_get($protocol_settings, 'flow')) { + $config[] = "flow={$flow}"; + } + + // TLS/Reality + switch (data_get($protocol_settings, 'tls')) { + case 1: + $config[] = "over-tls=true"; + $config[] = "skip-cert-verify=" . (data_get($protocol_settings, 'tls_settings.allow_insecure', false) ? "true" : "false"); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $config[] = "sni={$serverName}"; + } + break; + case 2: + $config[] = "over-tls=true"; + $config[] = "skip-cert-verify=" . (data_get($protocol_settings, 'reality_settings.allow_insecure', false) ? "true" : "false"); + if ($serverName = data_get($protocol_settings, 'reality_settings.server_name')) { + $config[] = "sni={$serverName}"; + } + if ($pubkey = data_get($protocol_settings, 'reality_settings.public_key')) { + $config[] = "public-key={$pubkey}"; + } + if ($shortid = data_get($protocol_settings, 'reality_settings.short_id')) { + $config[] = "short-id={$shortid}"; + } + break; + default: + $config[] = "over-tls=false"; + break; + } + + // network + switch (data_get($protocol_settings, 'network')) { + case 'ws': + $config[] = "transport=ws"; + if ($path = data_get($protocol_settings, 'network_settings.path')) { + $config[] = "path={$path}"; + } + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) { + $config[] = "host={$host}"; + } + break; + case 'grpc': + $config[] = "transport=grpc"; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) { + $config[] = "grpc-service-name={$serviceName}"; + } + break; + default: + $config[] = "transport=tcp"; + break; + } + + $config = array_filter($config); + $uri = implode(',', $config) . "\r\n"; + return $uri; + } + + public static function buildHysteria($password, $server, $user) + { + $protocol_settings = $server['protocol_settings']; + if ($protocol_settings['version'] != 2) { + return; + } + $config = [ + "{$server['name']}=Hysteria2", + $server['host'], + $server['port'], + $password, + $protocol_settings['tls']['server_name'] ? "sni={$protocol_settings['tls']['server_name']}" : "(null)" + ]; + if (data_get($protocol_settings, 'tls.allow_insecure')) + $config[] = "skip-cert-verify=true"; + if ($down = data_get($protocol_settings, 'bandwidth.down')) { + $config[] = "download-bandwidth={$down}"; + } + $config[] = "udp=true"; + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildAnyTLS($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + + $config = [ + "{$server['name']}=anytls", + "{$server['host']}", + "{$server['port']}", + "{$password}", + "udp=true" + ]; + + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $config[] = "sni={$serverName}"; + } + // ✅ 跳过证书校验 + if (data_get($protocol_settings, 'tls.allow_insecure')) { + $config[] = 'skip-cert-verify=true'; + } + + $config = array_filter($config); + + return implode(',', $config) . "\r\n"; + } +} diff --git a/Xboard/app/Protocols/QuantumultX.php b/Xboard/app/Protocols/QuantumultX.php new file mode 100644 index 0000000..8488e0a --- /dev/null +++ b/Xboard/app/Protocols/QuantumultX.php @@ -0,0 +1,232 @@ +servers; + $user = $this->user; + $uri = ''; + foreach ($servers as $item) { + $uri .= match ($item['type']) { + Server::TYPE_SHADOWSOCKS => self::buildShadowsocks($item['password'], $item), + Server::TYPE_VMESS => self::buildVmess($item['password'], $item), + Server::TYPE_VLESS => self::buildVless($item['password'], $item), + Server::TYPE_TROJAN => self::buildTrojan($item['password'], $item), + Server::TYPE_SOCKS => self::buildSocks5($item['password'], $item), + Server::TYPE_HTTP => self::buildHttp($item['password'], $item), + default => '' + }; + } + return response(base64_encode($uri)) + ->header('content-type', 'text/plain') + ->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}"); + } + + public static function buildShadowsocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $password = data_get($server, 'password', $password); + $addr = Helper::wrapIPv6($server['host']); + $config = [ + "shadowsocks={$addr}:{$server['port']}", + "method=" . data_get($protocol_settings, 'cipher'), + "password={$password}", + ]; + + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $plugin = data_get($protocol_settings, 'plugin'); + $pluginOpts = data_get($protocol_settings, 'plugin_opts', ''); + $parsedOpts = collect(explode(';', $pluginOpts)) + ->filter() + ->mapWithKeys(function ($pair) { + if (!str_contains($pair, '=')) { + return []; + } + [$key, $value] = explode('=', $pair, 2); + return [trim($key) => trim($value)]; + }) + ->all(); + if ($plugin === 'obfs') { + if (isset($parsedOpts['obfs'])) { + $config[] = "obfs={$parsedOpts['obfs']}"; + } + if (isset($parsedOpts['obfs-host'])) { + $config[] = "obfs-host={$parsedOpts['obfs-host']}"; + } + if (isset($parsedOpts['path'])) { + $config[] = "obfs-uri={$parsedOpts['path']}"; + } + } + } + + self::applyCommonSettings($config, $server); + + return implode(',', array_filter($config)) . "\r\n"; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $addr = Helper::wrapIPv6($server['host']); + $config = [ + "vmess={$addr}:{$server['port']}", + "method=" . data_get($protocol_settings, 'cipher', 'auto'), + "password={$uuid}", + ]; + + self::applyTransportSettings($config, $protocol_settings); + self::applyCommonSettings($config, $server); + + return implode(',', array_filter($config)) . "\r\n"; + } + + public static function buildVless($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $addr = Helper::wrapIPv6($server['host']); + $config = [ + "vless={$addr}:{$server['port']}", + 'method=none', + "password={$uuid}", + ]; + + self::applyTransportSettings($config, $protocol_settings); + + if ($flow = data_get($protocol_settings, 'flow')) { + $config[] = "vless-flow={$flow}"; + } + + self::applyCommonSettings($config, $server); + + return implode(',', array_filter($config)) . "\r\n"; + } + + private static function applyTransportSettings(&$config, $settings, bool $nativeTls = false, ?array $tlsData = null) + { + $tlsMode = (int) data_get($settings, 'tls', 0); + $network = data_get($settings, 'network', 'tcp'); + $host = null; + $isWs = $network === 'ws'; + + switch ($network) { + case 'ws': + $config[] = $tlsMode ? 'obfs=wss' : 'obfs=ws'; + if ($path = data_get($settings, 'network_settings.path')) { + $config[] = "obfs-uri={$path}"; + } + $host = data_get($settings, 'network_settings.headers.Host'); + break; + case 'tcp': + $headerType = data_get($settings, 'network_settings.header.type', 'tcp'); + if ($headerType === 'http') { + $config[] = 'obfs=http'; + $paths = data_get($settings, 'network_settings.header.request.path', ['/']); + $config[] = 'obfs-uri=' . (is_array($paths) ? ($paths[0] ?? '/') : $paths); + $hostVal = data_get($settings, 'network_settings.header.request.headers.Host'); + $host = is_array($hostVal) ? ($hostVal[0] ?? null) : $hostVal; + } elseif ($tlsMode) { + $config[] = $nativeTls ? 'over-tls=true' : 'obfs=over-tls'; + } + break; + } + + switch ($tlsMode) { + case 2: // Reality + $host = $host ?? data_get($settings, 'reality_settings.server_name'); + if ($pubKey = data_get($settings, 'reality_settings.public_key')) { + $config[] = "reality-base64-pubkey={$pubKey}"; + } + if ($shortId = data_get($settings, 'reality_settings.short_id')) { + $config[] = "reality-hex-shortid={$shortId}"; + } + break; + case 1: // TLS + $resolved = $tlsData ?? (array) data_get($settings, 'tls_settings', []); + $allowInsecure = (bool) ($resolved['allow_insecure'] ?? false); + $config[] = 'tls-verification=' . ($allowInsecure ? 'false' : 'true'); + $host = $host ?? ($resolved['server_name'] ?? null); + break; + } + + if ($host) { + $config[] = ($nativeTls && !$isWs) ? "tls-host={$host}" : "obfs-host={$host}"; + } + } + + private static function applyCommonSettings(&$config, $server) + { + $config[] = 'fast-open=true'; + if ($server['type'] !== Server::TYPE_HTTP) { + $config[] = 'udp-relay=true'; + } + $config[] = "tag={$server['name']}"; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $addr = Helper::wrapIPv6($server['host']); + $config = [ + "trojan={$addr}:{$server['port']}", + "password={$password}", + ]; + + $tlsData = [ + 'allow_insecure' => data_get($protocol_settings, 'allow_insecure', false), + 'server_name' => data_get($protocol_settings, 'server_name'), + ]; + self::applyTransportSettings($config, $protocol_settings, true, $tlsData); + self::applyCommonSettings($config, $server); + + return implode(',', array_filter($config)) . "\r\n"; + } + + public static function buildSocks5($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $addr = Helper::wrapIPv6($server['host']); + $config = [ + "socks5={$addr}:{$server['port']}", + "username={$password}", + "password={$password}", + ]; + + self::applyTransportSettings($config, $protocol_settings, true); + self::applyCommonSettings($config, $server); + + return implode(',', array_filter($config)) . "\r\n"; + } + + public static function buildHttp($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $addr = Helper::wrapIPv6($server['host']); + $config = [ + "http={$addr}:{$server['port']}", + "username={$password}", + "password={$password}", + ]; + + self::applyTransportSettings($config, $protocol_settings, true); + self::applyCommonSettings($config, $server); + + return implode(',', array_filter($config)) . "\r\n"; + } +} diff --git a/Xboard/app/Protocols/Shadowrocket.php b/Xboard/app/Protocols/Shadowrocket.php new file mode 100644 index 0000000..37028e3 --- /dev/null +++ b/Xboard/app/Protocols/Shadowrocket.php @@ -0,0 +1,415 @@ + [2 => '1993'], + 'shadowrocket.anytls.base_version' => '2592', + ]; + + public function handle() + { + $servers = $this->servers; + $user = $this->user; + + $uri = ''; + //display remaining traffic and expire date + $upload = round($user['u'] / (1024 * 1024 * 1024), 2); + $download = round($user['d'] / (1024 * 1024 * 1024), 2); + $totalTraffic = round($user['transfer_enable'] / (1024 * 1024 * 1024), 2); + $expiredDate = $user['expired_at'] === null ? 'N/A' : date('Y-m-d', $user['expired_at']); + $uri .= "STATUS=🚀↑:{$upload}GB,↓:{$download}GB,TOT:{$totalTraffic}GB💡Expires:{$expiredDate}\r\n"; + foreach ($servers as $item) { + if ($item['type'] === Server::TYPE_SHADOWSOCKS) { + $uri .= self::buildShadowsocks($item['password'], $item); + } + if ($item['type'] === Server::TYPE_VMESS) { + $uri .= self::buildVmess($item['password'], $item); + } + if ($item['type'] === Server::TYPE_VLESS) { + $uri .= self::buildVless($item['password'], $item); + } + if ($item['type'] === Server::TYPE_TROJAN) { + $uri .= self::buildTrojan($item['password'], $item); + } + if ($item['type'] === Server::TYPE_HYSTERIA) { + $uri .= self::buildHysteria($item['password'], $item); + } + if ($item['type'] === Server::TYPE_TUIC) { + $uri .= self::buildTuic($item['password'], $item); + } + if ($item['type'] === Server::TYPE_ANYTLS) { + $uri .= self::buildAnyTLS($item['password'], $item); + } + if ($item['type'] === Server::TYPE_SOCKS) { + $uri .= self::buildSocks($item['password'], $item); + } + } + return response(base64_encode($uri)) + ->header('content-type', 'text/plain'); + } + + + public static function buildShadowsocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $password = data_get($server, 'password', $password); + $str = str_replace( + ['+', '/', '='], + ['-', '_', ''], + base64_encode(data_get($protocol_settings, 'cipher') . ":{$password}") + ); + $addr = Helper::wrapIPv6($server['host']); + + $uri = "ss://{$str}@{$addr}:{$server['port']}"; + $plugin = data_get($protocol_settings, 'plugin') == 'obfs' ? 'obfs-local' : data_get($protocol_settings, 'plugin'); + $plugin_opts = data_get($protocol_settings, 'plugin_opts'); + if ($plugin && $plugin_opts) { + $uri .= '/?' . 'plugin=' . $plugin . ';' . rawurlencode($plugin_opts); + } + return $uri . "#{$name}\r\n"; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $userinfo = base64_encode('auto:' . $uuid . '@' . Helper::wrapIPv6($server['host']) . ':' . $server['port']); + $config = [ + 'tfo' => 1, + 'remark' => $server['name'], + 'alterId' => 0 + ]; + if (data_get($protocol_settings, 'tls')) { + $config['tls'] = 1; + if (data_get($protocol_settings, 'tls_settings')) { + if (!!data_get($protocol_settings, 'tls_settings.allow_insecure')) + $config['allowInsecure'] = (int) data_get($protocol_settings, 'tls_settings.allow_insecure'); + if (!!data_get($protocol_settings, 'tls_settings.server_name')) + $config['peer'] = data_get($protocol_settings, 'tls_settings.server_name'); + } + } + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + if (data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none') { + $config['obfs'] = data_get($protocol_settings, 'network_settings.header.type'); + $config['path'] = \Illuminate\Support\Arr::random(data_get($protocol_settings, 'network_settings.header.request.path', ['/'])); + $config['obfsParam'] = \Illuminate\Support\Arr::random(data_get($protocol_settings, 'network_settings.header.request.headers.Host', ['www.example.com'])); + } + break; + case 'ws': + $config['obfs'] = "websocket"; + $config['path'] = data_get($protocol_settings, 'network_settings.path'); + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) { + $config['obfsParam'] = $host; + } + break; + case 'grpc': + $config['obfs'] = "grpc"; + $config['path'] = data_get($protocol_settings, 'network_settings.serviceName'); + $config['host'] = data_get($protocol_settings, 'tls_settings.server_name') ?? $server['host']; + break; + case 'httpupgrade': + $config['obfs'] = "httpupgrade"; + if ($path = data_get($protocol_settings, 'network_settings.path')) { + $config['path'] = $path; + } + if ($host = data_get($protocol_settings, 'network_settings.host', $server['host'])) { + $config['obfsParam'] = $host; + } + break; + case 'h2': + $config['obfs'] = "h2"; + if ($path = data_get($protocol_settings, 'network_settings.path')) { + $config['path'] = $path; + } + if ($host = data_get($protocol_settings, 'network_settings.host')) { + $config['obfsParam'] = $host[0] ?? $server['host']; + $config['peer'] = $host [0] ?? $server['host']; + } + break; + } + $query = http_build_query($config, '', '&', PHP_QUERY_RFC3986); + $uri = "vmess://{$userinfo}?{$query}"; + $uri .= "\r\n"; + return $uri; + } + + public static function buildVless($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $userinfo = base64_encode('auto:' . $uuid . '@' . Helper::wrapIPv6($server['host']) . ':' . $server['port']); + $config = [ + 'tfo' => 1, + 'remark' => $server['name'], + 'alterId' => 0 + ]; + + // 判断是否开启xtls + if (data_get($protocol_settings, 'flow')) { + $xtlsMap = [ + 'none' => 0, + 'xtls-rprx-direct' => 1, + 'xtls-rprx-vision' => 2 + ]; + if (array_key_exists(data_get($protocol_settings, 'flow'), $xtlsMap)) { + $config['tls'] = 1; + $config['xtls'] = $xtlsMap[data_get($protocol_settings, 'flow')]; + } + } + switch (data_get($protocol_settings, 'tls')) { + case 1: + $config['tls'] = 1; + $config['allowInsecure'] = (int) data_get($protocol_settings, 'tls_settings.allow_insecure'); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $config['peer'] = $serverName; + } + if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $config['fp'] = $fp; + } + break; + case 2: + $config['tls'] = 1; + $config['sni'] = data_get($protocol_settings, 'reality_settings.server_name'); + $config['pbk'] = data_get($protocol_settings, 'reality_settings.public_key'); + $config['sid'] = data_get($protocol_settings, 'reality_settings.short_id'); + if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $config['fp'] = $fp; + } + break; + default: + break; + } + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + if (data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none') { + $config['obfs'] = data_get($protocol_settings, 'network_settings.header.type'); + $config['path'] = \Illuminate\Support\Arr::random(data_get($protocol_settings, 'network_settings.header.request.path', ['/'])); + $config['obfsParam'] = \Illuminate\Support\Arr::random(data_get($protocol_settings, 'network_settings.header.request.headers.Host', ['www.example.com'])); + } + break; + case 'ws': + $config['obfs'] = "websocket"; + if (data_get($protocol_settings, 'network_settings.path')) { + $config['path'] = data_get($protocol_settings, 'network_settings.path'); + } + + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) { + $config['obfsParam'] = $host; + } + break; + case 'grpc': + $config['obfs'] = "grpc"; + $config['path'] = data_get($protocol_settings, 'network_settings.serviceName'); + $config['host'] = data_get($protocol_settings, 'tls_settings.server_name') ?? $server['host']; + break; + case 'kcp': + $config['obfs'] = "kcp"; + if ($seed = data_get($protocol_settings, 'network_settings.seed')) { + $config['path'] = $seed; + } + $config['type'] = data_get($protocol_settings, 'network_settings.header.type', 'none'); + break; + case 'h2': + $config['obfs'] = "h2"; + if ($path = data_get($protocol_settings, 'network_settings.path')) { + $config['path'] = $path; + } + if ($host = data_get($protocol_settings, 'network_settings.host', $server['host'])) { + $config['obfsParam'] = $host; + } + break; + case 'httpupgrade': + $config['obfs'] = "httpupgrade"; + if ($path = data_get($protocol_settings, 'network_settings.path')) { + $config['path'] = $path; + } + if ($host = data_get($protocol_settings, 'network_settings.host', $server['host'])) { + $config['obfsParam'] = $host; + } + break; + case 'xhttp': + $config['obfs'] = "xhttp"; + if ($path = data_get($protocol_settings, 'network_settings.path')) { + $config['path'] = $path; + } + if ($host = data_get($protocol_settings, 'network_settings.host', $server['host'])) { + $config['obfsParam'] = $host; + } + if ($mode = data_get($protocol_settings, 'network_settings.mode', 'auto')) { + $config['mode'] = $mode; + } + break; + } + + $query = http_build_query($config, '', '&', PHP_QUERY_RFC3986); + $uri = "vless" . "://{$userinfo}?{$query}"; + $uri .= "\r\n"; + return $uri; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $params = []; + $tlsMode = (int) data_get($protocol_settings, 'tls', 1); + + switch ($tlsMode) { + case 2: // Reality + $params['security'] = 'reality'; + $params['pbk'] = data_get($protocol_settings, 'reality_settings.public_key'); + $params['sid'] = data_get($protocol_settings, 'reality_settings.short_id'); + $params['sni'] = data_get($protocol_settings, 'reality_settings.server_name'); + break; + default: // Standard TLS + $params['allowInsecure'] = data_get($protocol_settings, 'allow_insecure'); + if ($serverName = data_get($protocol_settings, 'server_name')) { + $params['peer'] = $serverName; + } + break; + } + + switch (data_get($protocol_settings, 'network')) { + case 'grpc': + $params['obfs'] = 'grpc'; + $params['path'] = data_get($protocol_settings, 'network_settings.serviceName'); + break; + case 'ws': + $host = data_get($protocol_settings, 'network_settings.headers.Host'); + $path = data_get($protocol_settings, 'network_settings.path'); + $params['plugin'] = "obfs-local;obfs=websocket;obfs-host={$host};obfs-uri={$path}"; + break; + } + $query = http_build_query($params); + $addr = Helper::wrapIPv6($server['host']); + + $uri = "trojan://{$password}@{$addr}:{$server['port']}?{$query}&tfo=1#{$name}"; + $uri .= "\r\n"; + return $uri; + } + + public static function buildHysteria($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $uri = ''; // 初始化变量 + + switch (data_get($protocol_settings, 'version')) { + case 1: + $params = [ + "auth" => $password, + "upmbps" => data_get($protocol_settings, 'bandwidth.up'), + "downmbps" => data_get($protocol_settings, 'bandwidth.down'), + "protocol" => 'udp', + "fastopen" => 1, + ]; + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $params['peer'] = $serverName; + } + if (data_get($protocol_settings, 'obfs.open')) { + $params["obfs"] = "xplus"; + $params["obfsParam"] = data_get($protocol_settings, 'obfs.password'); + } + $params['insecure'] = data_get($protocol_settings, 'tls.allow_insecure'); + if (isset($server['ports'])) + $params['mport'] = $server['ports']; + $query = http_build_query($params); + $addr = Helper::wrapIPv6($server['host']); + + $uri = "hysteria://{$addr}:{$server['port']}?{$query}#{$server['name']}"; + $uri .= "\r\n"; + break; + case 2: + $params = [ + "obfs" => 'none', + "fastopen" => 1 + ]; + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $params['peer'] = $serverName; + } + if (data_get($protocol_settings, 'obfs.open')) { + $params['obfs'] = data_get($protocol_settings, 'obfs.type'); + $params['obfs-password'] = data_get($protocol_settings, 'obfs.password'); + } + $params['insecure'] = data_get($protocol_settings, 'tls.allow_insecure'); + if (isset($protocol_settings['hop_interval'])) { + $params['keepalive'] = data_get($protocol_settings, 'hop_interval'); + } + if (isset($server['ports'])) { + $params['mport'] = $server['ports']; + } + $query = http_build_query($params); + $addr = Helper::wrapIPv6($server['host']); + + $uri = "hysteria2://{$password}@{$addr}:{$server['port']}?{$query}#{$server['name']}"; + $uri .= "\r\n"; + break; + } + return $uri; + } + public static function buildTuic($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $params = [ + 'alpn' => data_get($protocol_settings, 'alpn'), + 'sni' => data_get($protocol_settings, 'tls.server_name'), + 'insecure' => data_get($protocol_settings, 'tls.allow_insecure') + ]; + if (data_get($protocol_settings, 'version') === 4) { + $params['token'] = $password; + } else { + $params['uuid'] = $password; + $params['password'] = $password; + } + $query = http_build_query($params); + $addr = Helper::wrapIPv6($server['host']); + $uri = "tuic://{$addr}:{$server['port']}?{$query}#{$name}"; + $uri .= "\r\n"; + return $uri; + } + + public static function buildAnyTLS($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $params = [ + 'sni' => data_get($protocol_settings, 'tls.server_name'), + 'insecure' => data_get($protocol_settings, 'tls.allow_insecure') + ]; + $query = http_build_query($params); + $addr = Helper::wrapIPv6($server['host']); + $uri = "anytls://{$password}@{$addr}:{$server['port']}?{$query}#{$name}"; + $uri .= "\r\n"; + return $uri; + } + + public static function buildSocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $name = rawurlencode($server['name']); + $addr = Helper::wrapIPv6($server['host']); + $uri = 'socks://' . base64_encode("{$password}:{$password}@{$addr}:{$server['port']}") . "?method=auto#{$name}"; + $uri .= "\r\n"; + return $uri; + } +} diff --git a/Xboard/app/Protocols/Shadowsocks.php b/Xboard/app/Protocols/Shadowsocks.php new file mode 100644 index 0000000..36f2d2a --- /dev/null +++ b/Xboard/app/Protocols/Shadowsocks.php @@ -0,0 +1,60 @@ +servers; + $user = $this->user; + + $configs = []; + $subs = []; + $subs['servers'] = []; + $subs['bytes_used'] = ''; + $subs['bytes_remaining'] = ''; + + $bytesUsed = $user['u'] + $user['d']; + $bytesRemaining = $user['transfer_enable'] - $bytesUsed; + + foreach ($servers as $item) { + if ( + $item['type'] === 'shadowsocks' + && in_array(data_get($item, 'protocol_settings.cipher'), ['aes-128-gcm', 'aes-256-gcm', 'aes-192-gcm', 'chacha20-ietf-poly1305']) + ) { + array_push($configs, self::SIP008($item, $user)); + } + } + + $subs['version'] = 1; + $subs['bytes_used'] = $bytesUsed; + $subs['bytes_remaining'] = $bytesRemaining; + $subs['servers'] = array_merge($subs['servers'], $configs); + + return response()->json($subs) + ->header('content-type', 'application/json'); + } + + public static function SIP008($server, $user) + { + $config = [ + "id" => $server['id'], + "remarks" => $server['name'], + "server" => $server['host'], + "server_port" => $server['port'], + "password" => $server['password'], + "method" => data_get($server, 'protocol_settings.cipher') + ]; + return $config; + } +} diff --git a/Xboard/app/Protocols/SingBox.php b/Xboard/app/Protocols/SingBox.php new file mode 100644 index 0000000..b412787 --- /dev/null +++ b/Xboard/app/Protocols/SingBox.php @@ -0,0 +1,757 @@ + [ + 'vless' => [ + 'base_version' => '1.5.0', + 'protocol_settings.flow' => [ + 'xtls-rprx-vision' => '1.5.0' + ], + 'protocol_settings.tls' => [ + '2' => '1.6.0' // Reality + ] + ], + 'hysteria' => [ + 'base_version' => '1.5.0', + 'protocol_settings.version' => [ + '2' => '1.5.0' // Hysteria 2 + ] + ], + 'tuic' => [ + 'base_version' => '1.5.0' + ], + 'ssh' => [ + 'base_version' => '1.8.0' + ], + 'juicity' => [ + 'base_version' => '1.7.0' + ], + 'wireguard' => [ + 'base_version' => '1.5.0' + ], + 'anytls' => [ + 'base_version' => '1.12.0' + ], + ] + ]; + + public function handle() + { + $appName = admin_setting('app_name', 'XBoard'); + $this->config = $this->loadConfig(); + $this->buildOutbounds(); + $this->buildRule(); + $this->adaptConfigForVersion(); + $user = $this->user; + + return response() + ->json($this->config) + ->header('profile-title', 'base64:' . base64_encode($appName)) + ->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}") + ->header('profile-update-interval', '24'); + } + + protected function loadConfig() + { + $jsonData = subscribe_template('singbox'); + + return is_array($jsonData) ? $jsonData : json_decode($jsonData, true); + } + + protected function buildOutbounds() + { + $outbounds = $this->config['outbounds']; + $proxies = []; + foreach ($this->servers as $item) { + $protocol_settings = $item['protocol_settings']; + if ($item['type'] === Server::TYPE_SHADOWSOCKS) { + $ssConfig = $this->buildShadowsocks($item['password'], $item); + $proxies[] = $ssConfig; + } + if ($item['type'] === Server::TYPE_TROJAN) { + $trojanConfig = $this->buildTrojan($this->user['uuid'], $item); + $proxies[] = $trojanConfig; + } + if ($item['type'] === Server::TYPE_VMESS) { + $vmessConfig = $this->buildVmess($this->user['uuid'], $item); + $proxies[] = $vmessConfig; + } + if ( + $item['type'] === Server::TYPE_VLESS + && in_array(data_get($protocol_settings, 'network'), ['tcp', 'ws', 'grpc', 'http', 'quic', 'httpupgrade']) + ) { + $vlessConfig = $this->buildVless($this->user['uuid'], $item); + $proxies[] = $vlessConfig; + } + if ($item['type'] === Server::TYPE_HYSTERIA) { + $hysteriaConfig = $this->buildHysteria($this->user['uuid'], $item); + $proxies[] = $hysteriaConfig; + } + if ($item['type'] === Server::TYPE_TUIC) { + $tuicConfig = $this->buildTuic($this->user['uuid'], $item); + $proxies[] = $tuicConfig; + } + if ($item['type'] === Server::TYPE_ANYTLS) { + $anytlsConfig = $this->buildAnyTLS($this->user['uuid'], $item); + $proxies[] = $anytlsConfig; + } + if ($item['type'] === Server::TYPE_SOCKS) { + $socksConfig = $this->buildSocks($this->user['uuid'], $item); + $proxies[] = $socksConfig; + } + if ($item['type'] === Server::TYPE_HTTP) { + $httpConfig = $this->buildHttp($this->user['uuid'], $item); + $proxies[] = $httpConfig; + } + } + foreach ($outbounds as &$outbound) { + if (in_array($outbound['type'], ['urltest', 'selector'])) { + array_push($outbound['outbounds'], ...array_column($proxies, 'tag')); + } + } + + $outbounds = array_merge($outbounds, $proxies); + $this->config['outbounds'] = $outbounds; + return $outbounds; + } + + /** + * Build rule + */ + protected function buildRule() + { + $rules = $this->config['route']['rules']; + $this->config['route']['rules'] = $rules; + } + + /** + * 根据客户端版本自适应配置格式 + * 模板基准格式: 1.13.0+ (最新) + */ + protected function adaptConfigForVersion(): void + { + $coreVersion = $this->getSingBoxCoreVersion(); + if (empty($coreVersion)) { + return; + } + + // >= 1.13.0: 移除已删除的 block/dns 出站 + if (version_compare($coreVersion, '1.13.0', '>=')) { + $this->upgradeSpecialOutboundsToActions(); + } + + // < 1.11.0: rule action 降级为旧出站; 恢复废弃字段 + if (version_compare($coreVersion, '1.11.0', '<')) { + $this->downgradeActionsToSpecialOutbounds(); + $this->restoreDeprecatedInboundFields(); + } + + // < 1.12.0: DNS type+server → 旧 address 格式 + if (version_compare($coreVersion, '1.12.0', '<')) { + $this->convertDnsServersToLegacy(); + } + + // < 1.10.0: tun address 数组 → inet4_address/inet6_address + if (version_compare($coreVersion, '1.10.0', '<')) { + $this->convertTunAddressToLegacy(); + } + } + + /** + * 获取核心版本 (Hiddify/SFM 等映射到内核版本) + */ + private function getSingBoxCoreVersion(): ?string + { + // 优先从 UA 提取核心版本 + if (!empty($this->userAgent)) { + if (preg_match('/sing-box\s+v?(\d+(?:\.\d+){0,2})/i', $this->userAgent, $matches)) { + return $matches[1]; + } + } + + if (empty($this->clientVersion)) { + return null; + } + + if ($this->clientName === 'sing-box') { + return $this->clientVersion; + } + + return '1.13.0'; + } + + /** + * sing-box >= 1.13.0: block/dns 出站升级为 action + */ + private function upgradeSpecialOutboundsToActions(): void + { + $removedTags = []; + $this->config['outbounds'] = array_values(array_filter( + $this->config['outbounds'] ?? [], + function ($outbound) use (&$removedTags) { + if (in_array($outbound['type'] ?? '', ['block', 'dns'])) { + $removedTags[$outbound['tag']] = $outbound['type']; + return false; + } + return true; + } + )); + + if (empty($removedTags)) { + return; + } + + if (isset($this->config['route']['rules'])) { + foreach ($this->config['route']['rules'] as &$rule) { + if (!isset($rule['outbound']) || !isset($removedTags[$rule['outbound']])) { + continue; + } + $type = $removedTags[$rule['outbound']]; + unset($rule['outbound']); + $rule['action'] = $type === 'dns' ? 'hijack-dns' : 'reject'; + } + unset($rule); + } + } + + /** + * sing-box < 1.11.0: rule action 降级为旧 block/dns 出站 + */ + private function downgradeActionsToSpecialOutbounds(): void + { + $needsDnsOutbound = false; + $needsBlockOutbound = false; + + if (isset($this->config['route']['rules'])) { + foreach ($this->config['route']['rules'] as &$rule) { + if (!isset($rule['action'])) { + continue; + } + switch ($rule['action']) { + case 'hijack-dns': + unset($rule['action']); + $rule['outbound'] = 'dns-out'; + $needsDnsOutbound = true; + break; + case 'reject': + unset($rule['action']); + $rule['outbound'] = 'block'; + $needsBlockOutbound = true; + break; + } + } + unset($rule); + } + + if ($needsBlockOutbound) { + $this->config['outbounds'][] = ['type' => 'block', 'tag' => 'block']; + } + if ($needsDnsOutbound) { + $this->config['outbounds'][] = ['type' => 'dns', 'tag' => 'dns-out']; + } + } + + /** + * sing-box < 1.11.0: 恢复废弃的入站字段 + */ + private function restoreDeprecatedInboundFields(): void + { + if (!isset($this->config['inbounds'])) { + return; + } + foreach ($this->config['inbounds'] as &$inbound) { + if ($inbound['type'] === 'tun') { + $inbound['endpoint_independent_nat'] = true; + } + if (!empty($inbound['sniff'])) { + $inbound['sniff_override_destination'] = true; + } + } + } + + /** + * sing-box < 1.12.0: 将新 DNS server type+server 格式转换为旧 address 格式 + */ + private function convertDnsServersToLegacy(): void + { + if (!isset($this->config['dns']['servers'])) { + return; + } + foreach ($this->config['dns']['servers'] as &$server) { + if (!isset($server['type'])) { + continue; + } + $type = $server['type']; + $host = $server['server'] ?? null; + switch ($type) { + case 'https': + $server['address'] = "https://{$host}/dns-query"; + break; + case 'tls': + $server['address'] = "tls://{$host}"; + break; + case 'tcp': + $server['address'] = "tcp://{$host}"; + break; + case 'quic': + $server['address'] = "quic://{$host}"; + break; + case 'udp': + $server['address'] = $host; + break; + case 'block': + $server['address'] = 'rcode://refused'; + break; + case 'rcode': + $server['address'] = 'rcode://' . ($server['rcode'] ?? 'success'); + unset($server['rcode']); + break; + default: + $server['address'] = $host; + break; + } + unset($server['type'], $server['server']); + } + unset($server); + } + + /** + * sing-box < 1.10.0: 将 tun address 数组转换为 inet4_address/inet6_address + */ + private function convertTunAddressToLegacy(): void + { + if (!isset($this->config['inbounds'])) { + return; + } + foreach ($this->config['inbounds'] as &$inbound) { + if ($inbound['type'] !== 'tun' || !isset($inbound['address'])) { + continue; + } + foreach ($inbound['address'] as $addr) { + if (str_contains($addr, ':')) { + $inbound['inet6_address'] = $addr; + } else { + $inbound['inet4_address'] = $addr; + } + } + unset($inbound['address']); + } + } + + protected function buildShadowsocks($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings'); + $array = []; + $array['tag'] = $server['name']; + $array['type'] = 'shadowsocks'; + $array['server'] = $server['host']; + $array['server_port'] = $server['port']; + $array['method'] = data_get($protocol_settings, 'cipher'); + $array['password'] = data_get($server, 'password', $password); + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $array['plugin'] = data_get($protocol_settings, 'plugin'); + $array['plugin_opts'] = data_get($protocol_settings, 'plugin_opts', ''); + } + + return $array; + } + + + protected function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = [ + 'tag' => $server['name'], + 'type' => 'vmess', + 'server' => $server['host'], + 'server_port' => $server['port'], + 'uuid' => $uuid, + 'security' => 'auto', + 'alter_id' => 0, + ]; + + if ($protocol_settings['tls']) { + $array['tls'] = [ + 'enabled' => true, + 'insecure' => (bool) data_get($protocol_settings, 'tls_settings.allow_insecure'), + ]; + + $this->appendUtls($array['tls'], $protocol_settings); + + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['tls']['server_name'] = $serverName; + } + } + + $this->appendMultiplex($array, $protocol_settings); + + if ($transport = $this->buildTransport($protocol_settings, $server)) { + $array['transport'] = $transport; + } + return $array; + } + + protected function buildVless($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + "type" => "vless", + "tag" => $server['name'], + "server" => $server['host'], + "server_port" => $server['port'], + "uuid" => $password, + "packet_encoding" => "xudp", + ]; + if ($flow = data_get($protocol_settings, 'flow')) { + $array['flow'] = $flow; + } + + if (data_get($protocol_settings, 'tls')) { + $tlsMode = (int) data_get($protocol_settings, 'tls', 0); + $tlsConfig = [ + 'enabled' => true, + 'insecure' => $tlsMode === 2 + ? (bool) data_get($protocol_settings, 'reality_settings.allow_insecure', false) + : (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false), + ]; + + $this->appendUtls($tlsConfig, $protocol_settings); + + switch ($tlsMode) { + case 1: + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $tlsConfig['server_name'] = $serverName; + } + break; + case 2: + $tlsConfig['server_name'] = data_get($protocol_settings, 'reality_settings.server_name'); + $tlsConfig['reality'] = [ + 'enabled' => true, + 'public_key' => data_get($protocol_settings, 'reality_settings.public_key'), + 'short_id' => data_get($protocol_settings, 'reality_settings.short_id') + ]; + break; + } + + $array['tls'] = $tlsConfig; + } + + $this->appendMultiplex($array, $protocol_settings); + + if ($transport = $this->buildTransport($protocol_settings, $server)) { + $array['transport'] = $transport; + } + + return $array; + } + + protected function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = [ + 'tag' => $server['name'], + 'type' => 'trojan', + 'server' => $server['host'], + 'server_port' => $server['port'], + 'password' => $password, + ]; + + $tlsMode = (int) data_get($protocol_settings, 'tls', 1); + $tlsConfig = ['enabled' => true]; + + switch ($tlsMode) { + case 2: // Reality + $tlsConfig['insecure'] = (bool) data_get($protocol_settings, 'reality_settings.allow_insecure', false); + $tlsConfig['server_name'] = data_get($protocol_settings, 'reality_settings.server_name'); + $tlsConfig['reality'] = [ + 'enabled' => true, + 'public_key' => data_get($protocol_settings, 'reality_settings.public_key'), + 'short_id' => data_get($protocol_settings, 'reality_settings.short_id'), + ]; + break; + default: // Standard TLS + $tlsConfig['insecure'] = (bool) data_get($protocol_settings, 'allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'server_name')) { + $tlsConfig['server_name'] = $serverName; + } + break; + } + + $this->appendUtls($tlsConfig, $protocol_settings); + $array['tls'] = $tlsConfig; + + $this->appendMultiplex($array, $protocol_settings); + + if ($transport = $this->buildTransport($protocol_settings, $server)) { + $array['transport'] = $transport; + } + return $array; + } + + protected function buildHysteria($password, $server): array + { + $protocol_settings = $server['protocol_settings']; + $baseConfig = [ + 'server' => $server['host'], + 'server_port' => $server['port'], + 'tag' => $server['name'], + 'tls' => [ + 'enabled' => true, + 'insecure' => (bool) data_get($protocol_settings, 'tls.allow_insecure', false), + ] + ]; + // 支持 1.11.0 版本及以上 `server_ports` 和 `hop_interval` 配置 + if ($this->supportsFeature('sing-box', '1.11.0')) { + if (isset($server['ports'])) { + $baseConfig['server_ports'] = [str_replace('-', ':', $server['ports'])]; + } + if (isset($protocol_settings['hop_interval'])) { + $baseConfig['hop_interval'] = "{$protocol_settings['hop_interval']}s"; + } + } + + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $baseConfig['tls']['server_name'] = $serverName; + } + $speedConfig = [ + 'up_mbps' => data_get($protocol_settings, 'bandwidth.up'), + 'down_mbps' => data_get($protocol_settings, 'bandwidth.down'), + ]; + $versionConfig = match (data_get($protocol_settings, 'version', 1)) { + 2 => [ + 'type' => 'hysteria2', + 'password' => $password, + 'obfs' => data_get($protocol_settings, 'obfs.open') ? [ + 'type' => data_get($protocol_settings, 'obfs.type'), + 'password' => data_get($protocol_settings, 'obfs.password') + ] : null, + ], + default => [ + 'type' => 'hysteria', + 'auth_str' => $password, + 'obfs' => data_get($protocol_settings, 'obfs.password'), + 'disable_mtu_discovery' => true, + ] + }; + + return array_filter( + array_merge($baseConfig, $speedConfig, $versionConfig), + fn($v) => !is_null($v) + ); + } + + protected function buildTuic($password, $server): array + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'type' => 'tuic', + 'tag' => $server['name'], + 'server' => $server['host'], + 'server_port' => $server['port'], + 'congestion_control' => data_get($protocol_settings, 'congestion_control', 'cubic'), + 'udp_relay_mode' => data_get($protocol_settings, 'udp_relay_mode', 'native'), + 'zero_rtt_handshake' => true, + 'heartbeat' => '10s', + 'tls' => [ + 'enabled' => true, + 'insecure' => (bool) data_get($protocol_settings, 'tls.allow_insecure', false), + 'alpn' => data_get($protocol_settings, 'alpn', ['h3']), + ] + ]; + + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $array['tls']['server_name'] = $serverName; + } + + if (data_get($protocol_settings, 'version') === 4) { + $array['token'] = $password; + } else { + $array['uuid'] = $password; + $array['password'] = $password; + } + + return $array; + } + + protected function buildAnyTLS($password, $server): array + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'type' => 'anytls', + 'tag' => $server['name'], + 'server' => $server['host'], + 'password' => $password, + 'server_port' => $server['port'], + 'tls' => [ + 'enabled' => true, + 'insecure' => (bool) data_get($protocol_settings, 'tls.allow_insecure', false), + 'alpn' => data_get($protocol_settings, 'alpn', ['h3']), + ] + ]; + + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $array['tls']['server_name'] = $serverName; + } + + return $array; + } + + protected function buildSocks($password, $server): array + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'type' => 'socks', + 'tag' => $server['name'], + 'server' => $server['host'], + 'server_port' => $server['port'], + 'version' => '5', // 默认使用 socks5 + 'username' => $password, + 'password' => $password, + ]; + + if (data_get($protocol_settings, 'udp_over_tcp')) { + $array['udp_over_tcp'] = true; + } + + return $array; + } + + protected function buildHttp($password, $server): array + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'type' => 'http', + 'tag' => $server['name'], + 'server' => $server['host'], + 'server_port' => $server['port'], + 'username' => $password, + 'password' => $password, + ]; + + if ($path = data_get($protocol_settings, 'path')) { + $array['path'] = $path; + } + + if ($headers = data_get($protocol_settings, 'headers')) { + $array['headers'] = $headers; + } + + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = [ + 'enabled' => true, + 'insecure' => (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false), + ]; + + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['tls']['server_name'] = $serverName; + } + } + + return $array; + } + + protected function buildTransport(array $protocol_settings, array $server): ?array + { + $transport = match (data_get($protocol_settings, 'network')) { + 'tcp' => data_get($protocol_settings, 'network_settings.header.type') === 'http' ? [ + 'type' => 'http', + 'path' => Arr::random(data_get($protocol_settings, 'network_settings.header.request.path', ['/'])), + 'host' => data_get($protocol_settings, 'network_settings.header.request.headers.Host', []) + ] : null, + 'ws' => [ + 'type' => 'ws', + 'path' => data_get($protocol_settings, 'network_settings.path'), + 'headers' => ($host = data_get($protocol_settings, 'network_settings.headers.Host')) ? ['Host' => $host] : null, + 'max_early_data' => 0, + // 'early_data_header_name' => 'Sec-WebSocket-Protocol' + ], + 'grpc' => [ + 'type' => 'grpc', + 'service_name' => data_get($protocol_settings, 'network_settings.serviceName') + ], + 'h2' => [ + 'type' => 'http', + 'host' => data_get($protocol_settings, 'network_settings.host'), + 'path' => data_get($protocol_settings, 'network_settings.path') + ], + 'httpupgrade' => [ + 'type' => 'httpupgrade', + 'path' => data_get($protocol_settings, 'network_settings.path'), + 'host' => data_get($protocol_settings, 'network_settings.host', $server['host']), + 'headers' => data_get($protocol_settings, 'network_settings.headers') + ], + 'quic' => ['type' => 'quic'], + default => null + }; + + if (!$transport) { + return null; + } + + return array_filter($transport, fn($v) => !is_null($v)); + } + + protected function appendMultiplex(&$array, $protocol_settings) + { + if ($multiplex = data_get($protocol_settings, 'multiplex')) { + if (data_get($multiplex, 'enabled')) { + $array['multiplex'] = [ + 'enabled' => true, + 'protocol' => data_get($multiplex, 'protocol', 'yamux'), + 'max_connections' => data_get($multiplex, 'max_connections'), + 'min_streams' => data_get($multiplex, 'min_streams'), + 'max_streams' => data_get($multiplex, 'max_streams'), + 'padding' => (bool) data_get($multiplex, 'padding', false), + ]; + if (data_get($multiplex, 'brutal.enabled')) { + $array['multiplex']['brutal'] = [ + 'enabled' => true, + 'up_mbps' => data_get($multiplex, 'brutal.up_mbps'), + 'down_mbps' => data_get($multiplex, 'brutal.down_mbps'), + ]; + } + $array['multiplex'] = array_filter($array['multiplex'], fn($v) => !is_null($v)); + } + } + } + + protected function appendUtls(&$tlsConfig, $protocol_settings) + { + if ($utls = data_get($protocol_settings, 'utls')) { + if (data_get($utls, 'enabled')) { + $tlsConfig['utls'] = [ + 'enabled' => true, + 'fingerprint' => Helper::getTlsFingerprint($utls) + ]; + } + } + } +} diff --git a/Xboard/app/Protocols/Stash.php b/Xboard/app/Protocols/Stash.php new file mode 100644 index 0000000..8a3eadc --- /dev/null +++ b/Xboard/app/Protocols/Stash.php @@ -0,0 +1,587 @@ + [ + 'trojan' => [ + 'protocol_settings.tls' => [ + '2' => '9999.0.0', // Trojan Reality not supported in Stash + ], + ], + 'vmess' => [ + 'protocol_settings.network' => [ + 'httpupgrade' => '9999.0.0', // httpupgrade not supported in Stash + ], + ], + ], + 'stash' => [ + 'anytls' => [ + 'base_version' => '3.3.0' // AnyTLS 协议在3.3.0版本中添加 + ], + 'vless' => [ + 'protocol_settings.tls' => [ + '2' => '3.1.0' // Reality 在3.1.0版本中添加 + ], + 'protocol_settings.flow' => [ + 'xtls-rprx-vision' => '3.1.0', + ] + ], + 'hysteria' => [ + 'base_version' => '2.0.0', + 'protocol_settings.version' => [ + '1' => '2.0.0', // Hysteria 1 + '2' => '2.5.0' // Hysteria 2,2.5.0 版本开始支持(2023年11月8日) + ], + // 'protocol_settings.ports' => [ + // 'true' => '2.6.4' // Hysteria 2 端口跳转功能于2.6.4版本支持(2024年8月4日) + // ] + ], + 'tuic' => [ + 'base_version' => '2.3.0' // TUIC 协议自身需要 2.3.0+ + ], + 'shadowsocks' => [ + 'base_version' => '2.0.0', + // ShadowSocks2022 在3.0.0版本中添加(2025年4月2日) + 'protocol_settings.cipher' => [ + '2022-blake3-aes-128-gcm' => '3.0.0', + '2022-blake3-aes-256-gcm' => '3.0.0', + '2022-blake3-chacha20-poly1305' => '3.0.0' + ] + ], + 'shadowtls' => [ + 'base_version' => '3.0.0' // ShadowTLS 在3.0.0版本中添加(2025年4月2日) + ], + 'ssh' => [ + 'base_version' => '2.6.4' // SSH 协议在2.6.4中添加(2024年8月4日) + ], + 'juicity' => [ + 'base_version' => '2.6.4' // Juicity 协议在2.6.4中添加(2024年8月4日) + ] + ] + ]; + + const CUSTOM_TEMPLATE_FILE = 'resources/rules/custom.stash.yaml'; + const CUSTOM_CLASH_TEMPLATE_FILE = 'resources/rules/custom.clash.yaml'; + const DEFAULT_TEMPLATE_FILE = 'resources/rules/default.clash.yaml'; + + public function handle() + { + $servers = $this->servers; + $user = $this->user; + $appName = admin_setting('app_name', 'XBoard'); + + $template = subscribe_template('stash'); + + $config = Yaml::parse($template); + $proxy = []; + $proxies = []; + + foreach ($servers as $item) { + if ($item['type'] === Server::TYPE_SHADOWSOCKS) { + array_push($proxy, self::buildShadowsocks($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_VMESS) { + array_push($proxy, self::buildVmess($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_VLESS) { + array_push($proxy, $this->buildVless($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_HYSTERIA) { + array_push($proxy, self::buildHysteria($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_TROJAN) { + array_push($proxy, self::buildTrojan($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_TUIC) { + array_push($proxy, self::buildTuic($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_ANYTLS) { + array_push($proxy, self::buildAnyTLS($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_SOCKS) { + array_push($proxy, self::buildSocks5($item['password'], $item)); + array_push($proxies, $item['name']); + } + if ($item['type'] === Server::TYPE_HTTP) { + array_push($proxy, self::buildHttp($item['password'], $item)); + array_push($proxies, $item['name']); + } + } + + $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy); + foreach ($config['proxy-groups'] as $k => $v) { + if (!is_array($config['proxy-groups'][$k]['proxies'])) + $config['proxy-groups'][$k]['proxies'] = []; + $isFilter = false; + foreach ($config['proxy-groups'][$k]['proxies'] as $src) { + foreach ($proxies as $dst) { + if (!$this->isRegex($src)) + continue; + $isFilter = true; + $config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src])); + if ($this->isMatch($src, $dst)) { + array_push($config['proxy-groups'][$k]['proxies'], $dst); + } + } + if ($isFilter) + continue; + } + if ($isFilter) + continue; + $config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies); + } + $config['proxy-groups'] = array_filter($config['proxy-groups'], function ($group) { + return $group['proxies']; + }); + $config['proxy-groups'] = array_values($config['proxy-groups']); + // Force the current subscription domain to be a direct rule + $subsDomain = request()->header('Host'); + if ($subsDomain) { + array_unshift($config['rules'], "DOMAIN,{$subsDomain},DIRECT"); + } + + $yaml = Yaml::dump($config, 2, 4, Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE); + $yaml = str_replace('$app_name', admin_setting('app_name', 'XBoard'), $yaml); + return response($yaml) + ->header('content-type', 'text/yaml') + ->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}") + ->header('profile-update-interval', '24') + ->header('content-disposition', 'attachment;filename*=UTF-8\'\'' . rawurlencode($appName)); + } + + public static function buildShadowsocks($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'ss'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['cipher'] = data_get($protocol_settings, 'cipher'); + $array['password'] = $uuid; + $array['udp'] = true; + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $plugin = data_get($protocol_settings, 'plugin'); + $pluginOpts = data_get($protocol_settings, 'plugin_opts', ''); + $array['plugin'] = $plugin; + + // 解析插件选项 + $parsedOpts = collect(explode(';', $pluginOpts)) + ->filter() + ->mapWithKeys(function ($pair) { + if (!str_contains($pair, '=')) { + return []; + } + [$key, $value] = explode('=', $pair, 2); + return [trim($key) => trim($value)]; + }) + ->all(); + + // 根据插件类型进行字段映射 + switch ($plugin) { + case 'obfs': + $array['plugin-opts'] = [ + 'mode' => $parsedOpts['obfs'], + 'host' => $parsedOpts['obfs-host'], + ]; + + // 可选path参数 + if (isset($parsedOpts['path'])) { + $array['plugin-opts']['path'] = $parsedOpts['path']; + } + break; + + case 'v2ray-plugin': + $array['plugin-opts'] = [ + 'mode' => $parsedOpts['mode'] ?? 'websocket', + 'tls' => isset($parsedOpts['tls']) && $parsedOpts['tls'] == 'true', + 'host' => $parsedOpts['host'] ?? '', + 'path' => $parsedOpts['path'] ?? '/', + ]; + break; + + default: + // 对于其他插件,直接使用解析出的键值对 + $array['plugin-opts'] = $parsedOpts; + } + } + return $array; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'vmess'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['uuid'] = $uuid; + $array['alterId'] = 0; + $array['cipher'] = 'auto'; + $array['udp'] = true; + + $array['tls'] = (bool) data_get($protocol_settings, 'tls'); + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['servername'] = $serverName; + } + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $headerType = data_get($protocol_settings, 'network_settings.header.type', 'tcp'); + $array['network'] = ($headerType === 'http') ? 'http' : 'tcp'; + if ($headerType === 'http') { + $array['http-opts']['path'] = data_get($protocol_settings, 'network_settings.header.request.path', ['/']); + if ($host = data_get($protocol_settings, 'network_settings.header.request.headers.Host')) { + $array['http-opts']['headers']['Host'] = $host; + } + } + break; + case 'ws': + $array['network'] = 'ws'; + $array['ws-opts']['path'] = data_get($protocol_settings, 'network_settings.path'); + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) { + $array['ws-opts']['headers'] = ['Host' => $host]; + } + break; + case 'grpc': + $array['network'] = 'grpc'; + $array['grpc-opts'] = []; + $array['grpc-opts']['grpc-service-name'] = data_get($protocol_settings, 'network_settings.serviceName'); + break; + case 'h2': + $array['network'] = 'h2'; + $array['tls'] = true; + $array['h2-opts'] = []; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['h2-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['h2-opts']['host'] = is_array($host) ? $host : [$host]; + break; + default: + break; + } + return $array; + } + + public function buildVless($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = []; + $array['name'] = $server['name']; + $array['type'] = 'vless'; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['uuid'] = $uuid; + $array['udp'] = true; + + if ($fingerprint = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { + $array['client-fingerprint'] = $fingerprint; + } + + switch (data_get($protocol_settings, 'tls')) { + case 1: + $array['tls'] = true; + $array['skip-cert-verify'] = data_get($protocol_settings, 'tls_settings.allow_insecure'); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['servername'] = $serverName; + } + break; + case 2: + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'reality_settings.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'reality_settings.server_name')) { + $array['servername'] = $serverName; + $array['sni'] = $serverName; + } + $array['flow'] = data_get($protocol_settings, 'flow'); + $array['reality-opts'] = [ + 'public-key' => data_get($protocol_settings, 'reality_settings.public_key'), + 'short-id' => data_get($protocol_settings, 'reality_settings.short_id') + ]; + break; + } + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $headerType = data_get($protocol_settings, 'network_settings.header.type', 'tcp'); + $array['network'] = ($headerType === 'http') ? 'http' : 'tcp'; + if ($headerType === 'http') { + if ( + $httpOpts = array_filter([ + 'headers' => data_get($protocol_settings, 'network_settings.header.request.headers'), + 'path' => data_get($protocol_settings, 'network_settings.header.request.path', ['/']) + ]) + ) { + $array['http-opts'] = $httpOpts; + } + } + break; + case 'ws': + $array['network'] = 'ws'; + $array['ws-opts']['path'] = data_get($protocol_settings, 'network_settings.path'); + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) { + $array['ws-opts']['headers'] = ['Host' => $host]; + } + break; + case 'grpc': + $array['network'] = 'grpc'; + $array['grpc-opts']['grpc-service-name'] = data_get($protocol_settings, 'network_settings.serviceName'); + break; + case 'h2': + $array['network'] = 'h2'; + $array['h2-opts'] = []; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $array['h2-opts']['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $array['h2-opts']['host'] = is_array($host) ? $host : [$host]; + break; + } + + return $array; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = [ + 'name' => $server['name'], + 'type' => 'trojan', + 'server' => $server['host'], + 'port' => $server['port'], + 'password' => $password, + 'udp' => true, + ]; + + $tlsMode = (int) data_get($protocol_settings, 'tls', 1); + switch ($tlsMode) { + case 2: // Reality + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'reality_settings.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'reality_settings.server_name')) { + $array['sni'] = $serverName; + } + $array['reality-opts'] = [ + 'public-key' => data_get($protocol_settings, 'reality_settings.public_key'), + 'short-id' => data_get($protocol_settings, 'reality_settings.short_id'), + ]; + break; + default: // Standard TLS + if ($serverName = data_get($protocol_settings, 'server_name')) { + $array['sni'] = $serverName; + } + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'allow_insecure', false); + break; + } + + switch (data_get($protocol_settings, 'network')) { + case 'tcp': + $headerType = data_get($protocol_settings, 'network_settings.header.type', 'tcp'); + $array['network'] = ($headerType === 'http') ? 'http' : 'tcp'; + if ($headerType === 'http') { + $array['http-opts']['path'] = data_get($protocol_settings, 'network_settings.header.request.path', ['/']); + } + break; + case 'ws': + $array['network'] = 'ws'; + $array['ws-opts']['path'] = data_get($protocol_settings, 'network_settings.path'); + if ($host = data_get($protocol_settings, 'network_settings.headers.Host')) { + $array['ws-opts']['headers'] = ['Host' => $host]; + } + break; + case 'grpc': + $array['network'] = 'grpc'; + if ($serviceName = data_get($protocol_settings, 'network_settings.serviceName')) + $array['grpc-opts']['grpc-service-name'] = $serviceName; + break; + } + + return $array; + } + + public static function buildHysteria($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array['name'] = $server['name']; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['up-speed'] = data_get($protocol_settings, 'bandwidth.up'); + $array['down-speed'] = data_get($protocol_settings, 'bandwidth.down'); + $array['skip-cert-verify'] = data_get($protocol_settings, 'tls.allow_insecure'); + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $array['sni'] = $serverName; + } + if (isset($server['ports'])) { + $array['ports'] = $server['ports']; + } + switch (data_get($protocol_settings, 'version')) { + case 1: + $array['type'] = 'hysteria'; + $array['auth-str'] = $password; + $array['protocol'] = 'udp'; + if (data_get($protocol_settings, 'obfs.open')) { + $array['obfs'] = data_get($protocol_settings, 'obfs.password'); + } + break; + case 2: + $array['type'] = 'hysteria2'; + $array['auth'] = $password; + $array['fast-open'] = true; + if (data_get($protocol_settings, 'obfs.open')) { + $array['obfs'] = data_get($protocol_settings, 'obfs.type', 'salamander'); + $array['obfs-password'] = data_get($protocol_settings, 'obfs.password'); + } + break; + } + return $array; + } + + public static function buildTuic($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'tuic', + 'server' => $server['host'], + 'port' => $server['port'], + 'congestion-controller' => data_get($protocol_settings, 'congestion_control', 'cubic'), + 'udp-relay-mode' => data_get($protocol_settings, 'udp_relay_mode', 'native'), + 'alpn' => data_get($protocol_settings, 'alpn', ['h3']), + 'reduce-rtt' => true, + 'fast-open' => true, + 'heartbeat-interval' => 10000, + 'request-timeout' => 8000, + 'max-udp-relay-packet-size' => 1500, + 'version' => data_get($protocol_settings, 'version', 5), + ]; + + if (data_get($protocol_settings, 'version') === 4) { + $array['token'] = $password; + } else { + $array['uuid'] = $password; + $array['password'] = $password; + } + + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $array['sni'] = $serverName; + } + + return $array; + } + + public static function buildAnyTLS($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $array = [ + 'name' => $server['name'], + 'type' => 'anytls', + 'server' => $server['host'], + 'port' => $server['port'], + 'password' => $password, + 'sni' => data_get($protocol_settings, 'tls.server_name'), + 'skip-cert-verify' => (bool) data_get($protocol_settings, 'tls.allow_insecure', false), + 'udp' => true, + ]; + + return $array; + } + + public static function buildSocks5($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = [ + 'name' => $server['name'], + 'type' => 'socks5', + 'server' => $server['host'], + 'port' => $server['port'], + 'username' => $password, + 'password' => $password, + 'udp' => true, + ]; + + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['sni'] = $serverName; + } + } + + return $array; + } + + public static function buildHttp($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $array = [ + 'name' => $server['name'], + 'type' => 'http', + 'server' => $server['host'], + 'port' => $server['port'], + 'username' => $password, + 'password' => $password, + ]; + + if (data_get($protocol_settings, 'tls')) { + $array['tls'] = true; + $array['skip-cert-verify'] = (bool) data_get($protocol_settings, 'tls_settings.allow_insecure', false); + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $array['sni'] = $serverName; + } + } + + return $array; + } + + private function isRegex($exp) + { + if (empty($exp)) { + return false; + } + try { + return preg_match($exp, '') !== false; + } catch (\Exception $e) { + return false; + } + } + + private function isMatch($exp, $str) + { + try { + return preg_match($exp, $str); + } catch (\Exception $e) { + return false; + } + } +} diff --git a/Xboard/app/Protocols/Surfboard.php b/Xboard/app/Protocols/Surfboard.php new file mode 100644 index 0000000..23fcf09 --- /dev/null +++ b/Xboard/app/Protocols/Surfboard.php @@ -0,0 +1,229 @@ +servers; + $user = $this->user; + + $appName = admin_setting('app_name', 'XBoard'); + + $proxies = ''; + $proxyGroup = ''; + + foreach ($servers as $item) { + if ( + $item['type'] === Server::TYPE_SHADOWSOCKS + && in_array(data_get($item, 'protocol_settings.cipher'), [ + 'aes-128-gcm', + 'aes-192-gcm', + 'aes-256-gcm', + 'chacha20-ietf-poly1305', + '2022-blake3-aes-128-gcm', + '2022-blake3-aes-256-gcm', + '2022-blake3-chacha20-poly1305' + ]) + ) { + // [Proxy] + $proxies .= self::buildShadowsocks($item['password'], $item); + // [Proxy Group] + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_VMESS) { + // [Proxy] + $proxies .= self::buildVmess($item['password'], $item); + // [Proxy Group] + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_TROJAN) { + // [Proxy] + $proxies .= self::buildTrojan($item['password'], $item); + // [Proxy Group] + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_ANYTLS) { + $proxies .= self::buildAnyTLS($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + } + + $config = subscribe_template('surfboard'); + // Subscription link + $subsURL = Helper::getSubscribeUrl($user['token']); + $subsDomain = request()->header('Host'); + + $config = str_replace('$subs_link', $subsURL, $config); + $config = str_replace('$subs_domain', $subsDomain, $config); + $config = str_replace('$proxies', $proxies, $config); + $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config); + + $upload = round($user['u'] / (1024 * 1024 * 1024), 2); + $download = round($user['d'] / (1024 * 1024 * 1024), 2); + $useTraffic = $upload + $download; + $totalTraffic = round($user['transfer_enable'] / (1024 * 1024 * 1024), 2); + $unusedTraffic = $totalTraffic - $useTraffic; + $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']); + $subscribeInfo = "title={$appName}订阅信息, content=上传流量:{$upload}GB\\n下载流量:{$download}GB\\n剩余流量:{$unusedTraffic}GB\\n套餐流量:{$totalTraffic}GB\\n到期时间:{$expireDate}"; + $config = str_replace('$subscribe_info', $subscribeInfo, $config); + + return response($config, 200) + ->header('content-disposition', "attachment;filename*=UTF-8''" . rawurlencode($appName) . ".conf"); + } + + + public static function buildShadowsocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']}=ss", + "{$server['host']}", + "{$server['port']}", + "encrypt-method=" . data_get($protocol_settings, 'cipher'), + "password={$password}", + 'tfo=true', + 'udp-relay=true' + ]; + + + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $plugin = data_get($protocol_settings, 'plugin'); + $pluginOpts = data_get($protocol_settings, 'plugin_opts', ''); + // 解析插件选项 + $parsedOpts = collect(explode(';', $pluginOpts)) + ->filter() + ->mapWithKeys(function ($pair) { + if (!str_contains($pair, '=')) { + return []; + } + [$key, $value] = explode('=', $pair, 2); + return [trim($key) => trim($value)]; + }) + ->all(); + switch ($plugin) { + case 'obfs': + $config[] = "obfs={$parsedOpts['obfs']}"; + if (isset($parsedOpts['obfs-host'])) { + $config[] = "obfs-host={$parsedOpts['obfs-host']}"; + } + if (isset($parsedOpts['path'])) { + $config[] = "obfs-uri={$parsedOpts['path']}"; + } + break; + } + } + + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']}=vmess", + "{$server['host']}", + "{$server['port']}", + "username={$uuid}", + "vmess-aead=true", + 'tfo=true', + 'udp-relay=true' + ]; + + if (data_get($protocol_settings, 'tls')) { + array_push($config, 'tls=true'); + if (data_get($protocol_settings, 'tls_settings')) { + $tlsSettings = data_get($protocol_settings, 'tls_settings'); + if (data_get($tlsSettings, 'allow_insecure')) { + array_push($config, 'skip-cert-verify=' . ($tlsSettings['allow_insecure'] ? 'true' : 'false')); + } + if ($sni = data_get($tlsSettings, 'server_name')) { + array_push($config, "sni={$sni}"); + } + } + } + if (data_get($protocol_settings, 'network') === 'ws') { + array_push($config, 'ws=true'); + if (data_get($protocol_settings, 'network_settings')) { + $wsSettings = data_get($protocol_settings, 'network_settings'); + if (isset($wsSettings['path']) && !empty($wsSettings['path'])) + array_push($config, "ws-path={$wsSettings['path']}"); + if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) + array_push($config, "ws-headers=Host:{$wsSettings['headers']['Host']}"); + } + } + + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']}=trojan", + "{$server['host']}", + "{$server['port']}", + "password={$password}", + data_get($protocol_settings, 'server_name') ? "sni=" . data_get($protocol_settings, 'server_name') : "", + 'tfo=true', + 'udp-relay=true' + ]; + if (data_get($protocol_settings, 'allow_insecure')) { + array_push($config, !!data_get($protocol_settings, 'allow_insecure') ? 'skip-cert-verify=true' : 'skip-cert-verify=false'); + } + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildAnyTLS($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + + $config = [ + "{$server['name']}=anytls", + "{$server['host']}", + "{$server['port']}", + "password={$password}", + "tfo=true", + "udp-relay=true" + ]; + + // SNI + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $config[] = "sni={$serverName}"; + } + + // 跳过证书校验 + if (data_get($protocol_settings, 'tls.allow_insecure')) { + $config[] = "skip-cert-verify=true"; + } + + $config = array_filter($config); + + return implode(',', $config) . "\r\n"; + } +} diff --git a/Xboard/app/Protocols/Surge.php b/Xboard/app/Protocols/Surge.php new file mode 100644 index 0000000..ee1ea62 --- /dev/null +++ b/Xboard/app/Protocols/Surge.php @@ -0,0 +1,319 @@ + [2 => '2398'], + ]; + + public function handle() + { + $servers = $this->servers; + $user = $this->user; + + $appName = admin_setting('app_name', 'XBoard'); + + $proxies = ''; + $proxyGroup = ''; + + foreach ($servers as $item) { + if ( + $item['type'] === Server::TYPE_SHADOWSOCKS + && in_array(data_get($item, 'protocol_settings.cipher'), [ + 'aes-128-gcm', + 'aes-192-gcm', + 'aes-256-gcm', + 'chacha20-ietf-poly1305', + '2022-blake3-aes-128-gcm', + '2022-blake3-aes-256-gcm' + ]) + ) { + $proxies .= self::buildShadowsocks($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_VMESS) { + $proxies .= self::buildVmess($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_TROJAN) { + $proxies .= self::buildTrojan($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_HYSTERIA) { + $proxies .= self::buildHysteria($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_ANYTLS) { + $proxies .= self::buildAnyTLS($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_SOCKS) { + $proxies .= self::buildSocks($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + if ($item['type'] === Server::TYPE_HTTP) { + $proxies .= self::buildHttp($item['password'], $item); + $proxyGroup .= $item['name'] . ', '; + } + } + + + $config = subscribe_template('surge'); + + // Subscription link + $subsDomain = request()->header('Host'); + $subsURL = Helper::getSubscribeUrl($user['token'], $subsDomain ? 'https://' . $subsDomain : null); + + $config = str_replace('$subs_link', $subsURL, $config); + $config = str_replace('$subs_domain', $subsDomain, $config); + $config = str_replace('$proxies', $proxies, $config); + $config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config); + + $upload = round($user['u'] / (1024 * 1024 * 1024), 2); + $download = round($user['d'] / (1024 * 1024 * 1024), 2); + $useTraffic = $upload + $download; + $totalTraffic = round($user['transfer_enable'] / (1024 * 1024 * 1024), 2); + $unusedTraffic = $totalTraffic - $useTraffic; + $expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']); + $subscribeInfo = "title={$appName}订阅信息, content=上传流量:{$upload}GB\\n下载流量:{$download}GB\\n剩余流量:{$unusedTraffic}GB\\n套餐流量:{$totalTraffic}GB\\n到期时间:{$expireDate}"; + $config = str_replace('$subscribe_info', $subscribeInfo, $config); + + return response($config, 200) + ->header('content-type', 'application/octet-stream') + ->header('content-disposition', "attachment;filename*=UTF-8''" . rawurlencode($appName) . ".conf"); + } + + + public static function buildShadowsocks($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']} = ss", + "{$server['host']}", + "{$server['port']}", + "encrypt-method={$protocol_settings['cipher']}", + "password={$password}", + 'tfo=true', + 'udp-relay=true' + ]; + if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) { + $plugin = data_get($protocol_settings, 'plugin'); + $pluginOpts = data_get($protocol_settings, 'plugin_opts', ''); + // 解析插件选项 + $parsedOpts = collect(explode(';', $pluginOpts)) + ->filter() + ->mapWithKeys(function ($pair) { + if (!str_contains($pair, '=')) { + return []; + } + [$key, $value] = explode('=', $pair, 2); + return [trim($key) => trim($value)]; + }) + ->all(); + switch ($plugin) { + case 'obfs': + $config[] = "obfs={$parsedOpts['obfs']}"; + if (isset($parsedOpts['obfs-host'])) { + $config[] = "obfs-host={$parsedOpts['obfs-host']}"; + } + if (isset($parsedOpts['path'])) { + $config[] = "obfs-uri={$parsedOpts['path']}"; + } + break; + } + } + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildVmess($uuid, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']} = vmess", + "{$server['host']}", + "{$server['port']}", + "username={$uuid}", + "vmess-aead=true", + 'tfo=true', + 'udp-relay=true' + ]; + + if (data_get($protocol_settings, 'tls')) { + array_push($config, 'tls=true'); + if (data_get($protocol_settings, 'tls_settings')) { + $tlsSettings = data_get($protocol_settings, 'tls_settings'); + if (data_get($tlsSettings, 'allow_insecure')) + array_push($config, 'skip-cert-verify=' . ($tlsSettings['allow_insecure'] ? 'true' : 'false')); + if (data_get($tlsSettings, 'server_name')) + array_push($config, "sni={$tlsSettings['server_name']}"); + } + } + if (data_get($protocol_settings, 'network') === 'ws') { + array_push($config, 'ws=true'); + if (data_get($protocol_settings, 'network_settings')) { + $wsSettings = data_get($protocol_settings, 'network_settings'); + if (data_get($wsSettings, 'path')) + array_push($config, "ws-path={$wsSettings['path']}"); + if (data_get($wsSettings, 'headers.Host')) + array_push($config, "ws-headers=Host:{$wsSettings['headers']['Host']}"); + } + } + + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + public static function buildTrojan($password, $server) + { + $protocol_settings = $server['protocol_settings']; + $config = [ + "{$server['name']} = trojan", + "{$server['host']}", + "{$server['port']}", + "password={$password}", + data_get($protocol_settings, 'server_name') ? "sni=" . data_get($protocol_settings, 'server_name') : "", + 'tfo=true', + 'udp-relay=true' + ]; + if (!empty($protocol_settings['allow_insecure'])) { + array_push($config, !!data_get($protocol_settings, 'allow_insecure') ? 'skip-cert-verify=true' : 'skip-cert-verify=false'); + } + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + //参考文档: https://manual.nssurge.com/policy/proxy.html + public static function buildAnyTLS($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $config = [ + "{$server['name']} = anytls", + "{$server['host']}", + "{$server['port']}", + "password={$password}", + ]; + if ($serverName = data_get($protocol_settings, 'tls.server_name')) { + $config[] = "sni={$serverName}"; + } + if (data_get($protocol_settings, 'tls.allow_insecure')) { + $config[] = 'skip-cert-verify=true'; + } + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + //参考文档: https://manual.nssurge.com/policy/proxy.html + public static function buildHysteria($password, $server) + { + $protocol_settings = $server['protocol_settings']; + if ($protocol_settings['version'] != 2) + return ''; + $config = [ + "{$server['name']} = hysteria2", + "{$server['host']}", + "{$server['port']}", + "password={$password}", + $protocol_settings['tls']['server_name'] ? "sni={$protocol_settings['tls']['server_name']}" : "", + // 'tfo=true', + 'udp-relay=true' + ]; + if (data_get($protocol_settings, 'bandwidth.up')) { + $config[] = "upload-bandwidth={$protocol_settings['bandwidth']['up']}"; + } + if (data_get($protocol_settings, 'bandwidth.down')) { + $config[] = "download-bandwidth={$protocol_settings['bandwidth']['down']}"; + } + if (data_get($protocol_settings, 'tls.allow_insecure')) { + $config[] = !!data_get($protocol_settings, 'tls.allow_insecure') ? 'skip-cert-verify=true' : 'skip-cert-verify=false'; + } + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + //参考文档: https://manual.nssurge.com/policy/proxy.html + public static function buildSocks($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $type = data_get($protocol_settings, 'tls') ? 'socks5-tls' : 'socks5'; + $config = [ + "{$server['name']} = {$type}", + "{$server['host']}", + "{$server['port']}", + "{$password}", + "{$password}", + ]; + + if (data_get($protocol_settings, 'tls')) { + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $config[] = "sni={$serverName}"; + } + if (data_get($protocol_settings, 'tls_settings.allow_insecure')) { + $config[] = 'skip-cert-verify=true'; + } + } + $config[] = 'udp-relay=true'; + + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } + + //参考文档: https://manual.nssurge.com/policy/proxy.html + public static function buildHttp($password, $server) + { + $protocol_settings = data_get($server, 'protocol_settings', []); + $type = data_get($protocol_settings, 'tls') ? 'https' : 'http'; + $config = [ + "{$server['name']} = {$type}", + "{$server['host']}", + "{$server['port']}", + "{$password}", + "{$password}", + ]; + + if (data_get($protocol_settings, 'tls')) { + if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { + $config[] = "sni={$serverName}"; + } + if (data_get($protocol_settings, 'tls_settings.allow_insecure')) { + $config[] = 'skip-cert-verify=true'; + } + } + + $config = array_filter($config); + $uri = implode(',', $config); + $uri .= "\r\n"; + return $uri; + } +} diff --git a/Xboard/app/Providers/AuthServiceProvider.php b/Xboard/app/Providers/AuthServiceProvider.php new file mode 100644 index 0000000..b92a7dd --- /dev/null +++ b/Xboard/app/Providers/AuthServiceProvider.php @@ -0,0 +1,28 @@ + + */ + protected $policies = [ + // 'App\Model' => 'App\Policies\ModelPolicy', + ]; + + /** + * 注册任何认证/授权服务 + * @return void + */ + public function boot() + { + $this->registerPolicies(); + + // + } +} diff --git a/Xboard/app/Providers/BroadcastServiceProvider.php b/Xboard/app/Providers/BroadcastServiceProvider.php new file mode 100644 index 0000000..395c518 --- /dev/null +++ b/Xboard/app/Providers/BroadcastServiceProvider.php @@ -0,0 +1,21 @@ +> + */ + protected $listen = [ + ]; + + /** + * 注册任何事件 + * @return void + */ + public function boot() + { + parent::boot(); + + User::observe(UserObserver::class); + Plan::observe(PlanObserver::class); + Server::observe(ServerObserver::class); + ServerRoute::observe(ServerRouteObserver::class); + + + } +} diff --git a/Xboard/app/Providers/HorizonServiceProvider.php b/Xboard/app/Providers/HorizonServiceProvider.php new file mode 100644 index 0000000..119fd69 --- /dev/null +++ b/Xboard/app/Providers/HorizonServiceProvider.php @@ -0,0 +1,43 @@ +email, [ + // + ]); + }); + } +} diff --git a/Xboard/app/Providers/OctaneServiceProvider.php b/Xboard/app/Providers/OctaneServiceProvider.php new file mode 100644 index 0000000..4cdd91c --- /dev/null +++ b/Xboard/app/Providers/OctaneServiceProvider.php @@ -0,0 +1,43 @@ +app->runningInConsole()) { + return; + } + if ($this->app->bound('octane')) { + $this->app['events']->listen(WorkerStarting::class, function () { + app(UpdateService::class)->updateVersionCache(); + HookManager::reset(); + }); + } + // 每半钟执行一次调度检查 + Octane::tick('scheduler', function () { + $lock = Cache::lock('scheduler-lock', 30); + + if ($lock->get()) { + try { + Artisan::call('schedule:run'); + } finally { + $lock->release(); + } + } + })->seconds(30); + } +} \ No newline at end of file diff --git a/Xboard/app/Providers/PluginServiceProvider.php b/Xboard/app/Providers/PluginServiceProvider.php new file mode 100644 index 0000000..f0352ec --- /dev/null +++ b/Xboard/app/Providers/PluginServiceProvider.php @@ -0,0 +1,29 @@ +app->scoped(PluginManager::class, function ($app) { + return new PluginManager(); + }); + } + + public function boot(): void + { + if (!file_exists(base_path('plugins'))) { + mkdir(base_path('plugins'), 0755, true); + } + } +} \ No newline at end of file diff --git a/Xboard/app/Providers/ProtocolServiceProvider.php b/Xboard/app/Providers/ProtocolServiceProvider.php new file mode 100644 index 0000000..dfa80ee --- /dev/null +++ b/Xboard/app/Providers/ProtocolServiceProvider.php @@ -0,0 +1,50 @@ +app->scoped('protocols.manager', function ($app) { + return new ProtocolManager($app); + }); + + $this->app->scoped('protocols.flags', function ($app) { + return $app->make('protocols.manager')->getAllFlags(); + }); + } + + /** + * 启动服务 + * + * @return void + */ + public function boot() + { + // 在启动时预加载协议类并缓存 + $this->app->make('protocols.manager')->registerAllProtocols(); + + } + + /** + * 提供的服务 + * + * @return array + */ + public function provides() + { + return [ + 'protocols.manager', + 'protocols.flags', + ]; + } +} \ No newline at end of file diff --git a/Xboard/app/Providers/RouteServiceProvider.php b/Xboard/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..d26a599 --- /dev/null +++ b/Xboard/app/Providers/RouteServiceProvider.php @@ -0,0 +1,87 @@ +mapApiRoutes(); + $this->mapWebRoutes(); + + // + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapWebRoutes() + { + Route::middleware('web') + ->namespace($this->namespace) + ->group(base_path('routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiRoutes() + { + Route::group([ + 'prefix' => '/api/v1', + 'middleware' => 'api', + 'namespace' => $this->namespace + ], function ($router) { + foreach (glob(app_path('Http//Routes//V1') . '/*.php') as $file) { + $this->app->make('App\\Http\\Routes\\V1\\' . basename($file, '.php'))->map($router); + } + }); + + + Route::group([ + 'prefix' => '/api/v2', + 'middleware' => 'api', + 'namespace' => $this->namespace + ], function ($router) { + foreach (glob(app_path('Http//Routes//V2') . '/*.php') as $file) { + $this->app->make('App\\Http\\Routes\\V2\\' . basename($file, '.php'))->map($router); + } + }); + } +} diff --git a/Xboard/app/Providers/SettingServiceProvider.php b/Xboard/app/Providers/SettingServiceProvider.php new file mode 100644 index 0000000..eee267f --- /dev/null +++ b/Xboard/app/Providers/SettingServiceProvider.php @@ -0,0 +1,33 @@ +app->scoped(Setting::class, function (Application $app) { + return new Setting(); + }); + + } + + /** + * Bootstrap services. + * + * @return void + */ + public function boot() + { + // App URL is forced per-request via middleware (Octane-safe). + } +} diff --git a/Xboard/app/Scope/FilterScope.php b/Xboard/app/Scope/FilterScope.php new file mode 100644 index 0000000..fcb92a5 --- /dev/null +++ b/Xboard/app/Scope/FilterScope.php @@ -0,0 +1,50 @@ +validate([ + 'filter.*.key' => "required|in:{$allowKeys}", + 'filter.*.condition' => 'required|in:in,is,not,like,lt,gt', + 'filter.*.value' => 'required' + ]); + $filters = $request->input('filter'); + if ($filters) { + foreach ($filters as $k => $filter) { + if ($filter['condition'] === 'in') { + $builder->whereIn($filter['key'], $filter['value']); + continue; + } + if ($filter['condition'] === 'is') { + $builder->where($filter['key'], $filter['value']); + continue; + } + if ($filter['condition'] === 'not') { + $builder->where($filter['key'], '!=', $filter['value']); + continue; + } + if ($filter['condition'] === 'gt') { + $builder->where($filter['key'], '>', $filter['value']); + continue; + } + if ($filter['condition'] === 'lt') { + $builder->where($filter['key'], '<', $filter['value']); + continue; + } + if ($filter['condition'] === 'like') { + $builder->where($filter['key'], 'like', "%{$filter['value']}%"); + continue; + } + } + } + return $builder; + } +} \ No newline at end of file diff --git a/Xboard/app/Services/Auth/LoginService.php b/Xboard/app/Services/Auth/LoginService.php new file mode 100644 index 0000000..363e82b --- /dev/null +++ b/Xboard/app/Services/Auth/LoginService.php @@ -0,0 +1,154 @@ += (int) admin_setting('password_limit_count', 5)) { + return [ + false, + [ + 429, + __('There are too many password errors, please try again after :minute minutes.', [ + 'minute' => admin_setting('password_limit_expire', 60) + ]) + ] + ]; + } + } + + // 查找用户 + $user = User::byEmail($email)->first(); + if (!$user) { + return [false, [400, __('Incorrect email or password')]]; + } + + // 验证密码 + if ( + !Helper::multiPasswordVerify( + $user->password_algo, + $user->password_salt, + $password, + $user->password + ) + ) { + // 增加密码错误计数 + if ((int) admin_setting('password_limit_enable', true)) { + $passwordErrorCount = (int) Cache::get(CacheKey::get('PASSWORD_ERROR_LIMIT', $email), 0); + Cache::put( + CacheKey::get('PASSWORD_ERROR_LIMIT', $email), + (int) $passwordErrorCount + 1, + 60 * (int) admin_setting('password_limit_expire', 60) + ); + } + return [false, [400, __('Incorrect email or password')]]; + } + + // 检查账户状态 + if ($user->banned) { + return [false, [400, __('Your account has been suspended')]]; + } + + // 更新最后登录时间 + $user->last_login_at = time(); + $user->save(); + + HookManager::call('user.login.after', $user); + return [true, $user]; + } + + /** + * 处理密码重置 + * + * @param string $email 用户邮箱 + * @param string $emailCode 邮箱验证码 + * @param string $password 新密码 + * @return array [成功状态, 结果或错误信息] + */ + public function resetPassword(string $email, string $emailCode, string $password): array + { + // 检查重置请求限制 + $forgetRequestLimitKey = CacheKey::get('FORGET_REQUEST_LIMIT', $email); + $forgetRequestLimit = (int) Cache::get($forgetRequestLimitKey); + if ($forgetRequestLimit >= 3) { + return [false, [429, __('Reset failed, Please try again later')]]; + } + + // 验证邮箱验证码 + if ((string) Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $email)) !== (string) $emailCode) { + Cache::put($forgetRequestLimitKey, $forgetRequestLimit ? $forgetRequestLimit + 1 : 1, 300); + return [false, [400, __('Incorrect email verification code')]]; + } + + // 查找用户 + $user = User::byEmail($email)->first(); + if (!$user) { + return [false, [400, __('This email is not registered in the system')]]; + } + + // 更新密码 + $user->password = password_hash($password, PASSWORD_DEFAULT); + $user->password_algo = NULL; + $user->password_salt = NULL; + + if (!$user->save()) { + return [false, [500, __('Reset failed')]]; + } + + HookManager::call('user.password.reset.after', $user); + + // 清除邮箱验证码 + Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $email)); + + return [true, true]; + } + + + /** + * 生成临时登录令牌和快速登录URL + * + * @param User $user 用户对象 + * @param string $redirect 重定向路径 + * @return string|null 快速登录URL + */ + public function generateQuickLoginUrl(User $user, ?string $redirect = null): ?string + { + if (!$user || !$user->exists) { + return null; + } + + $code = Helper::guid(); + $key = CacheKey::get('TEMP_TOKEN', $code); + + Cache::put($key, $user->id, 60); + + $redirect = $redirect ?: 'dashboard'; + $loginRedirect = '/#/login?verify=' . $code . '&redirect=' . rawurlencode($redirect); + + if (admin_setting('app_url')) { + $url = admin_setting('app_url') . $loginRedirect; + } else { + $url = url($loginRedirect); + } + + return $url; + } +} \ No newline at end of file diff --git a/Xboard/app/Services/Auth/MailLinkService.php b/Xboard/app/Services/Auth/MailLinkService.php new file mode 100644 index 0000000..259ced7 --- /dev/null +++ b/Xboard/app/Services/Auth/MailLinkService.php @@ -0,0 +1,100 @@ +first(); + if (!$user) { + return [true, true]; // 成功但用户不存在,保护用户隐私 + } + + $code = Helper::guid(); + $key = CacheKey::get('TEMP_TOKEN', $code); + Cache::put($key, $user->id, 300); + Cache::put(CacheKey::get('LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP', $email), time(), 60); + + $redirectUrl = '/#/login?verify=' . $code . '&redirect=' . ($redirect ? $redirect : 'dashboard'); + if (admin_setting('app_url')) { + $link = admin_setting('app_url') . $redirectUrl; + } else { + $link = url($redirectUrl); + } + + $this->sendMailLinkEmail($user, $link); + + return [true, $link]; + } + + /** + * 发送邮件链接登录邮件 + * + * @param User $user 用户对象 + * @param string $link 登录链接 + * @return void + */ + private function sendMailLinkEmail(User $user, string $link): void + { + SendEmailJob::dispatch([ + 'email' => $user->email, + 'subject' => __('Login to :name', [ + 'name' => admin_setting('app_name', 'XBoard') + ]), + 'template_name' => 'login', + 'template_value' => [ + 'name' => admin_setting('app_name', 'XBoard'), + 'link' => $link, + 'url' => admin_setting('app_url') + ] + ]); + } + + /** + * 处理Token登录 + * + * @param string $token 登录令牌 + * @return int|null 用户ID或null + */ + public function handleTokenLogin(string $token): ?int + { + $key = CacheKey::get('TEMP_TOKEN', $token); + $userId = Cache::get($key); + + if (!$userId) { + return null; + } + + $user = User::find($userId); + + if (!$user || $user->banned) { + return null; + } + + Cache::forget($key); + + return $userId; + } +} \ No newline at end of file diff --git a/Xboard/app/Services/Auth/RegisterService.php b/Xboard/app/Services/Auth/RegisterService.php new file mode 100644 index 0000000..78d207e --- /dev/null +++ b/Xboard/app/Services/Auth/RegisterService.php @@ -0,0 +1,193 @@ +ip())) ?? 0; + if ((int) $registerCountByIP >= (int) admin_setting('register_limit_count', 3)) { + return [ + false, + [ + 429, + __('Register frequently, please try again after :minute minute', [ + 'minute' => admin_setting('register_limit_expire', 60) + ]) + ] + ]; + } + } + + // 检查验证码 + $captchaService = app(CaptchaService::class); + [$captchaValid, $captchaError] = $captchaService->verify($request); + if (!$captchaValid) { + return [false, $captchaError]; + } + + // 检查邮箱白名单 + if ((int) admin_setting('email_whitelist_enable', 0)) { + if ( + !Helper::emailSuffixVerify( + $request->input('email'), + admin_setting('email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT) + ) + ) { + return [false, [400, __('Email suffix is not in the Whitelist')]]; + } + } + + // 检查Gmail限制 + if ((int) admin_setting('email_gmail_limit_enable', 0)) { + $prefix = explode('@', $request->input('email'))[0]; + if (strpos($prefix, '.') !== false || strpos($prefix, '+') !== false) { + return [false, [400, __('Gmail alias is not supported')]]; + } + } + + // 检查是否关闭注册 + if ((int) admin_setting('stop_register', 0)) { + return [false, [400, __('Registration has closed')]]; + } + + // 检查邀请码要求 + if ((int) admin_setting('invite_force', 0)) { + if (empty($request->input('invite_code'))) { + return [false, [422, __('You must use the invitation code to register')]]; + } + } + + // 检查邮箱验证 + if ((int) admin_setting('email_verify', 0)) { + if (empty($request->input('email_code'))) { + return [false, [422, __('Email verification code cannot be empty')]]; + } + if ((string) Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== (string) $request->input('email_code')) { + return [false, [400, __('Incorrect email verification code')]]; + } + } + + // 检查邮箱是否存在 + $exist = User::byEmail($request->input('email'))->first(); + if ($exist) { + return [false, [400201, __('Email already exists')]]; + } + + return [true, null]; + } + + /** + * 处理邀请码 + * + * @param string $inviteCode 邀请码 + * @return int|null 邀请人ID + */ + public function handleInviteCode(string $inviteCode): int|null + { + $inviteCodeModel = InviteCode::where('code', $inviteCode) + ->where('status', InviteCode::STATUS_UNUSED) + ->first(); + + if (!$inviteCodeModel) { + if ((int) admin_setting('invite_force', 0)) { + throw new ApiException(__('Invalid invitation code')); + } + return null; + } + + if (!(int) admin_setting('invite_never_expire', 0)) { + $inviteCodeModel->status = InviteCode::STATUS_USED; + $inviteCodeModel->save(); + } + + return $inviteCodeModel->user_id; + } + + + + /** + * 注册用户 + * + * @param Request $request 请求对象 + * @return array [成功状态, 用户对象或错误信息] + */ + public function register(Request $request): array + { + // 验证注册数据 + [$valid, $error] = $this->validateRegister($request); + if (!$valid) { + return [false, $error]; + } + + HookManager::call('user.register.before', $request); + + $email = $request->input('email'); + $password = $request->input('password'); + $inviteCode = $request->input('invite_code'); + + // 处理邀请码获取邀请人ID + $inviteUserId = null; + if ($inviteCode) { + $inviteUserId = $this->handleInviteCode($inviteCode); + } + + // 创建用户 + $userService = app(UserService::class); + $user = $userService->createUser([ + 'email' => $email, + 'password' => $password, + 'invite_user_id' => $inviteUserId, + ]); + + // 保存用户 + if (!$user->save()) { + return [false, [500, __('Register failed')]]; + } + + HookManager::call('user.register.after', $user); + + // 清除邮箱验证码 + if ((int) admin_setting('email_verify', 0)) { + Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $email)); + } + + // 更新最近登录时间 + $user->last_login_at = time(); + $user->save(); + + // 更新IP注册计数 + if ((int) admin_setting('register_limit_by_ip_enable', 0)) { + $registerCountByIP = Cache::get(CacheKey::get('REGISTER_IP_RATE_LIMIT', $request->ip())) ?? 0; + Cache::put( + CacheKey::get('REGISTER_IP_RATE_LIMIT', $request->ip()), + (int) $registerCountByIP + 1, + (int) admin_setting('register_limit_expire', 60) * 60 + ); + } + + return [true, $user]; + } +} \ No newline at end of file diff --git a/Xboard/app/Services/AuthService.php b/Xboard/app/Services/AuthService.php new file mode 100644 index 0000000..299f610 --- /dev/null +++ b/Xboard/app/Services/AuthService.php @@ -0,0 +1,87 @@ +user = $user; + } + + public function generateAuthData(): array + { + // Create a new Sanctum token with device info + $token = $this->user->createToken( + Str::random(20), // token name (device identifier) + ['*'], // abilities + now()->addYear() // expiration + ); + + // Format token: remove ID prefix and add Bearer + $tokenParts = explode('|', $token->plainTextToken); + $formattedToken = 'Bearer ' . ($tokenParts[1] ?? $tokenParts[0]); + + return [ + 'token' => $this->user->token, + 'auth_data' => $formattedToken, + 'is_admin' => $this->user->is_admin, + ]; + } + + public function getSessions(): array + { + return $this->user->tokens()->get()->toArray(); + } + + public function removeSession(string $sessionId): bool + { + $this->user->tokens()->where('id', $sessionId)->delete(); + return true; + } + + public function removeAllSessions(): bool + { + $this->user->tokens()->delete(); + return true; + } + + public static function findUserByBearerToken(string $bearerToken): ?User + { + $token = str_replace('Bearer ', '', $bearerToken); + + $accessToken = PersonalAccessToken::findToken($token); + + $tokenable = $accessToken?->tokenable; + + return $tokenable instanceof User ? $tokenable : null; + } + + /** + * 解密认证数据 + * + * @param string $authorization + * @return array|null 用户数据或null + */ + public static function decryptAuthData(string $authorization): ?array + { + $user = self::findUserByBearerToken($authorization); + + if (!$user) { + return null; + } + + return [ + 'id' => $user->id, + 'email' => $user->email, + 'is_admin' => (bool)$user->is_admin, + 'is_staff' => (bool)$user->is_staff + ]; + } +} diff --git a/Xboard/app/Services/CaptchaService.php b/Xboard/app/Services/CaptchaService.php new file mode 100644 index 0000000..b6a4f05 --- /dev/null +++ b/Xboard/app/Services/CaptchaService.php @@ -0,0 +1,112 @@ + $this->verifyTurnstile($request), + 'recaptcha-v3' => $this->verifyRecaptchaV3($request), + 'recaptcha' => $this->verifyRecaptcha($request), + default => [false, [400, __('Invalid captcha type')]] + }; + } + + /** + * 验证 Cloudflare Turnstile + * + * @param Request $request + * @return array + */ + private function verifyTurnstile(Request $request): array + { + $turnstileToken = $request->input('turnstile_token'); + if (!$turnstileToken) { + return [false, [400, __('Invalid code is incorrect')]]; + } + + $response = Http::post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [ + 'secret' => admin_setting('turnstile_secret_key'), + 'response' => $turnstileToken, + 'remoteip' => $request->ip() + ]); + + $result = $response->json(); + if (!$result['success']) { + return [false, [400, __('Invalid code is incorrect')]]; + } + + return [true, null]; + } + + /** + * 验证 Google reCAPTCHA v3 + * + * @param Request $request + * @return array + */ + private function verifyRecaptchaV3(Request $request): array + { + $recaptchaV3Token = $request->input('recaptcha_v3_token'); + if (!$recaptchaV3Token) { + return [false, [400, __('Invalid code is incorrect')]]; + } + + $recaptcha = new ReCaptcha(admin_setting('recaptcha_v3_secret_key')); + $recaptchaResp = $recaptcha->verify($recaptchaV3Token, $request->ip()); + + if (!$recaptchaResp->isSuccess()) { + return [false, [400, __('Invalid code is incorrect')]]; + } + + // 检查分数阈值(如果有的话) + $score = $recaptchaResp->getScore(); + $threshold = admin_setting('recaptcha_v3_score_threshold', 0.5); + if ($score < $threshold) { + return [false, [400, __('Invalid code is incorrect')]]; + } + + return [true, null]; + } + + /** + * 验证 Google reCAPTCHA v2 + * + * @param Request $request + * @return array + */ + private function verifyRecaptcha(Request $request): array + { + $recaptchaData = $request->input('recaptcha_data'); + if (!$recaptchaData) { + return [false, [400, __('Invalid code is incorrect')]]; + } + + $recaptcha = new ReCaptcha(admin_setting('recaptcha_key')); + $recaptchaResp = $recaptcha->verify($recaptchaData); + + if (!$recaptchaResp->isSuccess()) { + return [false, [400, __('Invalid code is incorrect')]]; + } + + return [true, null]; + } +} \ No newline at end of file diff --git a/Xboard/app/Services/CouponService.php b/Xboard/app/Services/CouponService.php new file mode 100644 index 0000000..e4bdae7 --- /dev/null +++ b/Xboard/app/Services/CouponService.php @@ -0,0 +1,122 @@ +coupon = Coupon::where('code', $code) + ->lockForUpdate() + ->first(); + } + + public function use(Order $order): bool + { + $this->setPlanId($order->plan_id); + $this->setUserId($order->user_id); + $this->setPeriod($order->period); + $this->check(); + switch ($this->coupon->type) { + case 1: + $order->discount_amount = $this->coupon->value; + break; + case 2: + $order->discount_amount = $order->total_amount * ($this->coupon->value / 100); + break; + } + if ($order->discount_amount > $order->total_amount) { + $order->discount_amount = $order->total_amount; + } + if ($this->coupon->limit_use !== NULL) { + if ($this->coupon->limit_use <= 0) + return false; + $this->coupon->limit_use = $this->coupon->limit_use - 1; + if (!$this->coupon->save()) { + return false; + } + } + return true; + } + + public function getId() + { + return $this->coupon->id; + } + + public function getCoupon() + { + return $this->coupon; + } + + public function setPlanId($planId) + { + $this->planId = $planId; + } + + public function setUserId($userId) + { + $this->userId = $userId; + } + + public function setPeriod($period) + { + if ($period) { + $this->period = PlanService::getPeriodKey($period); + } + } + + public function checkLimitUseWithUser(): bool + { + $usedCount = Order::where('coupon_id', $this->coupon->id) + ->where('user_id', $this->userId) + ->whereNotIn('status', [0, 2]) + ->count(); + if ($usedCount >= $this->coupon->limit_use_with_user) + return false; + return true; + } + + public function check() + { + if (!$this->coupon || !$this->coupon->show) { + throw new ApiException(__('Invalid coupon')); + } + if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) { + throw new ApiException(__('This coupon is no longer available')); + } + if (time() < $this->coupon->started_at) { + throw new ApiException(__('This coupon has not yet started')); + } + if (time() > $this->coupon->ended_at) { + throw new ApiException(__('This coupon has expired')); + } + if ($this->coupon->limit_plan_ids && $this->planId) { + if (!in_array($this->planId, $this->coupon->limit_plan_ids)) { + throw new ApiException(__('The coupon code cannot be used for this subscription')); + } + } + if ($this->coupon->limit_period && $this->period) { + if (!in_array($this->period, $this->coupon->limit_period)) { + throw new ApiException(__('The coupon code cannot be used for this period')); + } + } + if ($this->coupon->limit_use_with_user !== NULL && $this->userId) { + if (!$this->checkLimitUseWithUser()) { + throw new ApiException(__('The coupon can only be used :limit_use_with_user per person', [ + 'limit_use_with_user' => $this->coupon->limit_use_with_user + ])); + } + } + } +} diff --git a/Xboard/app/Services/DeviceStateService.php b/Xboard/app/Services/DeviceStateService.php new file mode 100644 index 0000000..09e106c --- /dev/null +++ b/Xboard/app/Services/DeviceStateService.php @@ -0,0 +1,187 @@ +removeNodeDevices($nodeId, $userId); + + if (!empty($ips)) { + $fields = []; + foreach ($ips as $ip) { + $fields["{$nodeId}:{$ip}"] = $timestamp; + } + Redis::hMset($key, $fields); + Redis::expire($key, self::TTL); + } + + $this->notifyUpdate($userId); + } + + /** + * 获取某节点的所有设备数据 + * 返回: {userId: [ip1, ip2, ...], ...} + */ + public function getNodeDevices(int $nodeId): array + { + $keys = Redis::keys(self::PREFIX . '*'); + $prefix = "{$nodeId}:"; + $result = []; + foreach ($keys as $key) { + $actualKey = $this->removeRedisPrefix($key); + $uid = (int) substr($actualKey, strlen(self::PREFIX)); + $data = Redis::hgetall($actualKey); + foreach ($data as $field => $timestamp) { + if (str_starts_with($field, $prefix)) { + $ip = substr($field, strlen($prefix)); + $result[$uid][] = $ip; + } + } + } + + return $result; + } + + /** + * 删除某节点某用户的设备 + */ + public function removeNodeDevices(int $nodeId, int $userId): void + { + $key = self::PREFIX . $userId; + $prefix = "{$nodeId}:"; + + foreach (Redis::hkeys($key) as $field) { + if (str_starts_with($field, $prefix)) { + Redis::hdel($key, $field); + } + } + } + + /** + * 清除节点所有设备数据(用于节点断开连接) + */ + public function clearAllNodeDevices(int $nodeId): array + { + $oldDevices = $this->getNodeDevices($nodeId); + $prefix = "{$nodeId}:"; + + foreach ($oldDevices as $userId => $ips) { + $key = self::PREFIX . $userId; + foreach (Redis::hkeys($key) as $field) { + if (str_starts_with($field, $prefix)) { + Redis::hdel($key, $field); + } + } + } + + return array_keys($oldDevices); + } + + /** + * get user device count (deduplicated by IP, filter expired data) + */ + public function getDeviceCount(int $userId): int + { + $data = Redis::hgetall(self::PREFIX . $userId); + $now = time(); + $ips = []; + + foreach ($data as $field => $timestamp) { + if ($now - $timestamp <= self::TTL) { + $ips[] = substr($field, strpos($field, ':') + 1); + } + } + + return count(array_unique($ips)); + } + + /** + * get user device count (for alivelist interface) + */ + public function getAliveList(Collection $users): array + { + if ($users->isEmpty()) { + return []; + } + + $result = []; + foreach ($users as $user) { + $count = $this->getDeviceCount($user->id); + if ($count > 0) { + $result[$user->id] = $count; + } + } + + return $result; + } + + /** + * get devices of multiple users (for sync.devices, filter expired data) + */ + public function getUsersDevices(array $userIds): array + { + $result = []; + $now = time(); + foreach ($userIds as $userId) { + $data = Redis::hgetall(self::PREFIX . $userId); + if (!empty($data)) { + $ips = []; + foreach ($data as $field => $timestamp) { + if ($now - $timestamp <= self::TTL) { + $ips[] = substr($field, strpos($field, ':') + 1); + } + } + if (!empty($ips)) { + $result[$userId] = array_unique($ips); + } + } + } + + return $result; + } + + /** + * notify update (throttle control) + */ + public function notifyUpdate(int $userId): void + { + $dbThrottleKey = "device:db_throttle:{$userId}"; + + // if (Redis::setnx($dbThrottleKey, 1)) { + // Redis::expire($dbThrottleKey, self::DB_THROTTLE); + + User::query() + ->whereKey($userId) + ->update([ + 'online_count' => $this->getDeviceCount($userId), + 'last_online_at' => now(), + ]); + // } + } +} diff --git a/Xboard/app/Services/GiftCardService.php b/Xboard/app/Services/GiftCardService.php new file mode 100644 index 0000000..8f14cbe --- /dev/null +++ b/Xboard/app/Services/GiftCardService.php @@ -0,0 +1,334 @@ +code = GiftCardCode::where('code', $code)->first() + ?? throw new ApiException('兑换码不存在'); + + $this->template = $this->code->template; + } + + /** + * 设置使用用户 + */ + public function setUser(User $user): self + { + $this->user = $user; + return $this; + } + + /** + * 验证兑换码 + */ + public function validate(): self + { + $this->validateIsActive(); + + $eligibility = $this->checkUserEligibility(); + if (!$eligibility['can_redeem']) { + throw new ApiException($eligibility['reason']); + } + + return $this; + } + + /** + * 验证礼品卡本身是否可用 (不检查用户条件) + * @throws ApiException + */ + public function validateIsActive(): self + { + if (!$this->template->isAvailable()) { + throw new ApiException('该礼品卡类型已停用'); + } + + if (!$this->code->isAvailable()) { + throw new ApiException('兑换码不可用:' . $this->code->status_name); + } + return $this; + } + + /** + * 检查用户是否满足兑换条件 (不抛出异常) + */ + public function checkUserEligibility(): array + { + if (!$this->user) { + return [ + 'can_redeem' => false, + 'reason' => '用户信息未提供' + ]; + } + + if (!$this->template->checkUserConditions($this->user)) { + return [ + 'can_redeem' => false, + 'reason' => '您不满足此礼品卡的使用条件' + ]; + } + + if (!$this->template->checkUsageLimit($this->user)) { + return [ + 'can_redeem' => false, + 'reason' => '您已达到此礼品卡的使用限制' + ]; + } + + return ['can_redeem' => true, 'reason' => null]; + } + + /** + * 使用礼品卡 + */ + public function redeem(array $options = []): array + { + if (!$this->user) { + throw new ApiException('未设置使用用户'); + } + + return DB::transaction(function () use ($options) { + $actualRewards = $this->template->calculateActualRewards($this->user); + + if ($this->template->type === GiftCardTemplate::TYPE_MYSTERY) { + $this->code->setActualRewards($actualRewards); + } + + $this->giveRewards($actualRewards); + + $inviteRewards = null; + if ($this->user->invite_user_id && isset($actualRewards['invite_reward_rate'])) { + $inviteRewards = $this->giveInviteRewards($actualRewards); + } + + $this->code->markAsUsed($this->user); + + GiftCardUsage::createRecord( + $this->code, + $this->user, + $actualRewards, + array_merge($options, [ + 'invite_rewards' => $inviteRewards, + 'multiplier' => $this->calculateMultiplier(), + ]) + ); + + return [ + 'rewards' => $actualRewards, + 'invite_rewards' => $inviteRewards, + 'code' => $this->code->code, + 'template_name' => $this->template->name, + ]; + }); + } + + /** + * 发放奖励 + */ + protected function giveRewards(array $rewards): void + { + $userService = app(UserService::class); + + if (isset($rewards['balance']) && $rewards['balance'] > 0) { + if (!$userService->addBalance($this->user->id, $rewards['balance'])) { + throw new ApiException('余额发放失败'); + } + } + + if (isset($rewards['transfer_enable']) && $rewards['transfer_enable'] > 0) { + $this->user->transfer_enable = ($this->user->transfer_enable ?? 0) + $rewards['transfer_enable']; + } + + if (isset($rewards['device_limit']) && $rewards['device_limit'] > 0) { + $this->user->device_limit = ($this->user->device_limit ?? 0) + $rewards['device_limit']; + } + + if (isset($rewards['reset_package']) && $rewards['reset_package']) { + if ($this->user->plan_id) { + app(TrafficResetService::class)->performReset($this->user, TrafficResetLog::SOURCE_GIFT_CARD); + } + } + + if (isset($rewards['plan_id'])) { + $plan = Plan::find($rewards['plan_id']); + if ($plan) { + $userService->assignPlan( + $this->user, + $plan, + $rewards['plan_validity_days'] ?? 0 + ); + } + } else { + // 只有在不是套餐卡的情况下,才处理独立的有效期奖励 + if (isset($rewards['expire_days']) && $rewards['expire_days'] > 0) { + $userService->extendSubscription($this->user, $rewards['expire_days']); + } + } + + // 保存用户更改 + if (!$this->user->save()) { + throw new ApiException('用户信息更新失败'); + } + } + + /** + * 发放邀请人奖励 + */ + protected function giveInviteRewards(array $rewards): ?array + { + if (!$this->user->invite_user_id) { + return null; + } + + $inviteUser = User::find($this->user->invite_user_id); + if (!$inviteUser) { + return null; + } + + $rate = $rewards['invite_reward_rate'] ?? 0.2; + $inviteRewards = []; + + $userService = app(UserService::class); + + // 邀请人余额奖励 + if (isset($rewards['balance']) && $rewards['balance'] > 0) { + $inviteBalance = intval($rewards['balance'] * $rate); + if ($inviteBalance > 0) { + $userService->addBalance($inviteUser->id, $inviteBalance); + $inviteRewards['balance'] = $inviteBalance; + } + } + + // 邀请人流量奖励 + if (isset($rewards['transfer_enable']) && $rewards['transfer_enable'] > 0) { + $inviteTransfer = intval($rewards['transfer_enable'] * $rate); + if ($inviteTransfer > 0) { + $inviteUser->transfer_enable = ($inviteUser->transfer_enable ?? 0) + $inviteTransfer; + $inviteUser->save(); + $inviteRewards['transfer_enable'] = $inviteTransfer; + } + } + + return $inviteRewards; + } + + /** + * 计算倍率 + */ + protected function calculateMultiplier(): float + { + return $this->getFestivalBonus(); + } + + /** + * 获取节日加成倍率 + */ + private function getFestivalBonus(): float + { + $festivalConfig = $this->template->special_config ?? []; + $now = time(); + + if ( + isset($festivalConfig['start_time'], $festivalConfig['end_time']) && + $now >= $festivalConfig['start_time'] && + $now <= $festivalConfig['end_time'] + ) { + return $festivalConfig['festival_bonus'] ?? 1.0; + } + + return 1.0; + } + + /** + * 获取兑换码信息(不包含敏感信息) + */ + public function getCodeInfo(): array + { + $info = [ + 'code' => $this->code->code, + 'template' => [ + 'name' => $this->template->name, + 'description' => $this->template->description, + 'type' => $this->template->type, + 'type_name' => $this->template->type_name, + 'icon' => $this->template->icon, + 'background_image' => $this->template->background_image, + 'theme_color' => $this->template->theme_color, + ], + 'status' => $this->code->status, + 'status_name' => $this->code->status_name, + 'expires_at' => $this->code->expires_at, + 'usage_count' => $this->code->usage_count, + 'max_usage' => $this->code->max_usage, + ]; + if ($this->template->type === GiftCardTemplate::TYPE_PLAN) { + $plan = Plan::find($this->code->template->rewards['plan_id']); + if ($plan) { + $info['plan_info'] = PlanResource::make($plan)->toArray(request()); + } + } + return $info; + } + + /** + * 预览奖励(不实际发放) + */ + public function previewRewards(): array + { + if (!$this->user) { + throw new ApiException('未设置使用用户'); + } + + return $this->template->calculateActualRewards($this->user); + } + + /** + * 获取兑换码 + */ + public function getCode(): GiftCardCode + { + return $this->code; + } + + /** + * 获取模板 + */ + public function getTemplate(): GiftCardTemplate + { + return $this->template; + } + + /** + * 记录日志 + */ + protected function logUsage(string $action, array $data = []): void + { + Log::info('礼品卡使用记录', [ + 'action' => $action, + 'code' => $this->code->code, + 'template_id' => $this->template->id, + 'user_id' => $this->user?->id, + 'data' => $data, + 'ip' => request()->ip(), + 'user_agent' => request()->userAgent(), + ]); + } +} diff --git a/Xboard/app/Services/MailService.php b/Xboard/app/Services/MailService.php new file mode 100644 index 0000000..7630111 --- /dev/null +++ b/Xboard/app/Services/MailService.php @@ -0,0 +1,295 @@ +where('remind_expire', true) + ->orWhere('remind_traffic', true); + }) + ->where('banned', false) + ->whereNotNull('email') + ->count(); + } + + /** + * 分块处理用户提醒邮件 + */ + public function processUsersInChunks(int $chunkSize, ?callable $progressCallback = null): array + { + $statistics = [ + 'processed_users' => 0, + 'expire_emails' => 0, + 'traffic_emails' => 0, + 'errors' => 0, + 'skipped' => 0, + ]; + + User::select('id', 'email', 'expired_at', 'transfer_enable', 'u', 'd', 'remind_expire', 'remind_traffic') + ->where(function ($query) { + $query->where('remind_expire', true) + ->orWhere('remind_traffic', true); + }) + ->where('banned', false) + ->whereNotNull('email') + ->chunk($chunkSize, function ($users) use (&$statistics, $progressCallback) { + $this->processUserChunk($users, $statistics); + + if ($progressCallback) { + $progressCallback(); + } + + // 定期清理内存 + if ($statistics['processed_users'] % 2500 === 0) { + gc_collect_cycles(); + } + }); + + return $statistics; + } + + /** + * 处理用户块 + */ + private function processUserChunk($users, array &$statistics): void + { + foreach ($users as $user) { + try { + $statistics['processed_users']++; + $emailsSent = 0; + + // 检查并发送过期提醒 + if ($user->remind_expire && $this->shouldSendExpireRemind($user)) { + $this->remindExpire($user); + $statistics['expire_emails']++; + $emailsSent++; + } + + // 检查并发送流量提醒 + if ($user->remind_traffic && $this->shouldSendTrafficRemind($user)) { + $this->remindTraffic($user); + $statistics['traffic_emails']++; + $emailsSent++; + } + + if ($emailsSent === 0) { + $statistics['skipped']++; + } + + } catch (\Exception $e) { + $statistics['errors']++; + + Log::error('发送提醒邮件失败', [ + 'user_id' => $user->id, + 'email' => $user->email, + 'error' => $e->getMessage() + ]); + } + } + } + + /** + * 检查是否应该发送过期提醒 + */ + private function shouldSendExpireRemind(User $user): bool + { + if ($user->expired_at === NULL) { + return false; + } + $expiredAt = $user->expired_at; + $now = time(); + if (($expiredAt - 86400) < $now && $expiredAt > $now) { + return true; + } + return false; + } + + /** + * 检查是否应该发送流量提醒 + */ + private function shouldSendTrafficRemind(User $user): bool + { + if ($user->transfer_enable <= 0) { + return false; + } + + $usedBytes = $user->u + $user->d; + $usageRatio = $usedBytes / $user->transfer_enable; + + // 流量使用超过80%时发送提醒 + return $usageRatio >= 0.8; + } + + public function remindTraffic(User $user) + { + if (!$user->remind_traffic) + return; + if (!$this->remindTrafficIsWarnValue($user->u, $user->d, $user->transfer_enable)) + return; + $flag = CacheKey::get('LAST_SEND_EMAIL_REMIND_TRAFFIC', $user->id); + if (Cache::get($flag)) + return; + if (!Cache::put($flag, 1, 24 * 3600)) + return; + + SendEmailJob::dispatch([ + 'email' => $user->email, + 'subject' => __('The traffic usage in :app_name has reached 80%', [ + 'app_name' => admin_setting('app_name', 'XBoard') + ]), + 'template_name' => 'remindTraffic', + 'template_value' => [ + 'name' => admin_setting('app_name', 'XBoard'), + 'url' => admin_setting('app_url') + ] + ]); + } + + public function remindExpire(User $user) + { + if (!$this->shouldSendExpireRemind($user)) { + return; + } + + SendEmailJob::dispatch([ + 'email' => $user->email, + 'subject' => __('The service in :app_name is about to expire', [ + 'app_name' => admin_setting('app_name', 'XBoard') + ]), + 'template_name' => 'remindExpire', + 'template_value' => [ + 'name' => admin_setting('app_name', 'XBoard'), + 'url' => admin_setting('app_url') + ] + ]); + } + + private function remindTrafficIsWarnValue($u, $d, $transfer_enable) + { + $ud = $u + $d; + if (!$ud) + return false; + if (!$transfer_enable) + return false; + $percentage = ($ud / $transfer_enable) * 100; + if ($percentage < 80) + return false; + if ($percentage >= 100) + return false; + return true; + } + + /** + * 发送邮件 + * + * @param array $params 包含邮件参数的数组,必须包含以下字段: + * - email: 收件人邮箱地址 + * - subject: 邮件主题 + * - template_name: 邮件模板名称,例如 "welcome" 或 "password_reset" + * - template_value: 邮件模板变量,一个关联数组,包含模板中需要替换的变量和对应的值 + * @return array 包含邮件发送结果的数组,包含以下字段: + * - email: 收件人邮箱地址 + * - subject: 邮件主题 + * - template_name: 邮件模板名称 + * - error: 如果邮件发送失败,包含错误信息;否则为 null + * @throws \InvalidArgumentException 如果 $params 参数缺少必要的字段,抛出此异常 + */ + public static function sendEmail(array $params) + { + if (admin_setting('email_host')) { + Config::set('mail.host', admin_setting('email_host', config('mail.host'))); + Config::set('mail.port', admin_setting('email_port', config('mail.port'))); + Config::set('mail.encryption', admin_setting('email_encryption', config('mail.encryption'))); + Config::set('mail.username', admin_setting('email_username', config('mail.username'))); + Config::set('mail.password', admin_setting('email_password', config('mail.password'))); + Config::set('mail.from.address', admin_setting('email_from_address', config('mail.from.address'))); + Config::set('mail.from.name', admin_setting('app_name', 'XBoard')); + } + $email = $params['email']; + $subject = $params['subject']; + + $templateValue = $params['template_value'] ?? []; + $vars = is_array($templateValue) ? ($templateValue['vars'] ?? []) : []; + $contentMode = is_array($templateValue) ? ($templateValue['content_mode'] ?? null) : null; + + if (is_array($vars) && !empty($vars)) { + $subject = self::renderPlaceholders((string) $subject, $vars); + + if (is_array($templateValue) && isset($templateValue['content']) && is_string($templateValue['content'])) { + $templateValue['content'] = self::renderPlaceholders($templateValue['content'], $vars); + } + } + + // Mass mail default: treat admin content as plain text and escape. + if ($contentMode === 'text' && is_array($templateValue) && isset($templateValue['content']) && is_string($templateValue['content'])) { + $templateValue['content'] = e($templateValue['content']); + } + + $params['template_value'] = $templateValue; + $params['template_name'] = 'mail.' . admin_setting('email_template', 'default') . '.' . $params['template_name']; + try { + Mail::send( + $params['template_name'], + $params['template_value'], + function ($message) use ($email, $subject) { + $message->to($email)->subject($subject); + } + ); + $error = null; + } catch (\Exception $e) { + Log::error($e); + $error = $e->getMessage(); + } + $log = [ + 'email' => $params['email'], + 'subject' => $params['subject'], + 'template_name' => $params['template_name'], + 'error' => $error, + 'config' => config('mail') + ]; + MailLog::create($log); + return $log; + } +} diff --git a/Xboard/app/Services/NodeRegistry.php b/Xboard/app/Services/NodeRegistry.php new file mode 100644 index 0000000..c0f7d13 --- /dev/null +++ b/Xboard/app/Services/NodeRegistry.php @@ -0,0 +1,77 @@ + nodeId → connection */ + private static array $connections = []; + + public static function add(int $nodeId, TcpConnection $conn): void + { + // Close existing connection for this node (if reconnecting) + if (isset(self::$connections[$nodeId])) { + self::$connections[$nodeId]->close(); + } + self::$connections[$nodeId] = $conn; + } + + public static function remove(int $nodeId): void + { + unset(self::$connections[$nodeId]); + } + + public static function get(int $nodeId): ?TcpConnection + { + return self::$connections[$nodeId] ?? null; + } + + /** + * Send a JSON message to a specific node. + */ + public static function send(int $nodeId, string $event, array $data): bool + { + $conn = self::get($nodeId); + if (!$conn) { + return false; + } + + $payload = json_encode([ + 'event' => $event, + 'data' => $data, + 'timestamp' => time(), + ]); + + $conn->send($payload); + return true; + } + + /** + * Get the connection for a node by ID, checking if it's still alive. + */ + public static function isOnline(int $nodeId): bool + { + $conn = self::get($nodeId); + return $conn !== null && $conn->getStatus() === TcpConnection::STATUS_ESTABLISHED; + } + + /** + * Get all connected node IDs. + * @return int[] + */ + public static function getConnectedNodeIds(): array + { + return array_keys(self::$connections); + } + + public static function count(): int + { + return count(self::$connections); + } +} diff --git a/Xboard/app/Services/NodeSyncService.php b/Xboard/app/Services/NodeSyncService.php new file mode 100644 index 0000000..ebab769 --- /dev/null +++ b/Xboard/app/Services/NodeSyncService.php @@ -0,0 +1,143 @@ + ServerService::buildNodeConfig($node)]); + } + + /** + * Push all users to all nodes in the group + */ + public static function notifyUsersUpdatedByGroup(int $groupId): void + { + $servers = Server::whereJsonContains('group_ids', (string) $groupId) + ->get(); + + foreach ($servers as $server) { + if (!self::isNodeOnline($server->id)) + continue; + + $users = ServerService::getAvailableUsers($server)->toArray(); + self::push($server->id, 'sync.users', ['users' => $users]); + } + } + + /** + * Push user changes (add/remove) to affected nodes + */ + public static function notifyUserChanged(User $user): void + { + if (!$user->group_id) + return; + + $servers = Server::whereJsonContains('group_ids', (string) $user->group_id)->get(); + foreach ($servers as $server) { + if (!self::isNodeOnline($server->id)) + continue; + + if ($user->isAvailable()) { + self::push($server->id, 'sync.user.delta', [ + 'action' => 'add', + 'users' => [ + [ + 'id' => $user->id, + 'uuid' => $user->uuid, + 'speed_limit' => $user->speed_limit, + 'device_limit' => $user->device_limit, + ] + ], + ]); + } else { + self::push($server->id, 'sync.user.delta', [ + 'action' => 'remove', + 'users' => [['id' => $user->id]], + ]); + } + } + } + + /** + * Push user removal from a specific group's nodes + */ + public static function notifyUserRemovedFromGroup(int $userId, int $groupId): void + { + $servers = Server::whereJsonContains('group_ids', (string) $groupId) + ->get(); + + foreach ($servers as $server) { + if (!self::isNodeOnline($server->id)) + continue; + + self::push($server->id, 'sync.user.delta', [ + 'action' => 'remove', + 'users' => [['id' => $userId]], + ]); + } + } + + /** + * Full sync: push config + users to a node + */ + public static function notifyFullSync(int $nodeId): void + { + if (!self::isNodeOnline($nodeId)) + return; + + $node = Server::find($nodeId); + if (!$node) + return; + + self::push($nodeId, 'sync.config', ['config' => ServerService::buildNodeConfig($node)]); + + $users = ServerService::getAvailableUsers($node)->toArray(); + self::push($nodeId, 'sync.users', ['users' => $users]); + } + + /** + * Publish a push command to Redis — picked up by the Workerman WS server + */ + public static function push(int $nodeId, string $event, array $data): void + { + try { + Redis::publish('node:push', json_encode([ + 'node_id' => $nodeId, + 'event' => $event, + 'data' => $data, + ])); + } catch (\Throwable $e) { + Log::warning("[NodePush] Redis publish failed: {$e->getMessage()}", [ + 'node_id' => $nodeId, + 'event' => $event, + ]); + } + } +} diff --git a/Xboard/app/Services/OrderService.php b/Xboard/app/Services/OrderService.php new file mode 100644 index 0000000..900be59 --- /dev/null +++ b/Xboard/app/Services/OrderService.php @@ -0,0 +1,429 @@ + 1, + Plan::PERIOD_QUARTERLY => 3, + Plan::PERIOD_HALF_YEARLY => 6, + Plan::PERIOD_YEARLY => 12, + Plan::PERIOD_TWO_YEARLY => 24, + Plan::PERIOD_THREE_YEARLY => 36 + ]; + public $order; + public $user; + + public function __construct(Order $order) + { + $this->order = $order; + } + + /** + * Create an order from a request. + * + * @param User $user + * @param Plan $plan + * @param string $period + * @param string|null $couponCode + * @return Order + * @throws ApiException + */ + public static function createFromRequest( + User $user, + Plan $plan, + string $period, + ?string $couponCode = null, + ): Order { + $userService = app(UserService::class); + $planService = new PlanService($plan); + + $planService->validatePurchase($user, $period); + HookManager::call('order.create.before', [$user, $plan, $period, $couponCode]); + + return DB::transaction(function () use ($user, $plan, $period, $couponCode, $userService) { + $newPeriod = PlanService::getPeriodKey($period); + + $order = new Order([ + 'user_id' => $user->id, + 'plan_id' => $plan->id, + 'period' => $newPeriod, + 'trade_no' => Helper::generateOrderNo(), + 'total_amount' => (int) (optional($plan->prices)[$newPeriod] * 100), + ]); + + $orderService = new self($order); + + if ($couponCode) { + $orderService->applyCoupon($couponCode); + } + + $orderService->setVipDiscount($user); + $orderService->setOrderType($user); + $orderService->setInvite(user: $user); + + if ($user->balance && $order->total_amount > 0) { + $orderService->handleUserBalance($user, $userService); + } + + if (!$order->save()) { + throw new ApiException(__('Failed to create order')); + } + + HookManager::call('order.create.after', $order); + // 兼容旧钩子 + HookManager::call('order.after_create', $order); + + return $order; + }); + } + + public function open(): void + { + $order = $this->order; + $plan = Plan::find($order->plan_id); + + HookManager::call('order.open.before', $order); + + + DB::transaction(function () use ($order, $plan) { + $this->user = User::lockForUpdate()->find($order->user_id); + + if ($order->refund_amount) { + $this->user->balance += $order->refund_amount; + } + + if ($order->surplus_order_ids) { + Order::whereIn('id', $order->surplus_order_ids) + ->update(['status' => Order::STATUS_DISCOUNTED]); + } + + match ((string) $order->period) { + Plan::PERIOD_ONETIME => $this->buyByOneTime($plan), + Plan::PERIOD_RESET_TRAFFIC => app(TrafficResetService::class)->performReset($this->user, TrafficResetLog::SOURCE_ORDER), + default => $this->buyByPeriod($order, $plan), + }; + + $this->setSpeedLimit($plan->speed_limit); + $this->setDeviceLimit($plan->device_limit); + + if (!$this->user->save()) { + throw new \RuntimeException('用户信息保存失败'); + } + + $order->status = Order::STATUS_COMPLETED; + if (!$order->save()) { + throw new \RuntimeException('订单信息保存失败'); + } + }); + + $eventId = match ((int) $order->type) { + Order::STATUS_PROCESSING => admin_setting('new_order_event_id', 0), + Order::TYPE_RENEWAL => admin_setting('renew_order_event_id', 0), + Order::TYPE_UPGRADE => admin_setting('change_order_event_id', 0), + default => 0, + }; + + if ($eventId) { + $this->openEvent($eventId); + } + + HookManager::call('order.open.after', $order); + } + + + public function setOrderType(User $user) + { + $order = $this->order; + if ($order->period === Plan::PERIOD_RESET_TRAFFIC) { + $order->type = Order::TYPE_RESET_TRAFFIC; + } else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id && ($user->expired_at > time() || $user->expired_at === NULL)) { + if (!(int) admin_setting('plan_change_enable', 1)) + throw new ApiException('目前不允许更改订阅,请联系客服或提交工单操作'); + $order->type = Order::TYPE_UPGRADE; + if ((int) admin_setting('surplus_enable', 1)) + $this->getSurplusValue($user, $order); + if ($order->surplus_amount >= $order->total_amount) { + $order->refund_amount = (int) ($order->surplus_amount - $order->total_amount); + $order->total_amount = 0; + } else { + $order->total_amount = (int) ($order->total_amount - $order->surplus_amount); + } + } else if (($user->expired_at === null || $user->expired_at > time()) && $order->plan_id == $user->plan_id) { // 用户订阅未过期或按流量订阅 且购买订阅与当前订阅相同 === 续费 + $order->type = Order::TYPE_RENEWAL; + } else { // 新购 + $order->type = Order::TYPE_NEW_PURCHASE; + } + } + + public function setVipDiscount(User $user) + { + $order = $this->order; + if ($user->discount) { + $order->discount_amount = $order->discount_amount + ($order->total_amount * ($user->discount / 100)); + } + $order->total_amount = $order->total_amount - $order->discount_amount; + } + + public function setInvite(User $user): void + { + $order = $this->order; + if ($user->invite_user_id && ($order->total_amount <= 0)) + return; + $order->invite_user_id = $user->invite_user_id; + $inviter = User::find($user->invite_user_id); + if (!$inviter) + return; + $commissionType = (int) $inviter->commission_type; + if ($commissionType === User::COMMISSION_TYPE_SYSTEM) { + $commissionType = (bool) admin_setting('commission_first_time_enable', true) ? User::COMMISSION_TYPE_ONETIME : User::COMMISSION_TYPE_PERIOD; + } + $isCommission = false; + switch ($commissionType) { + case User::COMMISSION_TYPE_PERIOD: + $isCommission = true; + break; + case User::COMMISSION_TYPE_ONETIME: + $isCommission = !$this->haveValidOrder($user); + break; + } + + if (!$isCommission) + return; + if ($inviter->commission_rate) { + $order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100); + } else { + $order->commission_balance = $order->total_amount * (admin_setting('invite_commission', 10) / 100); + } + } + + private function haveValidOrder(User $user): Order|null + { + return Order::where('user_id', $user->id) + ->whereNotIn('status', [Order::STATUS_PENDING, Order::STATUS_CANCELLED]) + ->first(); + } + + private function getSurplusValue(User $user, Order $order) + { + if ($user->expired_at === NULL) { + $lastOneTimeOrder = Order::where('user_id', $user->id) + ->where('period', Plan::PERIOD_ONETIME) + ->where('status', Order::STATUS_COMPLETED) + ->orderBy('id', 'DESC') + ->first(); + if (!$lastOneTimeOrder) + return; + $nowUserTraffic = Helper::transferToGB($user->transfer_enable); + if (!$nowUserTraffic) + return; + $paidTotalAmount = ($lastOneTimeOrder->total_amount + $lastOneTimeOrder->balance_amount); + if (!$paidTotalAmount) + return; + $trafficUnitPrice = $paidTotalAmount / $nowUserTraffic; + $notUsedTraffic = $nowUserTraffic - Helper::transferToGB($user->u + $user->d); + $result = $trafficUnitPrice * $notUsedTraffic; + $order->surplus_amount = (int) ($result > 0 ? $result : 0); + $order->surplus_order_ids = Order::where('user_id', $user->id) + ->where('period', '!=', Plan::PERIOD_RESET_TRAFFIC) + ->where('status', Order::STATUS_COMPLETED) + ->pluck('id') + ->all(); + } else { + $orders = Order::query() + ->where('user_id', $user->id) + ->whereNotIn('period', [Plan::PERIOD_RESET_TRAFFIC, Plan::PERIOD_ONETIME]) + ->where('status', Order::STATUS_COMPLETED) + ->get(); + + if ($orders->isEmpty()) { + $order->surplus_amount = 0; + $order->surplus_order_ids = []; + return; + } + + $orderAmountSum = $orders->sum(fn($item) => $item->total_amount + $item->balance_amount + $item->surplus_amount - $item->refund_amount); + $orderMonthSum = $orders->sum(fn($item) => self::STR_TO_TIME[PlanService::getPeriodKey($item->period)] ?? 0); + $firstOrderAt = $orders->min('created_at'); + $expiredAt = Carbon::createFromTimestamp($firstOrderAt)->addMonths($orderMonthSum); + + $now = now(); + $totalSeconds = $expiredAt->timestamp - $firstOrderAt; + $remainSeconds = max(0, $expiredAt->timestamp - $now->timestamp); + $cycleRatio = $totalSeconds > 0 ? $remainSeconds / $totalSeconds : 0; + + $plan = Plan::find($user->plan_id); + $totalTraffic = $plan?->transfer_enable * $orderMonthSum; + $usedTraffic = Helper::transferToGB($user->u + $user->d); + $remainTraffic = max(0, $totalTraffic - $usedTraffic); + $trafficRatio = $totalTraffic > 0 ? $remainTraffic / $totalTraffic : 0; + + $ratio = $cycleRatio; + if (admin_setting('change_order_event_id', 0) == 1) { + $ratio = min($cycleRatio, $trafficRatio); + } + + + $order->surplus_amount = (int) max(0, $orderAmountSum * $ratio); + $order->surplus_order_ids = $orders->pluck('id')->all(); + } + } + + public function paid(string $callbackNo) + { + $order = $this->order; + if ($order->status !== Order::STATUS_PENDING) + return true; + $order->status = Order::STATUS_PROCESSING; + $order->paid_at = time(); + $order->callback_no = $callbackNo; + if (!$order->save()) + return false; + try { + OrderHandleJob::dispatchSync($order->trade_no); + } catch (\Exception $e) { + Log::error($e); + return false; + } + return true; + } + + public function cancel(): bool + { + $order = $this->order; + HookManager::call('order.cancel.before', $order); + try { + DB::beginTransaction(); + $order->status = Order::STATUS_CANCELLED; + if (!$order->save()) { + throw new \Exception('Failed to save order status.'); + } + if ($order->balance_amount) { + $userService = new UserService(); + if (!$userService->addBalance($order->user_id, $order->balance_amount)) { + throw new \Exception('Failed to add balance.'); + } + } + DB::commit(); + HookManager::call('order.cancel.after', $order); + return true; + } catch (\Exception $e) { + DB::rollBack(); + Log::error($e); + return false; + } + } + + private function setSpeedLimit($speedLimit) + { + $this->user->speed_limit = $speedLimit; + } + + private function setDeviceLimit($deviceLimit) + { + $this->user->device_limit = $deviceLimit; + } + + private function buyByPeriod(Order $order, Plan $plan) + { + // change plan process + if ((int) $order->type === Order::TYPE_UPGRADE) { + $this->user->expired_at = time(); + } + $this->user->transfer_enable = $plan->transfer_enable * 1073741824; + // 从一次性转换到循环或者新购的时候,重置流量 + if ($this->user->expired_at === NULL || $order->type === Order::TYPE_NEW_PURCHASE) + app(TrafficResetService::class)->performReset($this->user, TrafficResetLog::SOURCE_ORDER); + $this->user->plan_id = $plan->id; + $this->user->group_id = $plan->group_id; + $this->user->expired_at = $this->getTime($order->period, $this->user->expired_at); + } + + private function buyByOneTime(Plan $plan) + { + app(TrafficResetService::class)->performReset($this->user, TrafficResetLog::SOURCE_ORDER); + $this->user->transfer_enable = $plan->transfer_enable * 1073741824; + $this->user->plan_id = $plan->id; + $this->user->group_id = $plan->group_id; + $this->user->expired_at = NULL; + } + + /** + * 计算套餐到期时间 + * @param string $periodKey + * @param int $timestamp + * @return int + * @throws ApiException + */ + private function getTime(string $periodKey, ?int $timestamp = null): int + { + $timestamp = $timestamp < time() ? time() : $timestamp; + $periodKey = PlanService::getPeriodKey($periodKey); + + if (isset(self::STR_TO_TIME[$periodKey])) { + $months = self::STR_TO_TIME[$periodKey]; + return Carbon::createFromTimestamp($timestamp)->addMonths($months)->timestamp; + } + + throw new ApiException('无效的套餐周期'); + } + + private function openEvent($eventId) + { + switch ((int) $eventId) { + case 0: + break; + case 1: + app(TrafficResetService::class)->performReset($this->user, TrafficResetLog::SOURCE_ORDER); + break; + } + } + + protected function applyCoupon(string $couponCode): void + { + $couponService = new CouponService($couponCode); + if (!$couponService->use($this->order)) { + throw new ApiException(__('Coupon failed')); + } + $this->order->coupon_id = $couponService->getId(); + } + + /** + * Summary of handleUserBalance + * @param User $user + * @param UserService $userService + * @return void + */ + protected function handleUserBalance(User $user, UserService $userService): void + { + $remainingBalance = $user->balance - $this->order->total_amount; + + if ($remainingBalance >= 0) { + if (!$userService->addBalance($this->order->user_id, -$this->order->total_amount)) { + throw new ApiException(__('Insufficient balance')); + } + $this->order->balance_amount = $this->order->total_amount; + $this->order->total_amount = 0; + } else { + if (!$userService->addBalance($this->order->user_id, -$user->balance)) { + throw new ApiException(__('Insufficient balance')); + } + $this->order->balance_amount = $user->balance; + $this->order->total_amount = $this->order->total_amount - $user->balance; + } + } +} diff --git a/Xboard/app/Services/PaymentService.php b/Xboard/app/Services/PaymentService.php new file mode 100644 index 0000000..c496d38 --- /dev/null +++ b/Xboard/app/Services/PaymentService.php @@ -0,0 +1,135 @@ +method = $method; + $this->pluginManager = app(PluginManager::class); + + if ($method === 'temp') { + return; + } + + if ($id) { + $paymentModel = Payment::find($id); + if (!$paymentModel) { + throw new ApiException('payment not found'); + } + $payment = $paymentModel->toArray(); + } + if ($uuid) { + $paymentModel = Payment::where('uuid', $uuid)->first(); + if (!$paymentModel) { + throw new ApiException('payment not found'); + } + $payment = $paymentModel->toArray(); + } + + $this->config = []; + if (isset($payment)) { + $this->config = is_string($payment['config']) ? json_decode($payment['config'], true) : $payment['config']; + $this->config['enable'] = $payment['enable']; + $this->config['id'] = $payment['id']; + $this->config['uuid'] = $payment['uuid']; + $this->config['notify_domain'] = $payment['notify_domain'] ?? ''; + } + + $paymentMethods = $this->getAvailablePaymentMethods(); + if (isset($paymentMethods[$this->method])) { + $pluginCode = $paymentMethods[$this->method]['plugin_code']; + $paymentPlugins = $this->pluginManager->getEnabledPaymentPlugins(); + foreach ($paymentPlugins as $plugin) { + if ($plugin->getPluginCode() === $pluginCode) { + $plugin->setConfig($this->config); + $this->payment = $plugin; + return; + } + } + } + + $this->payment = new $this->class($this->config); + } + + public function notify($params) + { + if (!$this->config['enable']) + throw new ApiException('gate is not enable'); + return $this->payment->notify($params); + } + + public function pay($order) + { + // custom notify domain name + $notifyUrl = url("/api/v1/guest/payment/notify/{$this->method}/{$this->config['uuid']}"); + if ($this->config['notify_domain']) { + $parseUrl = parse_url($notifyUrl); + $notifyUrl = $this->config['notify_domain'] . $parseUrl['path']; + } + + return $this->payment->pay([ + 'notify_url' => $notifyUrl, + 'return_url' => source_base_url('/#/order/' . $order['trade_no']), + 'trade_no' => $order['trade_no'], + 'total_amount' => $order['total_amount'], + 'user_id' => $order['user_id'], + 'stripe_token' => $order['stripe_token'] + ]); + } + + public function form() + { + $form = $this->payment->form(); + $result = []; + foreach ($form as $key => $field) { + $result[$key] = [ + 'type' => $field['type'], + 'label' => $field['label'] ?? '', + 'placeholder' => $field['placeholder'] ?? '', + 'description' => $field['description'] ?? '', + 'value' => $this->config[$key] ?? $field['default'] ?? '', + 'options' => $field['select_options'] ?? $field['options'] ?? [] + ]; + } + return $result; + } + + /** + * 获取所有可用的支付方式 + */ + public function getAvailablePaymentMethods(): array + { + $methods = []; + + $methods = HookManager::filter('available_payment_methods', $methods); + + return $methods; + } + + /** + * 获取所有支付方式名称列表(用于管理后台) + */ + public static function getAllPaymentMethodNames(): array + { + $pluginManager = app(PluginManager::class); + $pluginManager->initializeEnabledPlugins(); + + $instance = new self('temp'); + $methods = $instance->getAvailablePaymentMethods(); + + return array_keys($methods); + } +} diff --git a/Xboard/app/Services/PlanService.php b/Xboard/app/Services/PlanService.php new file mode 100644 index 0000000..e67363d --- /dev/null +++ b/Xboard/app/Services/PlanService.php @@ -0,0 +1,194 @@ +plan = $plan; + } + + /** + * 获取所有可销售的订阅计划列表 + * 条件:show 和 sell 为 true,且容量充足 + * + * @return Collection + */ + public function getAvailablePlans(): Collection + { + return Plan::where('show', true) + ->where('sell', true) + ->orderBy('sort') + ->get() + ->filter(function ($plan) { + return $this->hasCapacity($plan); + }); + } + + /** + * 获取指定订阅计划的可用状态 + * 条件:renew 和 sell 为 true + * + * @param int $planId + * @return Plan|null + */ + public function getAvailablePlan(int $planId): ?Plan + { + return Plan::where('id', $planId) + ->where('sell', true) + ->where('renew', true) + ->first(); + } + + /** + * 检查指定计划是否可用于指定用户 + * + * @param Plan $plan + * @param User $user + * @return bool + */ + public function isPlanAvailableForUser(Plan $plan, User $user): bool + { + // 如果是续费 + if ($user->plan_id === $plan->id) { + return $plan->renew; + } + + // 如果是新购 + return $plan->show && $plan->sell && $this->hasCapacity($plan); + } + + public function validatePurchase(User $user, string $period): void + { + if (!$this->plan) { + throw new ApiException(__('Subscription plan does not exist')); + } + + // 转换周期格式为新版格式 + $periodKey = self::getPeriodKey($period); + $price = $this->plan->prices[$periodKey] ?? null; + + if ($price === null) { + throw new ApiException(__('This payment period cannot be purchased, please choose another period')); + } + + if ($periodKey === Plan::PERIOD_RESET_TRAFFIC) { + $this->validateResetTrafficPurchase($user); + return; + } + + if ($user->plan_id !== $this->plan->id && !$this->hasCapacity($this->plan)) { + throw new ApiException(__('Current product is sold out')); + } + + $this->validatePlanAvailability($user); + } + + /** + * 智能转换周期格式为新版格式 + * 如果是新版格式直接返回,如果是旧版格式则转换为新版格式 + * + * @param string $period + * @return string + */ + public static function getPeriodKey(string $period): string + { + // 如果是新版格式直接返回 + if (in_array($period, self::getNewPeriods())) { + return $period; + } + + // 如果是旧版格式则转换为新版格式 + return Plan::LEGACY_PERIOD_MAPPING[$period] ?? $period; + } + /** + * 只能转换周期格式为旧版本 + */ + public static function convertToLegacyPeriod(string $period): string + { + $flippedMapping = array_flip(Plan::LEGACY_PERIOD_MAPPING); + return $flippedMapping[$period] ?? $period; + } + + /** + * 获取所有支持的新版周期格式 + * + * @return array + */ + public static function getNewPeriods(): array + { + return array_values(Plan::LEGACY_PERIOD_MAPPING); + } + + /** + * 获取旧版周期格式 + * + * @param string $period + * @return string + */ + public static function getLegacyPeriod(string $period): string + { + $flipped = array_flip(Plan::LEGACY_PERIOD_MAPPING); + return $flipped[$period] ?? $period; + } + + protected function validateResetTrafficPurchase(User $user): void + { + if (!app(UserService::class)->isAvailable($user) || $this->plan->id !== $user->plan_id) { + throw new ApiException(__('Subscription has expired or no active subscription, unable to purchase Data Reset Package')); + } + } + + protected function validatePlanAvailability(User $user): void + { + if ((!$this->plan->show && !$this->plan->renew) || (!$this->plan->show && $user->plan_id !== $this->plan->id)) { + throw new ApiException(__('This subscription has been sold out, please choose another subscription')); + } + + if (!$this->plan->renew && $user->plan_id == $this->plan->id) { + throw new ApiException(__('This subscription cannot be renewed, please change to another subscription')); + } + + if (!$this->plan->show && $this->plan->renew && !app(UserService::class)->isAvailable($user)) { + throw new ApiException(__('This subscription has expired, please change to another subscription')); + } + } + + public function hasCapacity(Plan $plan): bool + { + if ($plan->capacity_limit === null) { + return true; + } + + $activeUserCount = User::where('plan_id', $plan->id) + ->where(function ($query) { + $query->where('expired_at', '>=', time()) + ->orWhereNull('expired_at'); + }) + ->count(); + + return ($plan->capacity_limit - $activeUserCount) > 0; + } + + public function getAvailablePeriods(Plan $plan): array + { + return array_filter( + $plan->getActivePeriods(), + fn($period) => isset($plan->prices[$period]) && $plan->prices[$period] > 0 + ); + } + + public function canResetTraffic(Plan $plan): bool + { + return $plan->reset_traffic_method !== Plan::RESET_TRAFFIC_NEVER + && $plan->getResetTrafficPrice() > 0; + } +} diff --git a/Xboard/app/Services/Plugin/AbstractPlugin.php b/Xboard/app/Services/Plugin/AbstractPlugin.php new file mode 100644 index 0000000..23da9a3 --- /dev/null +++ b/Xboard/app/Services/Plugin/AbstractPlugin.php @@ -0,0 +1,222 @@ +pluginCode = $pluginCode; + $this->namespace = 'Plugin\\' . Str::studly($pluginCode); + $reflection = new \ReflectionClass($this); + $this->basePath = dirname($reflection->getFileName()); + } + + /** + * 获取插件代码 + */ + public function getPluginCode(): string + { + return $this->pluginCode; + } + + /** + * 获取插件命名空间 + */ + public function getNamespace(): string + { + return $this->namespace; + } + + /** + * 获取插件基础路径 + */ + public function getBasePath(): string + { + return $this->basePath; + } + + /** + * 设置配置 + */ + public function setConfig(array $config): void + { + $this->config = $config; + } + + /** + * 获取配置 + */ + public function getConfig(?string $key = null, $default = null): mixed + { + $config = $this->config; + if ($key) { + $config = $config[$key] ?? $default; + } + return $config; + } + + /** + * 获取视图 + */ + protected function view(string $view, array $data = [], array $mergeData = []): \Illuminate\Contracts\View\View + { + return view(Str::studly($this->pluginCode) . '::' . $view, $data, $mergeData); + } + + /** + * 注册动作钩子监听器 + */ + protected function listen(string $hook, callable $callback, int $priority = 20): void + { + HookManager::register($hook, $callback, $priority); + } + + /** + * 注册过滤器钩子 + */ + protected function filter(string $hook, callable $callback, int $priority = 20): void + { + HookManager::registerFilter($hook, $callback, $priority); + } + + /** + * 移除事件监听器 + */ + protected function removeListener(string $hook): void + { + HookManager::remove($hook); + } + + /** + * 注册 Artisan 命令 + */ + protected function registerCommand(string $commandClass): void + { + if (class_exists($commandClass)) { + app('Illuminate\Contracts\Console\Kernel')->registerCommand(new $commandClass()); + } + } + + /** + * 注册插件命令目录 + */ + public function registerCommands(): void + { + $commandsPath = $this->basePath . '/Commands'; + if (File::exists($commandsPath)) { + $files = File::glob($commandsPath . '/*.php'); + foreach ($files as $file) { + $className = pathinfo($file, PATHINFO_FILENAME); + $commandClass = $this->namespace . '\\Commands\\' . $className; + + if (class_exists($commandClass)) { + $this->registerCommand($commandClass); + } + } + } + } + + /** + * 中断当前请求并返回新的响应 + * + * @param Response|string|array $response + * @return never + */ + protected function intercept(Response|string|array $response): never + { + HookManager::intercept($response); + } + + /** + * 插件启动时调用 + */ + public function boot(): void + { + // 插件启动时的初始化逻辑 + } + + /** + * 插件安装时调用 + */ + public function install(): void + { + // 插件安装时的初始化逻辑 + } + + /** + * 插件卸载时调用 + */ + public function cleanup(): void + { + // 插件卸载时的清理逻辑 + } + + /** + * 插件更新时调用 + */ + public function update(string $oldVersion, string $newVersion): void + { + // 插件更新时的迁移逻辑 + } + + /** + * 获取插件资源URL + */ + protected function asset(string $path): string + { + return asset('plugins/' . $this->pluginCode . '/' . ltrim($path, '/')); + } + + /** + * 获取插件配置项 + */ + protected function getConfigValue(string $key, $default = null) + { + return $this->config[$key] ?? $default; + } + + /** + * 获取插件数据库迁移路径 + */ + protected function getMigrationsPath(): string + { + return $this->basePath . '/database/migrations'; + } + + /** + * 获取插件视图路径 + */ + protected function getViewsPath(): string + { + return $this->basePath . '/resources/views'; + } + + /** + * 获取插件资源路径 + */ + protected function getAssetsPath(): string + { + return $this->basePath . '/resources/assets'; + } + + /** + * Register plugin scheduled tasks. Plugins can override this method. + * + * @param \Illuminate\Console\Scheduling\Schedule $schedule + * @return void + */ + public function schedule(\Illuminate\Console\Scheduling\Schedule $schedule): void + { + // Plugin can override this method to register scheduled tasks + } +} \ No newline at end of file diff --git a/Xboard/app/Services/Plugin/HookManager.php b/Xboard/app/Services/Plugin/HookManager.php new file mode 100644 index 0000000..5b6e9af --- /dev/null +++ b/Xboard/app/Services/Plugin/HookManager.php @@ -0,0 +1,286 @@ +json($response); + } + + throw new InterceptResponseException($response); + } + + /** + * Trigger action hook + * + * @param string $hook Hook name + * @param mixed $payload Data passed to hook + * @return void + */ + public static function call(string $hook, mixed $payload = null): void + { + $actions = self::getActions(); + + if (!isset($actions[$hook])) { + return; + } + + ksort($actions[$hook]); + + foreach ($actions[$hook] as $callbacks) { + foreach ($callbacks as $callback) { + $callback($payload); + } + } + } + + /** + * Trigger filter hook + * + * @param string $hook Hook name + * @param mixed $value Value to filter + * @param mixed ...$args Other parameters + * @return mixed + */ + public static function filter(string $hook, mixed $value, mixed ...$args): mixed + { + $filters = self::getFilters(); + + if (!isset($filters[$hook])) { + return $value; + } + + ksort($filters[$hook]); + + $result = $value; + foreach ($filters[$hook] as $callbacks) { + foreach ($callbacks as $callback) { + $result = $callback($result, ...$args); + } + } + + return $result; + } + + /** + * Register action hook listener + * + * @param string $hook Hook name + * @param callable $callback Callback function + * @param int $priority Priority + * @return void + */ + public static function register(string $hook, callable $callback, int $priority = 20): void + { + $actions = self::getActions(); + + if (!isset($actions[$hook])) { + $actions[$hook] = []; + } + + if (!isset($actions[$hook][$priority])) { + $actions[$hook][$priority] = []; + } + + $actions[$hook][$priority][self::getCallableId($callback)] = $callback; + + self::setActions($actions); + } + + /** + * Register filter hook + * + * @param string $hook Hook name + * @param callable $callback Callback function + * @param int $priority Priority + * @return void + */ + public static function registerFilter(string $hook, callable $callback, int $priority = 20): void + { + $filters = self::getFilters(); + + if (!isset($filters[$hook])) { + $filters[$hook] = []; + } + + if (!isset($filters[$hook][$priority])) { + $filters[$hook][$priority] = []; + } + + $filters[$hook][$priority][self::getCallableId($callback)] = $callback; + + self::setFilters($filters); + } + + /** + * Remove hook listener + * + * @param string $hook Hook name + * @param callable|null $callback Callback function + * @return void + */ + public static function remove(string $hook, ?callable $callback = null): void + { + $actions = self::getActions(); + $filters = self::getFilters(); + + if ($callback === null) { + if (isset($actions[$hook])) { + unset($actions[$hook]); + self::setActions($actions); + } + + if (isset($filters[$hook])) { + unset($filters[$hook]); + self::setFilters($filters); + } + + return; + } + + $callbackId = self::getCallableId($callback); + + if (isset($actions[$hook])) { + foreach ($actions[$hook] as $priority => $callbacks) { + if (isset($callbacks[$callbackId])) { + unset($actions[$hook][$priority][$callbackId]); + + if (empty($actions[$hook][$priority])) { + unset($actions[$hook][$priority]); + } + + if (empty($actions[$hook])) { + unset($actions[$hook]); + } + } + } + self::setActions($actions); + } + + if (isset($filters[$hook])) { + foreach ($filters[$hook] as $priority => $callbacks) { + if (isset($callbacks[$callbackId])) { + unset($filters[$hook][$priority][$callbackId]); + + if (empty($filters[$hook][$priority])) { + unset($filters[$hook][$priority]); + } + + if (empty($filters[$hook])) { + unset($filters[$hook]); + } + } + } + self::setFilters($filters); + } + } + + /** + * Check if hook exists + * + * @param string $hook Hook name + * @return bool + */ + public static function hasHook(string $hook): bool + { + $actions = self::getActions(); + $filters = self::getFilters(); + + return isset($actions[$hook]) || isset($filters[$hook]); + } + + /** + * Clear all hooks (called when Octane resets) + */ + public static function reset(): void + { + App::instance('hook.actions', []); + App::instance('hook.filters', []); + } +} \ No newline at end of file diff --git a/Xboard/app/Services/Plugin/InterceptResponseException.php b/Xboard/app/Services/Plugin/InterceptResponseException.php new file mode 100644 index 0000000..298d8d3 --- /dev/null +++ b/Xboard/app/Services/Plugin/InterceptResponseException.php @@ -0,0 +1,22 @@ +response = $response; + } + + public function getResponse(): Response + { + return $this->response; + } +} \ No newline at end of file diff --git a/Xboard/app/Services/Plugin/PluginConfigService.php b/Xboard/app/Services/Plugin/PluginConfigService.php new file mode 100644 index 0000000..977614b --- /dev/null +++ b/Xboard/app/Services/Plugin/PluginConfigService.php @@ -0,0 +1,111 @@ +pluginManager = app(PluginManager::class); + } + + /** + * 获取插件配置 + * + * @param string $pluginCode + * @return array + */ + public function getConfig(string $pluginCode): array + { + $defaultConfig = $this->getDefaultConfig($pluginCode); + if (empty($defaultConfig)) { + return []; + } + $dbConfig = $this->getDbConfig($pluginCode); + + $result = []; + foreach ($defaultConfig as $key => $item) { + $result[$key] = [ + 'type' => $item['type'], + 'label' => $item['label'] ?? '', + 'placeholder' => $item['placeholder'] ?? '', + 'description' => $item['description'] ?? '', + 'value' => $dbConfig[$key] ?? $item['default'], + 'options' => $item['options'] ?? [] + ]; + } + + return $result; + } + + /** + * 更新插件配置 + * + * @param string $pluginCode + * @param array $config + * @return bool + */ + public function updateConfig(string $pluginCode, array $config): bool + { + $defaultConfig = $this->getDefaultConfig($pluginCode); + if (empty($defaultConfig)) { + throw new \Exception('插件配置结构不存在'); + } + $values = []; + foreach ($config as $key => $value) { + if (!isset($defaultConfig[$key])) { + continue; + } + $values[$key] = $value; + } + Plugin::query() + ->where('code', $pluginCode) + ->update([ + 'config' => json_encode($values), + 'updated_at' => now() + ]); + + return true; + } + + /** + * 获取插件默认配置 + * + * @param string $pluginCode + * @return array + */ + protected function getDefaultConfig(string $pluginCode): array + { + $configFile = $this->pluginManager->getPluginPath($pluginCode) . '/config.json'; + if (!File::exists($configFile)) { + return []; + } + + $config = json_decode(File::get($configFile), true); + return $config['config'] ?? []; + } + + /** + * 获取数据库中的配置 + * + * @param string $pluginCode + * @return array + */ + public function getDbConfig(string $pluginCode): array + { + $plugin = Plugin::query() + ->where('code', $pluginCode) + ->first(); + + if (!$plugin || empty($plugin->config)) { + return []; + } + + return json_decode($plugin->config, true); + } +} \ No newline at end of file diff --git a/Xboard/app/Services/Plugin/PluginManager.php b/Xboard/app/Services/Plugin/PluginManager.php new file mode 100644 index 0000000..7859da0 --- /dev/null +++ b/Xboard/app/Services/Plugin/PluginManager.php @@ -0,0 +1,727 @@ +pluginPath = base_path('plugins'); + } + + /** + * 获取插件的命名空间 + */ + public function getPluginNamespace(string $pluginCode): string + { + return 'Plugin\\' . Str::studly($pluginCode); + } + + /** + * 获取插件的基础路径 + */ + public function getPluginPath(string $pluginCode): string + { + return $this->pluginPath . '/' . Str::studly($pluginCode); + } + + /** + * 加载插件类 + */ + protected function loadPlugin(string $pluginCode): ?AbstractPlugin + { + if (isset($this->loadedPlugins[$pluginCode])) { + return $this->loadedPlugins[$pluginCode]; + } + + $pluginClass = $this->getPluginNamespace($pluginCode) . '\\Plugin'; + + if (!class_exists($pluginClass)) { + $pluginFile = $this->getPluginPath($pluginCode) . '/Plugin.php'; + if (!File::exists($pluginFile)) { + Log::warning("Plugin class file not found: {$pluginFile}"); + Plugin::query()->where('code', $pluginCode)->delete(); + return null; + } + require_once $pluginFile; + } + + if (!class_exists($pluginClass)) { + Log::error("Plugin class not found: {$pluginClass}"); + return null; + } + + $plugin = new $pluginClass($pluginCode); + $this->loadedPlugins[$pluginCode] = $plugin; + + return $plugin; + } + + /** + * 注册插件的服务提供者 + */ + protected function registerServiceProvider(string $pluginCode): void + { + $providerClass = $this->getPluginNamespace($pluginCode) . '\\Providers\\PluginServiceProvider'; + + if (class_exists($providerClass)) { + app()->register($providerClass); + } + } + + /** + * 加载插件的路由 + */ + protected function loadRoutes(string $pluginCode): void + { + $routesPath = $this->getPluginPath($pluginCode) . '/routes'; + if (File::exists($routesPath)) { + $webRouteFile = $routesPath . '/web.php'; + $apiRouteFile = $routesPath . '/api.php'; + if (File::exists($webRouteFile)) { + Route::middleware('web') + ->namespace($this->getPluginNamespace($pluginCode) . '\\Controllers') + ->group(function () use ($webRouteFile) { + require $webRouteFile; + }); + } + if (File::exists($apiRouteFile)) { + Route::middleware('api') + ->namespace($this->getPluginNamespace($pluginCode) . '\\Controllers') + ->group(function () use ($apiRouteFile) { + require $apiRouteFile; + }); + } + } + } + + /** + * 加载插件的视图 + */ + protected function loadViews(string $pluginCode): void + { + $viewsPath = $this->getPluginPath($pluginCode) . '/resources/views'; + if (File::exists($viewsPath)) { + View::addNamespace(Str::studly($pluginCode), $viewsPath); + return; + } + } + + /** + * 注册插件命令 + */ + protected function registerPluginCommands(string $pluginCode, AbstractPlugin $pluginInstance): void + { + try { + // 调用插件的命令注册方法 + $pluginInstance->registerCommands(); + } catch (\Exception $e) { + Log::error("Failed to register commands for plugin '{$pluginCode}': " . $e->getMessage()); + } + } + + /** + * 安装插件 + */ + public function install(string $pluginCode): bool + { + $configFile = $this->getPluginPath($pluginCode) . '/config.json'; + + if (!File::exists($configFile)) { + throw new \Exception('Plugin config file not found'); + } + + $config = json_decode(File::get($configFile), true); + if (!$this->validateConfig($config)) { + throw new \Exception('Invalid plugin config'); + } + + // 检查插件是否已安装 + if (Plugin::where('code', $pluginCode)->exists()) { + throw new \Exception('Plugin already installed'); + } + + // 检查依赖 + if (!$this->checkDependencies($config['require'] ?? [])) { + throw new \Exception('Dependencies not satisfied'); + } + + // 运行数据库迁移 + $this->runMigrations(pluginCode: $pluginCode); + + DB::beginTransaction(); + try { + // 提取配置默认值 + $defaultValues = $this->extractDefaultConfig($config); + + // 创建插件实例 + $plugin = $this->loadPlugin($pluginCode); + + // 注册到数据库 + Plugin::create([ + 'code' => $pluginCode, + 'name' => $config['name'], + 'version' => $config['version'], + 'type' => $config['type'] ?? Plugin::TYPE_FEATURE, + 'is_enabled' => false, + 'config' => json_encode($defaultValues), + 'installed_at' => now(), + ]); + + // 运行插件安装方法 + if (method_exists($plugin, 'install')) { + $plugin->install(); + } + + // 发布插件资源 + $this->publishAssets($pluginCode); + + DB::commit(); + return true; + } catch (\Exception $e) { + if (DB::transactionLevel() > 0) { + DB::rollBack(); + } + throw $e; + } + } + + /** + * 提取插件默认配置 + */ + protected function extractDefaultConfig(array $config): array + { + $defaultValues = []; + if (isset($config['config']) && is_array($config['config'])) { + foreach ($config['config'] as $key => $item) { + if (is_array($item)) { + $defaultValues[$key] = $item['default'] ?? null; + } else { + $defaultValues[$key] = $item; + } + } + } + return $defaultValues; + } + + /** + * 运行插件数据库迁移 + */ + protected function runMigrations(string $pluginCode): void + { + $migrationsPath = $this->getPluginPath($pluginCode) . '/database/migrations'; + + if (File::exists($migrationsPath)) { + Artisan::call('migrate', [ + '--path' => "plugins/" . Str::studly($pluginCode) . "/database/migrations", + '--force' => true + ]); + } + } + + /** + * 回滚插件数据库迁移 + */ + protected function runMigrationsRollback(string $pluginCode): void + { + $migrationsPath = $this->getPluginPath($pluginCode) . '/database/migrations'; + + if (File::exists($migrationsPath)) { + Artisan::call('migrate:rollback', [ + '--path' => "plugins/" . Str::studly($pluginCode) . "/database/migrations", + '--force' => true + ]); + } + } + + /** + * 发布插件资源 + */ + protected function publishAssets(string $pluginCode): void + { + $assetsPath = $this->getPluginPath($pluginCode) . '/resources/assets'; + if (File::exists($assetsPath)) { + $publishPath = public_path('plugins/' . $pluginCode); + File::ensureDirectoryExists($publishPath); + File::copyDirectory($assetsPath, $publishPath); + } + } + + /** + * 验证配置文件 + */ + protected function validateConfig(array $config): bool + { + $requiredFields = [ + 'name', + 'code', + 'version', + 'description', + 'author' + ]; + + foreach ($requiredFields as $field) { + if (!isset($config[$field]) || empty($config[$field])) { + return false; + } + } + + // 验证插件代码格式 + if (!preg_match('/^[a-z0-9_]+$/', $config['code'])) { + return false; + } + + // 验证版本号格式 + if (!preg_match('/^\d+\.\d+\.\d+$/', $config['version'])) { + return false; + } + + // 验证插件类型 + if (isset($config['type'])) { + $validTypes = ['feature', 'payment']; + if (!in_array($config['type'], $validTypes)) { + return false; + } + } + + return true; + } + + /** + * 启用插件 + */ + public function enable(string $pluginCode): bool + { + $plugin = $this->loadPlugin($pluginCode); + + if (!$plugin) { + Plugin::where('code', $pluginCode)->delete(); + throw new \Exception('Plugin not found: ' . $pluginCode); + } + + // 获取插件配置 + $dbPlugin = Plugin::query() + ->where('code', $pluginCode) + ->first(); + + if ($dbPlugin && !empty($dbPlugin->config)) { + $values = json_decode($dbPlugin->config, true) ?: []; + $values = $this->castConfigValuesByType($pluginCode, $values); + $plugin->setConfig($values); + } + + // 注册服务提供者 + $this->registerServiceProvider($pluginCode); + + // 加载路由 + $this->loadRoutes($pluginCode); + + // 加载视图 + $this->loadViews($pluginCode); + + // 更新数据库状态 + Plugin::query() + ->where('code', $pluginCode) + ->update([ + 'is_enabled' => true, + 'updated_at' => now(), + ]); + // 初始化插件 + $plugin->boot(); + + return true; + } + + /** + * 禁用插件 + */ + public function disable(string $pluginCode): bool + { + $plugin = $this->loadPlugin($pluginCode); + if (!$plugin) { + throw new \Exception('Plugin not found'); + } + + Plugin::query() + ->where('code', $pluginCode) + ->update([ + 'is_enabled' => false, + 'updated_at' => now(), + ]); + + $plugin->cleanup(); + + return true; + } + + /** + * 卸载插件 + */ + public function uninstall(string $pluginCode): bool + { + $this->disable($pluginCode); + $this->runMigrationsRollback($pluginCode); + Plugin::query()->where('code', $pluginCode)->delete(); + + return true; + } + + /** + * 删除插件 + * + * @param string $pluginCode + * @return bool + * @throws \Exception + */ + public function delete(string $pluginCode): bool + { + // 先卸载插件 + if (Plugin::where('code', $pluginCode)->exists()) { + $this->uninstall($pluginCode); + } + + $pluginPath = $this->getPluginPath($pluginCode); + if (!File::exists($pluginPath)) { + throw new \Exception('插件不存在'); + } + + // 删除插件目录 + File::deleteDirectory($pluginPath); + + return true; + } + + /** + * 检查依赖关系 + */ + protected function checkDependencies(array $requires): bool + { + foreach ($requires as $package => $version) { + if ($package === 'xboard') { + // 检查xboard版本 + // 实现版本比较逻辑 + } + } + return true; + } + + /** + * 升级插件 + * + * @param string $pluginCode + * @return bool + * @throws \Exception + */ + public function update(string $pluginCode): bool + { + $dbPlugin = Plugin::where('code', $pluginCode)->first(); + if (!$dbPlugin) { + throw new \Exception('Plugin not installed: ' . $pluginCode); + } + + // 获取插件配置文件中的最新版本 + $configFile = $this->getPluginPath($pluginCode) . '/config.json'; + if (!File::exists($configFile)) { + throw new \Exception('Plugin config file not found'); + } + + $config = json_decode(File::get($configFile), true); + if (!$config || !isset($config['version'])) { + throw new \Exception('Invalid plugin config or missing version'); + } + + $newVersion = $config['version']; + $oldVersion = $dbPlugin->version; + + if (version_compare($newVersion, $oldVersion, '<=')) { + throw new \Exception('Plugin is already up to date'); + } + + $this->disable($pluginCode); + $this->runMigrations($pluginCode); + + $plugin = $this->loadPlugin($pluginCode); + if ($plugin) { + if (!empty($dbPlugin->config)) { + $values = json_decode($dbPlugin->config, true) ?: []; + $values = $this->castConfigValuesByType($pluginCode, $values); + $plugin->setConfig($values); + } + + $plugin->update($oldVersion, $newVersion); + } + + $dbPlugin->update([ + 'version' => $newVersion, + 'updated_at' => now(), + ]); + + $this->enable($pluginCode); + + return true; + } + + /** + * 上传插件 + * + * @param \Illuminate\Http\UploadedFile $file + * @return bool + * @throws \Exception + */ + public function upload($file): bool + { + $tmpPath = storage_path('tmp/plugins'); + if (!File::exists($tmpPath)) { + File::makeDirectory($tmpPath, 0755, true); + } + + $extractPath = $tmpPath . '/' . uniqid(); + $zip = new \ZipArchive(); + + if ($zip->open($file->path()) !== true) { + throw new \Exception('无法打开插件包文件'); + } + + $zip->extractTo($extractPath); + $zip->close(); + + $configFile = File::glob($extractPath . '/*/config.json'); + if (empty($configFile)) { + $configFile = File::glob($extractPath . '/config.json'); + } + + if (empty($configFile)) { + File::deleteDirectory($extractPath); + throw new \Exception('插件包格式错误:缺少配置文件'); + } + + $pluginPath = dirname(reset($configFile)); + $config = json_decode(File::get($pluginPath . '/config.json'), true); + + if (!$this->validateConfig($config)) { + File::deleteDirectory($extractPath); + throw new \Exception('插件配置文件格式错误'); + } + + $targetPath = $this->pluginPath . '/' . Str::studly($config['code']); + if (File::exists($targetPath)) { + $installedConfigPath = $targetPath . '/config.json'; + if (!File::exists($installedConfigPath)) { + throw new \Exception('已安装插件缺少配置文件,无法判断是否可升级'); + } + $installedConfig = json_decode(File::get($installedConfigPath), true); + + $oldVersion = $installedConfig['version'] ?? null; + $newVersion = $config['version'] ?? null; + if (!$oldVersion || !$newVersion) { + throw new \Exception('插件缺少版本号,无法判断是否可升级'); + } + if (version_compare($newVersion, $oldVersion, '<=')) { + throw new \Exception('上传插件版本不高于已安装版本,无法升级'); + } + + File::deleteDirectory($targetPath); + } + + File::copyDirectory($pluginPath, $targetPath); + File::deleteDirectory($pluginPath); + File::deleteDirectory($extractPath); + + if (Plugin::where('code', $config['code'])->exists()) { + return $this->update($config['code']); + } + + return true; + } + + /** + * Initializes all enabled plugins from the database. + * This method ensures that plugins are loaded, and their routes, views, + * and service providers are registered only once per request cycle. + */ + public function initializeEnabledPlugins(): void + { + if ($this->pluginsInitialized) { + return; + } + + $enabledPlugins = Plugin::where('is_enabled', true)->get(); + + foreach ($enabledPlugins as $dbPlugin) { + try { + $pluginCode = $dbPlugin->code; + + $pluginInstance = $this->loadPlugin($pluginCode); + if (!$pluginInstance) { + continue; + } + + if (!empty($dbPlugin->config)) { + $values = json_decode($dbPlugin->config, true) ?: []; + $values = $this->castConfigValuesByType($pluginCode, $values); + $pluginInstance->setConfig($values); + } + + $this->registerServiceProvider($pluginCode); + $this->loadRoutes($pluginCode); + $this->loadViews($pluginCode); + $this->registerPluginCommands($pluginCode, $pluginInstance); + + $pluginInstance->boot(); + + } catch (\Exception $e) { + Log::error("Failed to initialize plugin '{$dbPlugin->code}': " . $e->getMessage()); + } + } + + $this->pluginsInitialized = true; + } + + /** + * Register scheduled tasks for all enabled plugins. + * Called from Console Kernel. Only loads main plugin class and config for scheduling. + * Avoids full HTTP/plugin boot overhead. + * + * @param \Illuminate\Console\Scheduling\Schedule $schedule + */ + public function registerPluginSchedules(Schedule $schedule): void + { + Plugin::where('is_enabled', true) + ->get() + ->each(function ($dbPlugin) use ($schedule) { + try { + $pluginInstance = $this->loadPlugin($dbPlugin->code); + if (!$pluginInstance) { + return; + } + if (!empty($dbPlugin->config)) { + $values = json_decode($dbPlugin->config, true) ?: []; + $values = $this->castConfigValuesByType($dbPlugin->code, $values); + $pluginInstance->setConfig($values); + } + $pluginInstance->schedule($schedule); + + } catch (\Exception $e) { + Log::error("Failed to register schedule for plugin '{$dbPlugin->code}': " . $e->getMessage()); + } + }); + } + + /** + * Get all enabled plugin instances. + * + * This method ensures that all enabled plugins are initialized and then returns them. + * It's the central point for accessing active plugins. + * + * @return array + */ + public function getEnabledPlugins(): array + { + $this->initializeEnabledPlugins(); + + $enabledPluginCodes = Plugin::where('is_enabled', true) + ->pluck('code') + ->all(); + + return array_intersect_key($this->loadedPlugins, array_flip($enabledPluginCodes)); + } + + /** + * Get enabled plugins by type + */ + public function getEnabledPluginsByType(string $type): array + { + $this->initializeEnabledPlugins(); + + $enabledPluginCodes = Plugin::where('is_enabled', true) + ->byType($type) + ->pluck('code') + ->all(); + + return array_intersect_key($this->loadedPlugins, array_flip($enabledPluginCodes)); + } + + /** + * Get enabled payment plugins + */ + public function getEnabledPaymentPlugins(): array + { + return $this->getEnabledPluginsByType('payment'); + } + + /** + * install default plugins + */ + public static function installDefaultPlugins(): void + { + foreach (Plugin::PROTECTED_PLUGINS as $pluginCode) { + if (!Plugin::where('code', $pluginCode)->exists()) { + $pluginManager = app(self::class); + $pluginManager->install($pluginCode); + $pluginManager->enable($pluginCode); + Log::info("Installed and enabled default plugin: {$pluginCode}"); + } + } + } + + /** + * 根据 config.json 的类型信息对配置值进行类型转换(仅处理 type=json 键)。 + */ + protected function castConfigValuesByType(string $pluginCode, array $values): array + { + $types = $this->getConfigTypes($pluginCode); + foreach ($values as $key => $value) { + $type = $types[$key] ?? null; + + if ($type === 'json') { + if (is_array($value)) { + continue; + } + + if (is_string($value) && $value !== '') { + $decoded = json_decode($value, true); + if (json_last_error() === JSON_ERROR_NONE) { + $values[$key] = $decoded; + } + } + } + } + return $values; + } + + /** + * 读取并缓存插件 config.json 中的键类型映射。 + */ + protected function getConfigTypes(string $pluginCode): array + { + if (isset($this->configTypesCache[$pluginCode])) { + return $this->configTypesCache[$pluginCode]; + } + $types = []; + $configFile = $this->getPluginPath($pluginCode) . '/config.json'; + if (File::exists($configFile)) { + $config = json_decode(File::get($configFile), true); + $fields = $config['config'] ?? []; + foreach ($fields as $key => $meta) { + $types[$key] = is_array($meta) ? ($meta['type'] ?? 'string') : 'string'; + } + } + $this->configTypesCache[$pluginCode] = $types; + return $types; + } +} \ No newline at end of file diff --git a/Xboard/app/Services/ServerService.php b/Xboard/app/Services/ServerService.php new file mode 100644 index 0000000..00491ca --- /dev/null +++ b/Xboard/app/Services/ServerService.php @@ -0,0 +1,296 @@ +get()->append([ + 'last_check_at', + 'last_push_at', + 'online', + 'is_online', + 'available_status', + 'cache_key', + 'load_status', + 'metrics', + 'online_conn' + ]); + } + + /** + * 获取指定用户可用的服务器列表 + * @param User $user + * @return array + */ + public static function getAvailableServers(User $user): array + { + $servers = Server::whereJsonContains('group_ids', (string) $user->group_id) + ->where('show', true) + ->where(function ($query) { + $query->whereNull('transfer_enable') + ->orWhere('transfer_enable', 0) + ->orWhereRaw('u + d < transfer_enable'); + }) + ->orderBy('sort', 'ASC') + ->get() + ->append(['last_check_at', 'last_push_at', 'online', 'is_online', 'available_status', 'cache_key', 'server_key']); + + $servers = collect($servers)->map(function ($server) use ($user) { + // 判断动态端口 + if (str_contains($server->port, '-')) { + $port = $server->port; + $server->port = (int) Helper::randomPort($port); + $server->ports = $port; + } else { + $server->port = (int) $server->port; + } + $server->password = $server->generateServerPassword($user); + $server->rate = $server->getCurrentRate(); + return $server; + })->toArray(); + + return $servers; + } + + /** + * 根据权限组获取可用的用户列表 + * @param array $groupIds + * @return Collection + */ + public static function getAvailableUsers(Server $node) + { + $users = User::toBase() + ->whereIn('group_id', $node->group_ids) + ->whereRaw('u + d < transfer_enable') + ->where(function ($query) { + $query->where('expired_at', '>=', time()) + ->orWhere('expired_at', NULL); + }) + ->where('banned', 0) + ->select([ + 'id', + 'uuid', + 'speed_limit', + 'device_limit' + ]) + ->get(); + return HookManager::filter('server.users.get', $users, $node); + } + + // 获取路由规则 + public static function getRoutes(array $routeIds) + { + $routes = ServerRoute::select(['id', 'match', 'action', 'action_value'])->whereIn('id', $routeIds)->get(); + return $routes; + } + + /** + * Update node metrics and load status + */ + public static function updateMetrics(Server $node, array $metrics): void + { + $nodeType = strtoupper($node->type); + $nodeId = $node->id; + $cacheTime = max(300, (int) admin_setting('server_push_interval', 60) * 3); + + $metricsData = [ + 'uptime' => (int) ($metrics['uptime'] ?? 0), + 'goroutines' => (int) ($metrics['goroutines'] ?? 0), + 'active_connections' => (int) ($metrics['active_connections'] ?? 0), + 'total_connections' => (int) ($metrics['total_connections'] ?? 0), + 'total_users' => (int) ($metrics['total_users'] ?? 0), + 'active_users' => (int) ($metrics['active_users'] ?? 0), + 'inbound_speed' => (int) ($metrics['inbound_speed'] ?? 0), + 'outbound_speed' => (int) ($metrics['outbound_speed'] ?? 0), + 'cpu_per_core' => $metrics['cpu_per_core'] ?? [], + 'load' => $metrics['load'] ?? [], + 'speed_limiter' => $metrics['speed_limiter'] ?? [], + 'gc' => $metrics['gc'] ?? [], + 'api' => $metrics['api'] ?? [], + 'ws' => $metrics['ws'] ?? [], + 'limits' => $metrics['limits'] ?? [], + 'updated_at' => now()->timestamp, + 'kernel_status' => (bool) ($metrics['kernel_status'] ?? false), + ]; + + \Illuminate\Support\Facades\Cache::put( + \App\Utils\CacheKey::get('SERVER_' . $nodeType . '_METRICS', $nodeId), + $metricsData, + $cacheTime + ); + } + + public static function buildNodeConfig(Server $node): array + { + $nodeType = $node->type; + $protocolSettings = $node->protocol_settings; + $serverPort = $node->server_port; + $host = $node->host; + + $baseConfig = [ + 'protocol' => $nodeType, + 'listen_ip' => '0.0.0.0', + 'server_port' => (int) $serverPort, + 'network' => data_get($protocolSettings, 'network'), + 'networkSettings' => data_get($protocolSettings, 'network_settings') ?: null, + ]; + + $response = match ($nodeType) { + 'shadowsocks' => [ + ...$baseConfig, + 'cipher' => $protocolSettings['cipher'], + 'plugin' => $protocolSettings['plugin'], + 'plugin_opts' => $protocolSettings['plugin_opts'], + 'server_key' => match ($protocolSettings['cipher']) { + '2022-blake3-aes-128-gcm' => Helper::getServerKey($node->created_at, 16), + '2022-blake3-aes-256-gcm' => Helper::getServerKey($node->created_at, 32), + default => null, + }, + ], + 'vmess' => [ + ...$baseConfig, + 'tls' => (int) $protocolSettings['tls'], + 'multiplex' => data_get($protocolSettings, 'multiplex'), + ], + 'trojan' => [ + ...$baseConfig, + 'host' => $host, + 'server_name' => $protocolSettings['server_name'], + 'multiplex' => data_get($protocolSettings, 'multiplex'), + 'tls' => (int) $protocolSettings['tls'], + 'tls_settings' => match ((int) $protocolSettings['tls']) { + 2 => $protocolSettings['reality_settings'], + default => null, + }, + ], + 'vless' => [ + ...$baseConfig, + 'tls' => (int) $protocolSettings['tls'], + 'flow' => $protocolSettings['flow'], + 'decryption' => data_get($protocolSettings, 'encryption.decryption'), + 'tls_settings' => match ((int) $protocolSettings['tls']) { + 2 => $protocolSettings['reality_settings'], + default => $protocolSettings['tls_settings'], + }, + 'multiplex' => data_get($protocolSettings, 'multiplex'), + ], + 'hysteria' => [ + ...$baseConfig, + 'server_port' => (int) $serverPort, + 'version' => (int) $protocolSettings['version'], + 'host' => $host, + 'server_name' => $protocolSettings['tls']['server_name'], + 'up_mbps' => (int) $protocolSettings['bandwidth']['up'], + 'down_mbps' => (int) $protocolSettings['bandwidth']['down'], + ...match ((int) $protocolSettings['version']) { + 1 => ['obfs' => $protocolSettings['obfs']['password'] ?? null], + 2 => [ + 'obfs' => $protocolSettings['obfs']['open'] ? $protocolSettings['obfs']['type'] : null, + 'obfs-password' => $protocolSettings['obfs']['password'] ?? null, + ], + default => [], + }, + ], + 'tuic' => [ + ...$baseConfig, + 'version' => (int) $protocolSettings['version'], + 'server_port' => (int) $serverPort, + 'server_name' => $protocolSettings['tls']['server_name'], + 'congestion_control' => $protocolSettings['congestion_control'], + 'tls_settings' => data_get($protocolSettings, 'tls_settings'), + 'auth_timeout' => '3s', + 'zero_rtt_handshake' => false, + 'heartbeat' => '3s', + ], + 'anytls' => [ + ...$baseConfig, + 'server_port' => (int) $serverPort, + 'server_name' => $protocolSettings['tls']['server_name'], + 'padding_scheme' => $protocolSettings['padding_scheme'], + ], + 'socks' => [ + ...$baseConfig, + 'server_port' => (int) $serverPort, + ], + 'naive' => [ + ...$baseConfig, + 'server_port' => (int) $serverPort, + 'tls' => (int) $protocolSettings['tls'], + 'tls_settings' => $protocolSettings['tls_settings'], + ], + 'http' => [ + ...$baseConfig, + 'server_port' => (int) $serverPort, + 'tls' => (int) $protocolSettings['tls'], + 'tls_settings' => $protocolSettings['tls_settings'], + ], + 'mieru' => [ + ...$baseConfig, + 'server_port' => (int) $serverPort, + 'transport' => data_get($protocolSettings, 'transport', 'TCP'), + 'traffic_pattern' => $protocolSettings['traffic_pattern'], + // 'multiplex' => data_get($protocolSettings, 'multiplex'), + ], + default => [], + }; + + // $response = array_filter( + // $response, + // static fn ($value) => $value !== null + // ); + + if (!empty($node['route_ids'])) { + $response['routes'] = self::getRoutes($node['route_ids']); + } + + if (!empty($node['custom_outbounds'])) { + $response['custom_outbounds'] = $node['custom_outbounds']; + } + + if (!empty($node['custom_routes'])) { + $response['custom_routes'] = $node['custom_routes']; + } + + if (!empty($node['cert_config']) && data_get($node['cert_config'],'cert_mode') !== 'none' ) { + $response['cert_config'] = $node['cert_config']; + } + + return $response; + } + + /** + * 根据协议类型和标识获取服务器 + * @param int $serverId + * @param string $serverType + * @return Server|null + */ + public static function getServer($serverId, ?string $serverType = null): Server | null + { + return Server::query() + ->when($serverType, function ($query) use ($serverType) { + $query->where('type', Server::normalizeType($serverType)); + }) + ->where(function ($query) use ($serverId) { + $query->where('code', $serverId) + ->orWhere('id', $serverId); + }) + ->orderByRaw('CASE WHEN code = ? THEN 0 ELSE 1 END', [$serverId]) + ->first(); + } +} diff --git a/Xboard/app/Services/SettingService.php b/Xboard/app/Services/SettingService.php new file mode 100644 index 0000000..37e34d9 --- /dev/null +++ b/Xboard/app/Services/SettingService.php @@ -0,0 +1,18 @@ +first(); + return $setting ? $setting->value : $default; + } + + public function getAll(){ + return SettingModel::all()->pluck('value', 'name')->toArray(); + } +} diff --git a/Xboard/app/Services/StatisticalService.php b/Xboard/app/Services/StatisticalService.php new file mode 100644 index 0000000..e57d6ae --- /dev/null +++ b/Xboard/app/Services/StatisticalService.php @@ -0,0 +1,378 @@ +redis = Redis::connection(); + + } + + public function setStartAt($timestamp) + { + $this->startAt = $timestamp; + $this->statServerKey = "stat_server_{$this->startAt}"; + $this->statUserKey = "stat_user_{$this->startAt}"; + } + + public function setEndAt($timestamp) + { + $this->endAt = $timestamp; + } + + /** + * 生成统计报表 + */ + public function generateStatData(): array + { + $startAt = $this->startAt; + $endAt = $this->endAt; + if (!$startAt || !$endAt) { + $startAt = strtotime(date('Y-m-d')); + $endAt = strtotime('+1 day', $startAt); + } + $data = []; + $data['order_count'] = Order::where('created_at', '>=', $startAt) + ->where('created_at', '<', $endAt) + ->count(); + $data['order_total'] = Order::where('created_at', '>=', $startAt) + ->where('created_at', '<', $endAt) + ->sum('total_amount'); + $data['paid_count'] = Order::where('paid_at', '>=', $startAt) + ->where('paid_at', '<', $endAt) + ->whereNotIn('status', [0, 2]) + ->count(); + $data['paid_total'] = Order::where('paid_at', '>=', $startAt) + ->where('paid_at', '<', $endAt) + ->whereNotIn('status', [0, 2]) + ->sum('total_amount'); + $commissionLogBuilder = CommissionLog::where('created_at', '>=', $startAt) + ->where('created_at', '<', $endAt); + $data['commission_count'] = $commissionLogBuilder->count(); + $data['commission_total'] = $commissionLogBuilder->sum('get_amount'); + $data['register_count'] = User::where('created_at', '>=', $startAt) + ->where('created_at', '<', $endAt) + ->count(); + $data['invite_count'] = User::where('created_at', '>=', $startAt) + ->where('created_at', '<', $endAt) + ->whereNotNull('invite_user_id') + ->count(); + $data['transfer_used_total'] = StatServer::where('created_at', '>=', $startAt) + ->where('created_at', '<', $endAt) + ->select(DB::raw('SUM(u) + SUM(d) as total')) + ->value('total') ?? 0; + return $data; + } + + /** + * 往服务器报表缓存正追加流量使用数据 + */ + public function statServer($serverId, $serverType, $u, $d) + { + $u_menber = "{$serverType}_{$serverId}_u"; //储存上传流量的集合成员 + $d_menber = "{$serverType}_{$serverId}_d"; //储存下载流量的集合成员 + $this->redis->zincrby($this->statServerKey, $u, $u_menber); + $this->redis->zincrby($this->statServerKey, $d, $d_menber); + } + + /** + * 追加用户使用流量 + */ + public function statUser($rate, $userId, $u, $d) + { + $u_menber = "{$rate}_{$userId}_u"; //储存上传流量的集合成员 + $d_menber = "{$rate}_{$userId}_d"; //储存下载流量的集合成员 + $this->redis->zincrby($this->statUserKey, $u, $u_menber); + $this->redis->zincrby($this->statUserKey, $d, $d_menber); + } + + /** + * 获取指定用户的流量使用情况 + */ + public function getStatUserByUserID(int|string $userId): array + { + + $stats = []; + $statsUser = $this->redis->zrange($this->statUserKey, 0, -1, true); + foreach ($statsUser as $member => $value) { + list($rate, $uid, $type) = explode('_', $member); + if (intval($uid) !== intval($userId)) + continue; + $key = "{$rate}_{$uid}"; + $stats[$key] = $stats[$key] ?? [ + 'record_at' => $this->startAt, + 'server_rate' => number_format((float) $rate, 2, '.', ''), + 'u' => 0, + 'd' => 0, + 'user_id' => intval($userId), + ]; + $stats[$key][$type] += $value; + } + return array_values($stats); + } + + /** + * 获取缓存中的用户报表 + */ + public function getStatUser() + { + $stats = []; + $statsUser = $this->redis->zrange($this->statUserKey, 0, -1, true); + foreach ($statsUser as $member => $value) { + list($rate, $uid, $type) = explode('_', $member); + $key = "{$rate}_{$uid}"; + $stats[$key] = $stats[$key] ?? [ + 'record_at' => $this->startAt, + 'server_rate' => $rate, + 'u' => 0, + 'd' => 0, + 'user_id' => intval($uid), + ]; + $stats[$key][$type] += $value; + } + return array_values($stats); + } + + + /** + * Retrieve server statistics from Redis cache. + * + * @return array + */ + public function getStatServer(): array + { + /** @var array $stats */ + $stats = []; + $statsServer = $this->redis->zrange($this->statServerKey, 0, -1, true); + + foreach ($statsServer as $member => $value) { + $parts = explode('_', $member); + if (count($parts) !== 3) { + continue; // Skip malformed members + } + [$serverType, $serverId, $type] = $parts; + + if (!in_array($type, ['u', 'd'], true)) { + continue; // Skip invalid types + } + + $key = "{$serverType}_{$serverId}"; + if (!isset($stats[$key])) { + $stats[$key] = [ + 'server_id' => (int) $serverId, + 'server_type' => $serverType, + 'u' => 0.0, + 'd' => 0.0, + ]; + } + $stats[$key][$type] += (float) $value; + } + + return array_values($stats); + } + + /** + * 清除用户报表缓存数据 + */ + public function clearStatUser() + { + $this->redis->del($this->statUserKey); + } + + /** + * 清除服务器报表缓存数据 + */ + public function clearStatServer() + { + $this->redis->del($this->statServerKey); + } + + public function getStatRecord($type) + { + switch ($type) { + case "paid_total": { + return Stat::select([ + '*', + DB::raw('paid_total / 100 as paid_total') + ]) + ->where('record_at', '>=', $this->startAt) + ->where('record_at', '<', $this->endAt) + ->orderBy('record_at', 'ASC') + ->get(); + } + case "commission_total": { + return Stat::select([ + '*', + DB::raw('commission_total / 100 as commission_total') + ]) + ->where('record_at', '>=', $this->startAt) + ->where('record_at', '<', $this->endAt) + ->orderBy('record_at', 'ASC') + ->get(); + } + case "register_count": { + return Stat::where('record_at', '>=', $this->startAt) + ->where('record_at', '<', $this->endAt) + ->orderBy('record_at', 'ASC') + ->get(); + } + } + } + + public function getRanking($type, $limit = 20) + { + switch ($type) { + case 'server_traffic_rank': { + return $this->buildServerTrafficRank($limit); + } + case 'user_consumption_rank': { + return $this->buildUserConsumptionRank($limit); + } + case 'invite_rank': { + return $this->buildInviteRank($limit); + } + } + } + + /** + * 获取指定日期范围内的节点流量排行 + * @param mixed ...$times 可选值:'today', 'tomorrow', 'last_week',或指定日期范围,格式:timestamp + * @return array + */ + + public static function getServerRank(...$times) + { + $startAt = 0; + $endAt = Carbon::tomorrow()->endOfDay()->timestamp; + + if (count($times) == 1) { + switch ($times[0]) { + case 'today': + $startAt = Carbon::today()->startOfDay()->timestamp; + $endAt = Carbon::today()->endOfDay()->timestamp; + break; + case 'yesterday': + $startAt = Carbon::yesterday()->startOfDay()->timestamp; + $endAt = Carbon::yesterday()->endOfDay()->timestamp; + break; + case 'last_week': + $startAt = Carbon::now()->subWeek()->startOfWeek()->timestamp; + $endAt = Carbon::now()->endOfDay()->timestamp; + break; + } + } else if (count($times) == 2) { + $startAt = $times[0]; + $endAt = $times[1]; + } + + $statistics = Server::whereHas( + 'stats', + function ($query) use ($startAt, $endAt) { + $query->where('record_at', '>=', $startAt) + ->where('record_at', '<', $endAt) + ->where('record_type', 'd'); + } + ) + ->withSum('stats as u', 'u') // 预加载 u 的总和 + ->withSum('stats as d', 'd') // 预加载 d 的总和 + ->get() + ->map(function ($item) { + return [ + 'server_name' => optional($item->parent)->name ?? $item->name, + 'server_id' => $item->id, + 'server_type' => $item->type, + 'u' => (int) $item->u, + 'd' => (int) $item->d, + 'total' => (int) $item->u + (int) $item->d, + ]; + }) + ->sortByDesc('total') + ->values() + ->toArray(); + return $statistics; + } + + private function buildInviteRank($limit) + { + $stats = User::select([ + 'invite_user_id', + DB::raw('count(*) as count') + ]) + ->where('created_at', '>=', $this->startAt) + ->where('created_at', '<', $this->endAt) + ->whereNotNull('invite_user_id') + ->groupBy('invite_user_id') + ->orderBy('count', 'DESC') + ->limit($limit) + ->get(); + + $users = User::whereIn('id', $stats->pluck('invite_user_id')->toArray())->get()->keyBy('id'); + foreach ($stats as $k => $v) { + if (!isset($users[$v['invite_user_id']])) + continue; + $stats[$k]['email'] = $users[$v['invite_user_id']]['email']; + } + return $stats; + } + + private function buildUserConsumptionRank($limit) + { + $stats = StatUser::select([ + 'user_id', + DB::raw('sum(u) as u'), + DB::raw('sum(d) as d'), + DB::raw('sum(u) + sum(d) as total') + ]) + ->where('record_at', '>=', $this->startAt) + ->where('record_at', '<', $this->endAt) + ->groupBy('user_id') + ->orderBy('total', 'DESC') + ->limit($limit) + ->get(); + $users = User::whereIn('id', $stats->pluck('user_id')->toArray())->get()->keyBy('id'); + foreach ($stats as $k => $v) { + if (!isset($users[$v['user_id']])) + continue; + $stats[$k]['email'] = $users[$v['user_id']]['email']; + } + return $stats; + } + + private function buildServerTrafficRank($limit) + { + return StatServer::select([ + 'server_id', + 'server_type', + DB::raw('sum(u) as u'), + DB::raw('sum(d) as d'), + DB::raw('sum(u) + sum(d) as total') + ]) + ->where('record_at', '>=', $this->startAt) + ->where('record_at', '<', $this->endAt) + ->groupBy('server_id', 'server_type') + ->orderBy('total', 'DESC') + ->limit($limit) + ->get(); + } +} diff --git a/Xboard/app/Services/TelegramService.php b/Xboard/app/Services/TelegramService.php new file mode 100644 index 0000000..83f52c7 --- /dev/null +++ b/Xboard/app/Services/TelegramService.php @@ -0,0 +1,160 @@ +apiUrl = "https://api.telegram.org/bot{$botToken}/"; + + $this->http = Http::timeout(30) + ->retry(3, 1000) + ->withHeaders([ + 'Accept' => 'application/json', + ]); + } + + public function sendMessage(int $chatId, string $text, string $parseMode = ''): void + { + $text = $parseMode === 'markdown' ? str_replace('_', '\_', $text) : $text; + + $this->request('sendMessage', [ + 'chat_id' => $chatId, + 'text' => $text, + 'parse_mode' => $parseMode ?: null, + ]); + } + + public function approveChatJoinRequest(int $chatId, int $userId): void + { + $this->request('approveChatJoinRequest', [ + 'chat_id' => $chatId, + 'user_id' => $userId, + ]); + } + + public function declineChatJoinRequest(int $chatId, int $userId): void + { + $this->request('declineChatJoinRequest', [ + 'chat_id' => $chatId, + 'user_id' => $userId, + ]); + } + + public function getMe(): object + { + return $this->request('getMe'); + } + + public function setWebhook(string $url): object + { + $result = $this->request('setWebhook', ['url' => $url]); + return $result; + } + + /** + * 注册 Bot 命令列表 + */ + public function registerBotCommands(): void + { + try { + $commands = HookManager::filter('telegram.bot.commands', []); + + if (empty($commands)) { + Log::warning('没有找到任何 Telegram Bot 命令'); + return; + } + + $this->request('setMyCommands', [ + 'commands' => json_encode($commands), + 'scope' => json_encode(['type' => 'default']) + ]); + + Log::info('Telegram Bot 命令注册成功', [ + 'commands_count' => count($commands), + 'commands' => $commands + ]); + + } catch (\Exception $e) { + Log::error('Telegram Bot 命令注册失败', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + } + } + + /** + * 获取当前注册的命令列表 + */ + public function getMyCommands(): object + { + return $this->request('getMyCommands'); + } + + /** + * 删除所有命令 + */ + public function deleteMyCommands(): object + { + return $this->request('deleteMyCommands'); + } + + public function sendMessageWithAdmin(string $message, bool $isStaff = false): void + { + $query = User::where('telegram_id', '!=', null); + $query->where( + fn($q) => $q->where('is_admin', 1) + ->when($isStaff, fn($q) => $q->orWhere('is_staff', 1)) + ); + $users = $query->get(); + foreach ($users as $user) { + SendTelegramJob::dispatch($user->telegram_id, $message); + } + } + + protected function request(string $method, array $params = []): object + { + try { + $response = $this->http->get($this->apiUrl . $method, $params); + + if (!$response->successful()) { + throw new ApiException("HTTP 请求失败: {$response->status()}"); + } + + $data = $response->object(); + + if (!isset($data->ok)) { + throw new ApiException('无效的 Telegram API 响应'); + } + + if (!$data->ok) { + $description = $data->description ?? '未知错误'; + throw new ApiException("Telegram API 错误: {$description}"); + } + + return $data; + + } catch (\Exception $e) { + Log::error('Telegram API 请求失败', [ + 'method' => $method, + 'params' => $params, + 'error' => $e->getMessage(), + ]); + + throw new ApiException("Telegram 服务错误: {$e->getMessage()}"); + } + } +} diff --git a/Xboard/app/Services/ThemeService.php b/Xboard/app/Services/ThemeService.php new file mode 100644 index 0000000..232682d --- /dev/null +++ b/Xboard/app/Services/ThemeService.php @@ -0,0 +1,424 @@ +registerThemeViewPaths(); + } + + /** + * Register theme view paths + */ + private function registerThemeViewPaths(): void + { + $systemPath = base_path(self::SYSTEM_THEME_DIR); + if (File::exists($systemPath)) { + View::addNamespace('theme', $systemPath); + } + + $userPath = base_path(self::USER_THEME_DIR); + if (File::exists($userPath)) { + View::prependNamespace('theme', $userPath); + } + } + + /** + * Get theme view path + */ + public function getThemeViewPath(string $theme): ?string + { + $themePath = $this->getThemePath($theme); + if (!$themePath) { + return null; + } + return $themePath . '/dashboard.blade.php'; + } + + /** + * Get all available themes + */ + public function getList(): array + { + $themes = []; + + // 获取系统主题 + $systemPath = base_path(self::SYSTEM_THEME_DIR); + if (File::exists($systemPath)) { + $themes = $this->getThemesFromPath($systemPath, false); + } + + // 获取用户主题 + $userPath = base_path(self::USER_THEME_DIR); + if (File::exists($userPath)) { + $themes = array_merge($themes, $this->getThemesFromPath($userPath, true)); + } + + return $themes; + } + + /** + * Get themes from specified path + */ + private function getThemesFromPath(string $path, bool $canDelete): array + { + return collect(File::directories($path)) + ->mapWithKeys(function ($dir) use ($canDelete) { + $name = basename($dir); + if ( + !File::exists($dir . '/' . self::CONFIG_FILE) || + !File::exists($dir . '/dashboard.blade.php') + ) { + return []; + } + $config = $this->readConfigFile($name); + if (!$config) { + return []; + } + + $config['can_delete'] = $canDelete && $name !== admin_setting('current_theme'); + $config['is_system'] = !$canDelete; + return [$name => $config]; + })->toArray(); + } + + /** + * Upload new theme + */ + public function upload(UploadedFile $file): bool + { + $zip = new ZipArchive; + $tmpPath = storage_path('tmp/' . uniqid()); + + try { + if ($zip->open($file->path()) !== true) { + throw new Exception('Invalid theme package'); + } + + $configEntry = collect(range(0, $zip->numFiles - 1)) + ->map(fn($i) => $zip->getNameIndex($i)) + ->first(fn($name) => basename($name) === self::CONFIG_FILE); + + if (!$configEntry) { + throw new Exception('Theme config file not found'); + } + + $zip->extractTo($tmpPath); + $zip->close(); + + $sourcePath = $tmpPath . '/' . rtrim(dirname($configEntry), '.'); + $configFile = $sourcePath . '/' . self::CONFIG_FILE; + + if (!File::exists($configFile)) { + throw new Exception('Theme config file not found'); + } + + $config = json_decode(File::get($configFile), true); + if (empty($config['name'])) { + throw new Exception('Theme name not configured'); + } + + if (in_array($config['name'], self::SYSTEM_THEMES)) { + throw new Exception('Cannot upload theme with same name as system theme'); + } + + if (!File::exists($sourcePath . '/dashboard.blade.php')) { + throw new Exception('Missing required theme file: dashboard.blade.php'); + } + + $userThemePath = base_path(self::USER_THEME_DIR); + if (!File::exists($userThemePath)) { + File::makeDirectory($userThemePath, 0755, true); + } + + $targetPath = $userThemePath . $config['name']; + if (File::exists($targetPath)) { + $oldConfigFile = $targetPath . '/config.json'; + if (!File::exists($oldConfigFile)) { + throw new Exception('Existing theme missing config file'); + } + $oldConfig = json_decode(File::get($oldConfigFile), true); + $oldVersion = $oldConfig['version'] ?? '0.0.0'; + $newVersion = $config['version'] ?? '0.0.0'; + if (version_compare($newVersion, $oldVersion, '>')) { + $this->cleanupThemeFiles($config['name']); + File::deleteDirectory($targetPath); + File::copyDirectory($sourcePath, $targetPath); + // 更新主题时保留用户配置 + $this->initConfig($config['name'], true); + return true; + } else { + throw new Exception('Theme exists and not a newer version'); + } + } + + File::copyDirectory($sourcePath, $targetPath); + $this->initConfig($config['name']); + + return true; + + } catch (Exception $e) { + throw $e; + } finally { + if (File::exists($tmpPath)) { + File::deleteDirectory($tmpPath); + } + } + } + + /** + * Switch theme + */ + public function switch(string|null $theme): bool + { + if ($theme === null) { + return true; + } + + $currentTheme = admin_setting('current_theme'); + + try { + $themePath = $this->getThemePath($theme); + if (!$themePath) { + throw new Exception('Theme not found'); + } + + if (!File::exists($this->getThemeViewPath($theme))) { + throw new Exception('Theme view file not found'); + } + + if ($currentTheme && $currentTheme !== $theme) { + $this->cleanupThemeFiles($currentTheme); + } + + $targetPath = public_path('theme/' . $theme); + if (!File::copyDirectory($themePath, $targetPath)) { + throw new Exception('Failed to copy theme files'); + } + + admin_setting(['current_theme' => $theme]); + return true; + + } catch (Exception $e) { + Log::error('Theme switch failed', ['theme' => $theme, 'error' => $e->getMessage()]); + throw $e; + } + } + + /** + * Delete theme + */ + public function delete(string $theme): bool + { + try { + if (in_array($theme, self::SYSTEM_THEMES)) { + throw new Exception('System theme cannot be deleted'); + } + + if ($theme === admin_setting('current_theme')) { + throw new Exception('Current theme cannot be deleted'); + } + + $themePath = base_path(self::USER_THEME_DIR . $theme); + if (!File::exists($themePath)) { + throw new Exception('Theme not found'); + } + + $this->cleanupThemeFiles($theme); + File::deleteDirectory($themePath); + admin_setting([self::SETTING_PREFIX . $theme => null]); + return true; + + } catch (Exception $e) { + Log::error('Theme deletion failed', ['theme' => $theme, 'error' => $e->getMessage()]); + throw $e; + } + } + + /** + * Check if theme exists + */ + public function exists(string $theme): bool + { + return $this->getThemePath($theme) !== null; + } + + /** + * Get theme path + */ + public function getThemePath(string $theme): ?string + { + $systemPath = base_path(self::SYSTEM_THEME_DIR . $theme); + if (File::exists($systemPath)) { + return $systemPath; + } + + $userPath = base_path(self::USER_THEME_DIR . $theme); + if (File::exists($userPath)) { + return $userPath; + } + + return null; + } + + /** + * Get theme config + */ + public function getConfig(string $theme): ?array + { + $config = admin_setting(self::SETTING_PREFIX . $theme); + if ($config === null) { + $this->initConfig($theme); + $config = admin_setting(self::SETTING_PREFIX . $theme); + } + return $config; + } + + /** + * Update theme config + */ + public function updateConfig(string $theme, array $config): bool + { + try { + if (!$this->getThemePath($theme)) { + throw new Exception('Theme not found'); + } + + $schema = $this->readConfigFile($theme); + if (!$schema) { + throw new Exception('Invalid theme config file'); + } + + $validFields = collect($schema['configs'] ?? [])->pluck('field_name')->toArray(); + $validConfig = collect($config) + ->only($validFields) + ->toArray(); + + $currentConfig = $this->getConfig($theme) ?? []; + $newConfig = array_merge($currentConfig, $validConfig); + + admin_setting([self::SETTING_PREFIX . $theme => $newConfig]); + return true; + + } catch (Exception $e) { + Log::error('Config update failed', ['theme' => $theme, 'error' => $e->getMessage()]); + throw $e; + } + } + + /** + * Read theme config file + */ + private function readConfigFile(string $theme): ?array + { + $themePath = $this->getThemePath($theme); + if (!$themePath) { + return null; + } + + $file = $themePath . '/' . self::CONFIG_FILE; + return File::exists($file) ? json_decode(File::get($file), true) : null; + } + + /** + * Clean up theme files including public directory + */ + public function cleanupThemeFiles(string $theme): void + { + try { + $publicThemePath = public_path('theme/' . $theme); + if (File::exists($publicThemePath)) { + File::deleteDirectory($publicThemePath); + Log::info('Cleaned up public theme files', ['theme' => $theme, 'path' => $publicThemePath]); + } + + $cacheKey = "theme_{$theme}_assets"; + if (cache()->has($cacheKey)) { + cache()->forget($cacheKey); + Log::info('Cleaned up theme cache', ['theme' => $theme, 'cache_key' => $cacheKey]); + } + + } catch (Exception $e) { + Log::warning('Failed to cleanup theme files', [ + 'theme' => $theme, + 'error' => $e->getMessage() + ]); + } + } + + /** + * Force refresh current theme public files + */ + public function refreshCurrentTheme(): bool + { + try { + $currentTheme = admin_setting('current_theme'); + if (!$currentTheme) { + return false; + } + + $this->cleanupThemeFiles($currentTheme); + + $themePath = $this->getThemePath($currentTheme); + if (!$themePath) { + throw new Exception('Current theme path not found'); + } + + $targetPath = public_path('theme/' . $currentTheme); + if (!File::copyDirectory($themePath, $targetPath)) { + throw new Exception('Failed to copy theme files'); + } + + Log::info('Refreshed current theme files', ['theme' => $currentTheme]); + return true; + + } catch (Exception $e) { + Log::error('Failed to refresh current theme', [ + 'theme' => $currentTheme, + 'error' => $e->getMessage() + ]); + return false; + } + } + + /** + * Initialize theme config + * + * @param string $theme 主题名称 + * @param bool $preserveExisting 是否保留现有配置(更新主题时使用) + */ + private function initConfig(string $theme, bool $preserveExisting = false): void + { + $config = $this->readConfigFile($theme); + if (!$config) { + return; + } + + $defaults = collect($config['configs'] ?? []) + ->mapWithKeys(fn($col) => [$col['field_name'] => $col['default_value'] ?? '']) + ->toArray(); + + if ($preserveExisting) { + $existingConfig = admin_setting(self::SETTING_PREFIX . $theme) ?? []; + $mergedConfig = array_merge($defaults, $existingConfig); + admin_setting([self::SETTING_PREFIX . $theme => $mergedConfig]); + } else { + admin_setting([self::SETTING_PREFIX . $theme => $defaults]); + } + } +} diff --git a/Xboard/app/Services/TicketService.php b/Xboard/app/Services/TicketService.php new file mode 100644 index 0000000..f99d7c7 --- /dev/null +++ b/Xboard/app/Services/TicketService.php @@ -0,0 +1,125 @@ + $userId, + 'ticket_id' => $ticket->id, + 'message' => $message + ]); + if ($userId !== $ticket->user_id) { + $ticket->reply_status = Ticket::STATUS_OPENING; + } else { + $ticket->reply_status = Ticket::STATUS_CLOSED; + } + if (!$ticketMessage || !$ticket->save()) { + throw new \Exception(); + } + DB::commit(); + return $ticketMessage; + } catch (\Exception $e) { + DB::rollback(); + return false; + } + } + + public function replyByAdmin($ticketId, $message, $userId): void + { + $ticket = Ticket::where('id', $ticketId) + ->first(); + if (!$ticket) { + throw new ApiException('工单不存在'); + } + $ticket->status = Ticket::STATUS_OPENING; + try { + DB::beginTransaction(); + $ticketMessage = TicketMessage::create([ + 'user_id' => $userId, + 'ticket_id' => $ticket->id, + 'message' => $message + ]); + if ($userId !== $ticket->user_id) { + $ticket->reply_status = Ticket::STATUS_OPENING; + } else { + $ticket->reply_status = Ticket::STATUS_CLOSED; + } + if (!$ticketMessage || !$ticket->save()) { + throw new ApiException('工单回复失败'); + } + DB::commit(); + HookManager::call('ticket.reply.admin.after', [$ticket, $ticketMessage]); + } catch (\Exception $e) { + DB::rollBack(); + throw $e; + } + $this->sendEmailNotify($ticket, $ticketMessage); + } + + public function createTicket($userId, $subject, $level, $message) + { + try { + DB::beginTransaction(); + if (Ticket::where('status', 0)->where('user_id', $userId)->lockForUpdate()->first()) { + DB::rollBack(); + throw new ApiException('存在未关闭的工单'); + } + $ticket = Ticket::create([ + 'user_id' => $userId, + 'subject' => $subject, + 'level' => $level + ]); + if (!$ticket) { + throw new ApiException('工单创建失败'); + } + $ticketMessage = TicketMessage::create([ + 'user_id' => $userId, + 'ticket_id' => $ticket->id, + 'message' => $message + ]); + if (!$ticketMessage) { + DB::rollBack(); + throw new ApiException('工单消息创建失败'); + } + DB::commit(); + return $ticket; + } catch (\Exception $e) { + DB::rollBack(); + throw $e; + } + } + + // 半小时内不再重复通知 + private function sendEmailNotify(Ticket $ticket, TicketMessage $ticketMessage) + { + $user = User::find($ticket->user_id); + $cacheKey = 'ticket_sendEmailNotify_' . $ticket->user_id; + if (!Cache::get($cacheKey)) { + Cache::put($cacheKey, 1, 1800); + SendEmailJob::dispatch([ + 'email' => $user->email, + 'subject' => '您在' . admin_setting('app_name', 'XBoard') . '的工单得到了回复', + 'template_name' => 'notify', + 'template_value' => [ + 'name' => admin_setting('app_name', 'XBoard'), + 'url' => admin_setting('app_url'), + 'content' => "主题:{$ticket->subject}\r\n回复内容:{$ticketMessage->message}" + ] + ]); + } + } +} diff --git a/Xboard/app/Services/TrafficResetService.php b/Xboard/app/Services/TrafficResetService.php new file mode 100644 index 0000000..256fd69 --- /dev/null +++ b/Xboard/app/Services/TrafficResetService.php @@ -0,0 +1,415 @@ +shouldResetTraffic()) { + return false; + } + + return $this->performReset($user, $triggerSource); + } + + /** + * Perform the traffic reset for a user. + */ + public function performReset(User $user, string $triggerSource = TrafficResetLog::SOURCE_MANUAL): bool + { + try { + return DB::transaction(function () use ($user, $triggerSource) { + $oldUpload = $user->u ?? 0; + $oldDownload = $user->d ?? 0; + $oldTotal = $oldUpload + $oldDownload; + + $nextResetTime = $this->calculateNextResetTime($user); + + $user->update([ + 'u' => 0, + 'd' => 0, + 'last_reset_at' => time(), + 'reset_count' => $user->reset_count + 1, + 'next_reset_at' => $nextResetTime ? $nextResetTime->timestamp : null, + ]); + + $this->recordResetLog($user, [ + 'reset_type' => $this->getResetTypeFromPlan($user->plan), + 'trigger_source' => $triggerSource, + 'old_upload' => $oldUpload, + 'old_download' => $oldDownload, + 'old_total' => $oldTotal, + 'new_upload' => 0, + 'new_download' => 0, + 'new_total' => 0, + ]); + + $this->clearUserCache($user); + HookManager::call('traffic.reset.after', $user); + return true; + }); + } catch (\Exception $e) { + Log::error(__('traffic_reset.reset_failed'), [ + 'user_id' => $user->id, + 'email' => $user->email, + 'error' => $e->getMessage(), + 'trigger_source' => $triggerSource, + ]); + + return false; + } + } + + /** + * Calculate the next traffic reset time for a user. + */ + public function calculateNextResetTime(User $user): ?Carbon + { + if ( + !$user->plan + || $user->plan->reset_traffic_method === Plan::RESET_TRAFFIC_NEVER + || ($user->plan->reset_traffic_method === Plan::RESET_TRAFFIC_FOLLOW_SYSTEM + && (int) admin_setting('reset_traffic_method', Plan::RESET_TRAFFIC_MONTHLY) === Plan::RESET_TRAFFIC_NEVER) + || $user->expired_at === NULL + ) { + return null; + } + + $resetMethod = $user->plan->reset_traffic_method; + + if ($resetMethod === Plan::RESET_TRAFFIC_FOLLOW_SYSTEM) { + $resetMethod = (int) admin_setting('reset_traffic_method', Plan::RESET_TRAFFIC_MONTHLY); + } + + $now = Carbon::now(config('app.timezone')); + + return match ($resetMethod) { + Plan::RESET_TRAFFIC_FIRST_DAY_MONTH => $this->getNextMonthFirstDay($now), + Plan::RESET_TRAFFIC_MONTHLY => $this->getNextMonthlyReset($user, $now), + Plan::RESET_TRAFFIC_FIRST_DAY_YEAR => $this->getNextYearFirstDay($now), + Plan::RESET_TRAFFIC_YEARLY => $this->getNextYearlyReset($user, $now), + default => null, + }; + } + + /** + * Get the first day of the next month. + */ + private function getNextMonthFirstDay(Carbon $from): Carbon + { + return $from->copy()->addMonth()->startOfMonth(); + } + + /** + * Get the next monthly reset time based on the user's expiration date. + * + * Logic: + * 1. If the user has no expiration date, reset on the 1st of each month. + * 2. If the user has an expiration date, use the day of that date as the monthly reset day. + * 3. Prioritize the reset day in the current month if it has not passed yet. + * 4. Handle cases where the day does not exist in a month (e.g., 31st in February). + */ + private function getNextMonthlyReset(User $user, Carbon $from): Carbon + { + $expiredAt = Carbon::createFromTimestamp($user->expired_at, config('app.timezone')); + $resetDay = $expiredAt->day; + $resetTime = [$expiredAt->hour, $expiredAt->minute, $expiredAt->second]; + + $currentMonthTarget = $from->copy()->day($resetDay)->setTime(...$resetTime); + if ($currentMonthTarget->timestamp > $from->timestamp) { + return $currentMonthTarget; + } + + $nextMonthTarget = $from->copy()->startOfMonth()->addMonths(1)->day($resetDay)->setTime(...$resetTime); + + if ($nextMonthTarget->month !== ($from->month % 12) + 1) { + $nextMonth = ($from->month % 12) + 1; + $nextYear = $from->year + ($from->month === 12 ? 1 : 0); + $lastDayOfNextMonth = Carbon::create($nextYear, $nextMonth, 1)->endOfMonth()->day; + $targetDay = min($resetDay, $lastDayOfNextMonth); + $nextMonthTarget = Carbon::create($nextYear, $nextMonth, $targetDay)->setTime(...$resetTime); + } + + return $nextMonthTarget; + } + + /** + * Get the first day of the next year. + */ + private function getNextYearFirstDay(Carbon $from): Carbon + { + return $from->copy()->addYear()->startOfYear(); + } + + /** + * Get the next yearly reset time based on the user's expiration date. + * + * Logic: + * 1. If the user has no expiration date, reset on January 1st of each year. + * 2. If the user has an expiration date, use the month and day of that date as the yearly reset date. + * 3. Prioritize the reset date in the current year if it has not passed yet. + * 4. Handle the case of February 29th in a leap year. + */ + private function getNextYearlyReset(User $user, Carbon $from): Carbon + { + $expiredAt = Carbon::createFromTimestamp($user->expired_at, config('app.timezone')); + $resetMonth = $expiredAt->month; + $resetDay = $expiredAt->day; + $resetTime = [$expiredAt->hour, $expiredAt->minute, $expiredAt->second]; + + $currentYearTarget = $from->copy()->month($resetMonth)->day($resetDay)->setTime(...$resetTime); + if ($currentYearTarget->timestamp > $from->timestamp) { + return $currentYearTarget; + } + + $nextYearTarget = $from->copy()->startOfYear()->addYears(1)->month($resetMonth)->day($resetDay)->setTime(...$resetTime); + + if ($nextYearTarget->month !== $resetMonth) { + $nextYear = $from->year + 1; + $lastDayOfMonth = Carbon::create($nextYear, $resetMonth, 1)->endOfMonth()->day; + $targetDay = min($resetDay, $lastDayOfMonth); + $nextYearTarget = Carbon::create($nextYear, $resetMonth, $targetDay)->setTime(...$resetTime); + } + + return $nextYearTarget; + } + + + /** + * Record the traffic reset log. + */ + private function recordResetLog(User $user, array $data): void + { + TrafficResetLog::create([ + 'user_id' => $user->id, + 'reset_type' => $data['reset_type'], + 'reset_time' => now(), + 'old_upload' => $data['old_upload'], + 'old_download' => $data['old_download'], + 'old_total' => $data['old_total'], + 'new_upload' => $data['new_upload'], + 'new_download' => $data['new_download'], + 'new_total' => $data['new_total'], + 'trigger_source' => $data['trigger_source'], + 'metadata' => $data['metadata'] ?? null, + ]); + } + + /** + * Get the reset type from the user's plan. + */ + private function getResetTypeFromPlan(?Plan $plan): string + { + if (!$plan) { + return TrafficResetLog::TYPE_MANUAL; + } + + $resetMethod = $plan->reset_traffic_method; + + if ($resetMethod === Plan::RESET_TRAFFIC_FOLLOW_SYSTEM) { + $resetMethod = (int) admin_setting('reset_traffic_method', Plan::RESET_TRAFFIC_MONTHLY); + } + + return match ($resetMethod) { + Plan::RESET_TRAFFIC_FIRST_DAY_MONTH => TrafficResetLog::TYPE_FIRST_DAY_MONTH, + Plan::RESET_TRAFFIC_MONTHLY => TrafficResetLog::TYPE_MONTHLY, + Plan::RESET_TRAFFIC_FIRST_DAY_YEAR => TrafficResetLog::TYPE_FIRST_DAY_YEAR, + Plan::RESET_TRAFFIC_YEARLY => TrafficResetLog::TYPE_YEARLY, + Plan::RESET_TRAFFIC_NEVER => TrafficResetLog::TYPE_MANUAL, + default => TrafficResetLog::TYPE_MANUAL, + }; + } + + /** + * Clear user-related cache. + */ + private function clearUserCache(User $user): void + { + $cacheKeys = [ + "user_traffic_{$user->id}", + "user_reset_status_{$user->id}", + "user_subscription_{$user->token}", + ]; + + foreach ($cacheKeys as $key) { + Cache::forget($key); + } + } + + /** + * Batch check and reset users. Processes all eligible users in batches. + */ + public function batchCheckReset(int $batchSize = 100, ?callable $progressCallback = null): array + { + $startTime = microtime(true); + $totalResetCount = 0; + $totalProcessedCount = 0; + $batchNumber = 1; + $errors = []; + $lastProcessedId = 0; + + try { + do { + $users = User::where('next_reset_at', '<=', time()) + ->whereNotNull('next_reset_at') + ->where('id', '>', $lastProcessedId) + ->where(function ($query) { + $query->where('expired_at', '>', time()) + ->orWhereNull('expired_at'); + }) + ->where('banned', 0) + ->whereNotNull('plan_id') + ->orderBy('id') + ->limit($batchSize) + ->get(); + + if ($users->isEmpty()) { + break; + } + + $batchResetCount = 0; + + if ($progressCallback) { + $progressCallback([ + 'batch_number' => $batchNumber, + 'batch_size' => $users->count(), + 'total_processed' => $totalProcessedCount, + ]); + } + + foreach ($users as $user) { + try { + if ($this->checkAndReset($user, TrafficResetLog::SOURCE_CRON)) { + $batchResetCount++; + $totalResetCount++; + } + $totalProcessedCount++; + $lastProcessedId = $user->id; + } catch (\Exception $e) { + $error = [ + 'user_id' => $user->id, + 'email' => $user->email, + 'error' => $e->getMessage(), + 'batch' => $batchNumber, + 'timestamp' => now()->toDateTimeString(), + ]; + $batchErrors[] = $error; + $errors[] = $error; + + Log::error('User traffic reset failed', $error); + + $totalProcessedCount++; + $lastProcessedId = $user->id; + } + } + + $batchNumber++; + + if ($batchNumber % 10 === 0) { + gc_collect_cycles(); + } + + if ($batchNumber % 5 === 0) { + usleep(100000); + } + + } while (true); + + } catch (\Exception $e) { + Log::error('Batch traffic reset task failed with an exception', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + 'total_processed' => $totalProcessedCount, + 'total_reset' => $totalResetCount, + 'last_processed_id' => $lastProcessedId, + ]); + + $errors[] = [ + 'type' => 'system_error', + 'error' => $e->getMessage(), + 'batch' => $batchNumber, + 'last_processed_id' => $lastProcessedId, + 'timestamp' => now()->toDateTimeString(), + ]; + } + + $totalDuration = round(microtime(true) - $startTime, 2); + + $result = [ + 'total_processed' => $totalProcessedCount, + 'total_reset' => $totalResetCount, + 'total_batches' => $batchNumber - 1, + 'error_count' => count($errors), + 'errors' => $errors, + 'duration' => $totalDuration, + 'batch_size' => $batchSize, + 'last_processed_id' => $lastProcessedId, + 'completed_at' => now()->toDateTimeString(), + ]; + + return $result; + } + + /** + * Set the initial reset time for a new user. + */ + public function setInitialResetTime(User $user): void + { + if ($user->next_reset_at !== null) { + return; + } + + $nextResetTime = $this->calculateNextResetTime($user); + + if ($nextResetTime) { + $user->update(['next_reset_at' => $nextResetTime->timestamp]); + } + } + + /** + * Get the user's traffic reset history. + */ + public function getUserResetHistory(User $user, int $limit = 10): \Illuminate\Database\Eloquent\Collection + { + return $user->trafficResetLogs() + ->orderBy('reset_time', 'desc') + ->limit($limit) + ->get(); + } + + /** + * Check if the user is eligible for traffic reset. + */ + public function canReset(User $user): bool + { + return $user->isActive() && $user->plan !== null; + } + + /** + * Manually reset a user's traffic (Admin function). + */ + public function manualReset(User $user, array $metadata = []): bool + { + if (!$this->canReset($user)) { + return false; + } + + return $this->performReset($user, TrafficResetLog::SOURCE_MANUAL); + } +} \ No newline at end of file diff --git a/Xboard/app/Services/UpdateService.php b/Xboard/app/Services/UpdateService.php new file mode 100644 index 0000000..de063cd --- /dev/null +++ b/Xboard/app/Services/UpdateService.php @@ -0,0 +1,458 @@ +getCurrentCommit(); + }); + return $date . '-' . $hash; + } + + /** + * Update version cache + */ + public function updateVersionCache(): void + { + try { + $result = Process::run('git log -1 --format=%cd:%H --date=format:%Y%m%d'); + if ($result->successful()) { + list($date, $hash) = explode(':', trim($result->output())); + Cache::forever(self::CACHE_VERSION_DATE, $date); + Cache::forever(self::CACHE_VERSION, substr($hash, 0, 7)); + // Log::info('Version cache updated: ' . $date . '-' . substr($hash, 0, 7)); + return; + } + } catch (\Exception $e) { + Log::error('Failed to get version with date: ' . $e->getMessage()); + } + + // Fallback + Cache::forever(self::CACHE_VERSION_DATE, date('Ymd')); + $fallbackHash = $this->getCurrentCommit(); + Cache::forever(self::CACHE_VERSION, $fallbackHash); + Log::info('Version cache updated (fallback): ' . date('Ymd') . '-' . $fallbackHash); + } + + public function checkForUpdates(): array + { + try { + // Get current version commit + $currentCommit = $this->getCurrentCommit(); + if ($currentCommit === 'unknown') { + // If unable to get current commit, try to get the first commit + $currentCommit = $this->getFirstCommit(); + } + // Get local git logs + $localLogs = $this->getLocalGitLogs(); + if (empty($localLogs)) { + Log::error('Failed to get local git logs'); + return $this->getCachedUpdateInfo(); + } + + // Get remote latest commits + $response = Http::withHeaders([ + 'Accept' => 'application/vnd.github.v3+json', + 'User-Agent' => 'XBoard-Update-Checker' + ])->get(self::GITHUB_API_URL . '?per_page=50'); + + if ($response->successful()) { + $commits = $response->json(); + + if (empty($commits) || !is_array($commits)) { + Log::error('Invalid GitHub response format'); + return $this->getCachedUpdateInfo(); + } + + $latestCommit = $this->formatCommitHash($commits[0]['sha']); + $currentIndex = -1; + $updateLogs = []; + + // First, find the current version position in remote commit history + foreach ($commits as $index => $commit) { + $shortSha = $this->formatCommitHash($commit['sha']); + if ($shortSha === $currentCommit) { + $currentIndex = $index; + break; + } + } + + // Check local version status + $isLocalNewer = false; + if ($currentIndex === -1) { + // Current version not found in remote history, check local commits + foreach ($localLogs as $localCommit) { + $localHash = $this->formatCommitHash($localCommit['hash']); + // If latest remote commit found, local is not newer + if ($localHash === $latestCommit) { + $isLocalNewer = false; + break; + } + // Record additional local commits + $updateLogs[] = [ + 'version' => $localHash, + 'message' => $localCommit['message'], + 'author' => $localCommit['author'], + 'date' => $localCommit['date'], + 'is_local' => true + ]; + $isLocalNewer = true; + } + } + + // If local is not newer, collect commits that need to be updated + if (!$isLocalNewer && $currentIndex > 0) { + $updateLogs = []; + // Collect all commits between current version and latest version + for ($i = 0; $i < $currentIndex; $i++) { + $commit = $commits[$i]; + $updateLogs[] = [ + 'version' => $this->formatCommitHash($commit['sha']), + 'message' => $commit['commit']['message'], + 'author' => $commit['commit']['author']['name'], + 'date' => $commit['commit']['author']['date'], + 'is_local' => false + ]; + } + } + + $hasUpdate = !$isLocalNewer && $currentIndex > 0; + + $updateInfo = [ + 'has_update' => $hasUpdate, + 'is_local_newer' => $isLocalNewer, + 'latest_version' => $isLocalNewer ? $currentCommit : $latestCommit, + 'current_version' => $currentCommit, + 'update_logs' => $updateLogs, + 'download_url' => $commits[0]['html_url'] ?? '', + 'published_at' => $commits[0]['commit']['author']['date'] ?? '', + 'author' => $commits[0]['commit']['author']['name'] ?? '', + ]; + + // Cache check results + $this->setLastCheckTime(); + Cache::put(self::CACHE_UPDATE_INFO, $updateInfo, now()->addHours(24)); + + return $updateInfo; + } + + return $this->getCachedUpdateInfo(); + } catch (\Exception $e) { + Log::error('Update check failed: ' . $e->getMessage()); + return $this->getCachedUpdateInfo(); + } + } + + public function executeUpdate(): array + { + // Check for new version first + $updateInfo = $this->checkForUpdates(); + if ($updateInfo['is_local_newer']) { + return [ + 'success' => false, + 'message' => __('update.local_newer') + ]; + } + if (!$updateInfo['has_update']) { + return [ + 'success' => false, + 'message' => __('update.already_latest') + ]; + } + + // Check for update lock + if (Cache::get(self::CACHE_UPDATE_LOCK)) { + return [ + 'success' => false, + 'message' => __('update.process_running') + ]; + } + + try { + // Set update lock + Cache::put(self::CACHE_UPDATE_LOCK, true, now()->addMinutes(30)); + + // 1. Backup database + $this->backupDatabase(); + + // 2. Pull latest code + $result = $this->pullLatestCode(); + if (!$result['success']) { + throw new \Exception($result['message']); + } + + // 3. Run database migrations + $this->runMigrations(); + + // 4. Clear cache + $this->clearCache(); + + // 5. Create update flag + $this->createUpdateFlag(); + + // 6. Restart Octane if running + $this->restartOctane(); + + // Remove update lock + Cache::forget(self::CACHE_UPDATE_LOCK); + + // Format update logs + $logMessages = array_map(function($log) { + return sprintf("- %s (%s): %s", + $log['version'], + date('Y-m-d H:i', strtotime($log['date'])), + $log['message'] + ); + }, $updateInfo['update_logs']); + + return [ + 'success' => true, + 'message' => __('update.success', [ + 'from' => $updateInfo['current_version'], + 'to' => $updateInfo['latest_version'] + ]), + 'version' => $updateInfo['latest_version'], + 'update_info' => [ + 'from_version' => $updateInfo['current_version'], + 'to_version' => $updateInfo['latest_version'], + 'update_logs' => $logMessages, + 'author' => $updateInfo['author'], + 'published_at' => $updateInfo['published_at'] + ] + ]; + + } catch (\Exception $e) { + Log::error('Update execution failed: ' . $e->getMessage()); + Cache::forget(self::CACHE_UPDATE_LOCK); + + return [ + 'success' => false, + 'message' => __('update.failed', ['error' => $e->getMessage()]) + ]; + } + } + + protected function getCurrentCommit(): string + { + try { + // Ensure git configuration is correct + Process::run(sprintf('git config --global --add safe.directory %s', base_path())); + $result = Process::run('git rev-parse HEAD'); + $fullHash = trim($result->output()); + return $fullHash ? $this->formatCommitHash($fullHash) : 'unknown'; + } catch (\Exception $e) { + Log::error('Failed to get current commit: ' . $e->getMessage()); + return 'unknown'; + } + } + + protected function getFirstCommit(): string + { + try { + // Get first commit hash + $result = Process::run('git rev-list --max-parents=0 HEAD'); + $fullHash = trim($result->output()); + return $fullHash ? $this->formatCommitHash($fullHash) : 'unknown'; + } catch (\Exception $e) { + Log::error('Failed to get first commit: ' . $e->getMessage()); + return 'unknown'; + } + } + + protected function formatCommitHash(string $hash): string + { + // Use 7 characters for commit hash + return substr($hash, 0, 7); + } + + protected function backupDatabase(): void + { + try { + // Use existing backup command + Process::run('php artisan backup:database'); + + if (!Process::result()->successful()) { + throw new \Exception(__('update.backup_failed', ['error' => Process::result()->errorOutput()])); + } + } catch (\Exception $e) { + Log::error('Database backup failed: ' . $e->getMessage()); + throw $e; + } + } + + protected function pullLatestCode(): array + { + try { + // Get current project root directory + $basePath = base_path(); + + // Ensure git configuration is correct + Process::run(sprintf('git config --global --add safe.directory %s', $basePath)); + + // Pull latest code + Process::run('git fetch origin master'); + Process::run('git reset --hard origin/master'); + + // Update dependencies + Process::run('composer install --no-dev --optimize-autoloader'); + + // Update version cache after pulling new code + $this->updateVersionCache(); + + return ['success' => true]; + } catch (\Exception $e) { + return [ + 'success' => false, + 'message' => __('update.code_update_failed', ['error' => $e->getMessage()]) + ]; + } + } + + protected function runMigrations(): void + { + try { + Process::run('php artisan migrate --force'); + } catch (\Exception $e) { + Log::error('Migration failed: ' . $e->getMessage()); + throw new \Exception(__('update.migration_failed', ['error' => $e->getMessage()])); + } + } + + protected function clearCache(): void + { + try { + $commands = [ + 'php artisan config:clear', + 'php artisan cache:clear', + 'php artisan view:clear', + 'php artisan route:clear' + ]; + + foreach ($commands as $command) { + Process::run($command); + } + } catch (\Exception $e) { + Log::error('Cache clearing failed: ' . $e->getMessage()); + throw new \Exception(__('update.cache_clear_failed', ['error' => $e->getMessage()])); + } + } + + protected function createUpdateFlag(): void + { + try { + // Create update flag file for external script to detect and restart container + $flagFile = storage_path('update_pending'); + File::put($flagFile, date('Y-m-d H:i:s')); + } catch (\Exception $e) { + Log::error('Failed to create update flag: ' . $e->getMessage()); + throw new \Exception(__('update.flag_create_failed', ['error' => $e->getMessage()])); + } + } + + protected function restartOctane(): void + { + try { + if (!config('octane.server')) { + return; + } + + // Check Octane running status + $statusResult = Process::run('php artisan octane:status'); + if (!$statusResult->successful()) { + Log::info('Octane is not running, skipping restart.'); + return; + } + + $output = $statusResult->output(); + if (str_contains($output, 'Octane server is running')) { + Log::info('Restarting Octane server after update...'); + // Update version cache before restart + $this->updateVersionCache(); + Process::run('php artisan octane:stop'); + Log::info('Octane server restarted successfully.'); + } else { + Log::info('Octane is not running, skipping restart.'); + } + } catch (\Exception $e) { + Log::error('Failed to restart Octane server: ' . $e->getMessage()); + // Non-fatal error, don't throw exception + } + } + + public function getLastCheckTime() + { + return Cache::get(self::CACHE_LAST_CHECK, null); + } + + protected function setLastCheckTime(): void + { + Cache::put(self::CACHE_LAST_CHECK, now()->timestamp, now()->addDays(30)); + } + + public function getCachedUpdateInfo(): array + { + return Cache::get(self::CACHE_UPDATE_INFO, [ + 'has_update' => false, + 'latest_version' => $this->getCurrentCommit(), + 'current_version' => $this->getCurrentCommit(), + 'update_logs' => [], + 'download_url' => '', + 'published_at' => '', + 'author' => '', + ]); + } + + protected function getLocalGitLogs(int $limit = 50): array + { + try { + // 获取本地git log + $result = Process::run( + sprintf('git log -%d --pretty=format:"%%H||%%s||%%an||%%ai"', $limit) + ); + + if (!$result->successful()) { + return []; + } + + $logs = []; + $lines = explode("\n", trim($result->output())); + foreach ($lines as $line) { + $parts = explode('||', $line); + if (count($parts) === 4) { + $logs[] = [ + 'hash' => $parts[0], + 'message' => $parts[1], + 'author' => $parts[2], + 'date' => $parts[3] + ]; + } + } + return $logs; + } catch (\Exception $e) { + Log::error('Failed to get local git logs: ' . $e->getMessage()); + return []; + } + } +} \ No newline at end of file diff --git a/Xboard/app/Services/UserService.php b/Xboard/app/Services/UserService.php new file mode 100644 index 0000000..c068b67 --- /dev/null +++ b/Xboard/app/Services/UserService.php @@ -0,0 +1,288 @@ +calculateNextResetTime($user); + + if (!$nextResetTime) { + return null; + } + + // Calculate the remaining days from now to the next reset time + $now = time(); + $resetTimestamp = $nextResetTime->timestamp; + + if ($resetTimestamp <= $now) { + return 0; // Reset time has passed or is now + } + + // Calculate the difference in days (rounded up) + $daysDifference = ceil(($resetTimestamp - $now) / 86400); + + return (int) $daysDifference; + } + + public function isAvailable(User $user) + { + if (!$user->banned && $user->transfer_enable && ($user->expired_at > time() || $user->expired_at === NULL)) { + return true; + } + return false; + } + + public function getAvailableUsers() + { + return User::whereRaw('u + d < transfer_enable') + ->where(function ($query) { + $query->where('expired_at', '>=', time()) + ->orWhere('expired_at', NULL); + }) + ->where('banned', 0) + ->get(); + } + + public function getUnAvailbaleUsers() + { + return User::where(function ($query) { + $query->where('expired_at', '<', time()) + ->orWhere('expired_at', 0); + }) + ->where(function ($query) { + $query->where('plan_id', NULL) + ->orWhere('transfer_enable', 0); + }) + ->get(); + } + + public function getUsersByIds($ids) + { + return User::whereIn('id', $ids)->get(); + } + + public function getAllUsers() + { + return User::all(); + } + + public function addBalance(int $userId, int $balance): bool + { + $user = User::lockForUpdate()->find($userId); + if (!$user) { + return false; + } + $user->balance = $user->balance + $balance; + if ($user->balance < 0) { + return false; + } + if (!$user->save()) { + return false; + } + return true; + } + + public function isNotCompleteOrderByUserId(int $userId): bool + { + $order = Order::whereIn('status', [0, 1]) + ->where('user_id', $userId) + ->first(); + if (!$order) { + return false; + } + return true; + } + + public function trafficFetch(Server $server, string $protocol, array $data) + { + $server->rate = $server->getCurrentRate(); + $server = $server->toArray(); + + list($server, $protocol, $data) = HookManager::filter('traffic.process.before', [$server, $protocol, $data]); + // Compatible with legacy hook + list($server, $protocol, $data) = HookManager::filter('traffic.before_process', [$server, $protocol, $data]); + + $timestamp = strtotime(date('Y-m-d')); + collect($data)->chunk(1000)->each(function ($chunk) use ($timestamp, $server, $protocol) { + TrafficFetchJob::dispatch($server, $chunk->toArray(), $protocol, $timestamp); + StatUserJob::dispatch($server, $chunk->toArray(), $protocol, 'd'); + StatServerJob::dispatch($server, $chunk->toArray(), $protocol, 'd'); + }); + } + + /** + * 获取用户流量信息(增加重置检查) + */ + public function getUserTrafficInfo(User $user): array + { + // 检查是否需要重置流量 + app(TrafficResetService::class)->checkAndReset($user, TrafficResetLog::SOURCE_USER_ACCESS); + + // 重新获取用户数据(可能已被重置) + $user->refresh(); + + return [ + 'upload' => $user->u ?? 0, + 'download' => $user->d ?? 0, + 'total_used' => $user->getTotalUsedTraffic(), + 'total_available' => $user->transfer_enable ?? 0, + 'remaining' => $user->getRemainingTraffic(), + 'usage_percentage' => $user->getTrafficUsagePercentage(), + 'next_reset_at' => $user->next_reset_at, + 'last_reset_at' => $user->last_reset_at, + 'reset_count' => $user->reset_count, + ]; + } + + /** + * 创建用户 + */ + public function createUser(array $data): User + { + $user = new User(); + + // 基本信息 + $user->email = $data['email']; + $user->password = isset($data['password']) + ? Hash::make($data['password']) + : Hash::make($data['email']); + $user->uuid = Helper::guid(true); + $user->token = Helper::guid(); + + // 默认设置 + $user->remind_expire = admin_setting('default_remind_expire', 1); + $user->remind_traffic = admin_setting('default_remind_traffic', 1); + $user->expired_at = null; + + // 可选字段 + $this->setOptionalFields($user, $data); + + // 处理计划 + if (isset($data['plan_id'])) { + $this->setPlanForUser($user, $data['plan_id'], $data['expired_at'] ?? null); + } else { + $this->setTryOutPlan(user: $user); + } + + return $user; + } + + /** + * 设置可选字段 + */ + private function setOptionalFields(User $user, array $data): void + { + $optionalFields = [ + 'invite_user_id', + 'telegram_id', + 'group_id', + 'speed_limit', + 'expired_at', + 'transfer_enable' + ]; + + foreach ($optionalFields as $field) { + if (array_key_exists($field, $data)) { + $user->{$field} = $data[$field]; + } + } + } + + /** + * 为用户设置计划 + */ + private function setPlanForUser(User $user, int $planId, ?int $expiredAt = null): void + { + $plan = Plan::find($planId); + if (!$plan) + return; + + $user->plan_id = $plan->id; + $user->group_id = $plan->group_id; + $user->transfer_enable = $plan->transfer_enable * 1073741824; + $user->speed_limit = $plan->speed_limit; + + if ($expiredAt) { + $user->expired_at = $expiredAt; + } + } + + /** + * 为用户分配一个新套餐或续费现有套餐 + * + * @param User $user 用户模型 + * @param Plan $plan 套餐模型 + * @param int $validityDays 购买天数 + * @return User 更新后的用户模型 + */ + public function assignPlan(User $user, Plan $plan, int $validityDays): User + { + $user->plan_id = $plan->id; + $user->group_id = $plan->group_id; + $user->transfer_enable = $plan->transfer_enable * 1073741824; + $user->speed_limit = $plan->speed_limit; + $user->device_limit = $plan->device_limit; + + if ($validityDays > 0) { + $user = $this->extendSubscription($user, $validityDays); + } + + $user->save(); + return $user; + } + + /** + * 延长用户的订阅有效期 + * + * @param User $user 用户模型 + * @param int $days 延长天数 + * @return User 更新后的用户模型 + */ + public function extendSubscription(User $user, int $days): User + { + $currentExpired = $user->expired_at ?? time(); + $user->expired_at = max($currentExpired, time()) + ($days * 86400); + + return $user; + } + + /** + * 设置试用计划 + */ + private function setTryOutPlan(User $user): void + { + if (!(int) admin_setting('try_out_plan_id', 0)) + return; + + $plan = Plan::find(admin_setting('try_out_plan_id')); + if (!$plan) + return; + + $user->transfer_enable = $plan->transfer_enable * 1073741824; + $user->plan_id = $plan->id; + $user->group_id = $plan->group_id; + $user->expired_at = time() + (admin_setting('try_out_hour', 1) * 3600); + $user->speed_limit = $plan->speed_limit; + } +} diff --git a/Xboard/app/Support/AbstractProtocol.php b/Xboard/app/Support/AbstractProtocol.php new file mode 100644 index 0000000..78a2c0d --- /dev/null +++ b/Xboard/app/Support/AbstractProtocol.php @@ -0,0 +1,262 @@ +user = $user; + $this->servers = $servers; + $this->clientName = $clientName; + $this->clientVersion = $clientVersion; + $this->userAgent = $userAgent; + $this->protocolRequirements = $this->normalizeProtocolRequirements($this->protocolRequirements); + $this->servers = HookManager::filter('protocol.servers.filtered', $this->filterServersByVersion()); + } + + /** + * 获取协议标识 + * + * @return array + */ + public function getFlags(): array + { + return $this->flags; + } + + /** + * 处理请求 + * + * @return mixed + */ + abstract public function handle(); + + /** + * 根据客户端版本过滤不兼容的服务器 + * + * @return array + */ + protected function filterServersByVersion() + { + $this->filterByAllowedProtocols(); + $hasGlobalConfig = isset($this->protocolRequirements['*']); + $hasClientConfig = isset($this->protocolRequirements[$this->clientName]); + + if ((blank($this->clientName) || blank($this->clientVersion)) && !$hasGlobalConfig) { + return $this->servers; + } + + if (!$hasGlobalConfig && !$hasClientConfig) { + return $this->servers; + } + + return collect($this->servers) + ->filter(fn($server) => $this->isCompatible($server)) + ->values() + ->all(); + } + + /** + * 检查服务器是否与当前客户端兼容 + * + * @param array $server 服务器信息 + * @return bool + */ + protected function isCompatible($server) + { + $serverType = $server['type'] ?? null; + if (isset($this->protocolRequirements['*'][$serverType])) { + $globalRequirements = $this->protocolRequirements['*'][$serverType]; + if (!$this->checkRequirements($globalRequirements, $server)) { + return false; + } + } + + if (!isset($this->protocolRequirements[$this->clientName][$serverType])) { + return true; + } + + $requirements = $this->protocolRequirements[$this->clientName][$serverType]; + return $this->checkRequirements($requirements, $server); + } + + /** + * 检查版本要求 + * + * @param array $requirements 要求配置 + * @param array $server 服务器信息 + * @return bool + */ + private function checkRequirements(array $requirements, array $server): bool + { + foreach ($requirements as $field => $filterRule) { + if (in_array($field, ['base_version', 'incompatible'])) { + continue; + } + + $actualValue = data_get($server, $field); + + if (is_array($filterRule) && isset($filterRule['whitelist'])) { + $allowedValues = $filterRule['whitelist']; + $strict = $filterRule['strict'] ?? false; + if ($strict) { + if ($actualValue === null) { + return false; + } + if (!is_string($actualValue) && !is_int($actualValue)) { + return false; + } + if (!isset($allowedValues[$actualValue])) { + return false; + } + $requiredVersion = $allowedValues[$actualValue]; + if ($requiredVersion !== '0.0.0' && version_compare($this->clientVersion, $requiredVersion, '<')) { + return false; + } + continue; + } + } else { + $allowedValues = $filterRule; + $strict = false; + } + + if ($actualValue === null) { + continue; + } + if (!is_string($actualValue) && !is_int($actualValue)) { + continue; + } + if (!isset($allowedValues[$actualValue])) { + continue; + } + $requiredVersion = $allowedValues[$actualValue]; + if ($requiredVersion !== '0.0.0' && version_compare($this->clientVersion, $requiredVersion, '<')) { + return false; + } + } + + return true; + } + + /** + * 检查当前客户端是否支持特定功能 + * + * @param string $clientName 客户端名称 + * @param string $minVersion 最低版本要求 + * @param array $additionalConditions 额外条件检查 + * @return bool + */ + protected function supportsFeature(string $clientName, string $minVersion, array $additionalConditions = []): bool + { + // 检查客户端名称 + if ($this->clientName !== $clientName) { + return false; + } + + // 检查版本号 + if (empty($this->clientVersion) || version_compare($this->clientVersion, $minVersion, '<')) { + return false; + } + + // 检查额外条件 + foreach ($additionalConditions as $condition) { + if (!$condition) { + return false; + } + } + + return true; + } + + /** + * 根据白名单过滤服务器 + * + * @return void + */ + protected function filterByAllowedProtocols(): void + { + if (!empty($this->allowedProtocols)) { + $this->servers = collect($this->servers) + ->filter(fn($server) => in_array($server['type'], $this->allowedProtocols)) + ->values() + ->all(); + } + } + + /** + * 将平铺的协议需求转换为树形结构 + * + * @param array $flat 平铺的协议需求 + * @return array 树形结构的协议需求 + */ + protected function normalizeProtocolRequirements(array $flat): array + { + $result = []; + foreach ($flat as $key => $value) { + if (!str_contains($key, '.')) { + $result[$key] = $value; + continue; + } + $segments = explode('.', $key, 3); + if (count($segments) < 3) { + $result[$segments[0]][$segments[1] ?? '*'][''] = $value; + continue; + } + [$client, $type, $field] = $segments; + $result[$client][$type][$field] = $value; + } + return $result; + } +} \ No newline at end of file diff --git a/Xboard/app/Support/ProtocolManager.php b/Xboard/app/Support/ProtocolManager.php new file mode 100644 index 0000000..e1fb4cd --- /dev/null +++ b/Xboard/app/Support/ProtocolManager.php @@ -0,0 +1,162 @@ +container = $container; + } + + /** + * 发现并注册所有协议类 + * + * @return self + */ + public function registerAllProtocols() + { + if (empty($this->protocolClasses)) { + $files = glob(app_path('Protocols') . '/*.php'); + + foreach ($files as $file) { + $className = 'App\\Protocols\\' . basename($file, '.php'); + + if (class_exists($className) && is_subclass_of($className, AbstractProtocol::class)) { + $this->protocolClasses[] = $className; + } + } + } + + return $this; + } + + /** + * 获取所有注册的协议类 + * + * @return array + */ + public function getProtocolClasses() + { + if (empty($this->protocolClasses)) { + $this->registerAllProtocols(); + } + + return $this->protocolClasses; + } + + /** + * 获取所有协议的标识 + * + * @return array + */ + public function getAllFlags() + { + return collect($this->getProtocolClasses()) + ->map(function ($class) { + try { + $reflection = new \ReflectionClass($class); + if (!$reflection->isInstantiable()) { + return []; + } + // 'flags' is a public property with a default value in AbstractProtocol + $instanceForFlags = $reflection->newInstanceWithoutConstructor(); + return $instanceForFlags->flags; + } catch (\ReflectionException $e) { + // Log or handle error if a class is problematic + report($e); + return []; + } + }) + ->flatten() + ->unique() + ->values() + ->all(); + } + + /** + * 根据标识匹配合适的协议处理器类名 + * + * @param string $flag 请求标识 + * @return string|null 协议类名或null + */ + public function matchProtocolClassName(string $flag): ?string + { + // 按照相反顺序,使最新定义的协议有更高优先级 + foreach (array_reverse($this->getProtocolClasses()) as $protocolClassString) { + try { + $reflection = new \ReflectionClass($protocolClassString); + + if (!$reflection->isInstantiable() || !$reflection->isSubclassOf(AbstractProtocol::class)) { + continue; + } + + // 'flags' is a public property in AbstractProtocol + $instanceForFlags = $reflection->newInstanceWithoutConstructor(); + $flags = $instanceForFlags->flags; + + if (collect($flags)->contains(fn($f) => stripos($flag, (string) $f) !== false)) { + return $protocolClassString; // 返回类名字符串 + } + } catch (\ReflectionException $e) { + report($e); // Consider logging this error + continue; + } + } + return null; + } + + /** + * 根据标识匹配合适的协议处理器实例 (原有逻辑,如果还需要的话) + * + * @param string $flag 请求标识 + * @param array $user 用户信息 + * @param array $servers 服务器列表 + * @param array $clientInfo 客户端信息 + * @return AbstractProtocol|null + */ + public function matchProtocol($flag, $user, $servers, $clientInfo = []) + { + $protocolClassName = $this->matchProtocolClassName($flag); + if ($protocolClassName) { + return $this->makeProtocolInstance($protocolClassName, [ + 'user' => $user, + 'servers' => $servers, + 'clientName' => $clientInfo['name'] ?? null, + 'clientVersion' => $clientInfo['version'] ?? null + ]); + } + return null; + } + + /** + * 创建协议实例的通用方法,兼容不同版本的Laravel容器 + * + * @param string $class 类名 + * @param array $parameters 构造参数 + * @return object 实例 + */ + protected function makeProtocolInstance($class, array $parameters) + { + // Laravel's make method can accept an array of parameters as its second argument. + // These will be used when resolving the class's dependencies. + return $this->container->make($class, $parameters); + } +} \ No newline at end of file diff --git a/Xboard/app/Support/Setting.php b/Xboard/app/Support/Setting.php new file mode 100644 index 0000000..76adf8f --- /dev/null +++ b/Xboard/app/Support/Setting.php @@ -0,0 +1,140 @@ +cache = Cache::store('redis'); + } + + /** + * 获取配置. + */ + public function get(string $key, mixed $default = null): mixed + { + $this->load(); + return Arr::get($this->loadedSettings, strtolower($key), $default); + } + + /** + * 设置配置信息. + */ + public function set(string $key, mixed $value = null): bool + { + SettingModel::createOrUpdate(strtolower($key), $value); + $this->flush(); + return true; + } + + /** + * 保存配置到数据库. + */ + public function save(array $settings): bool + { + foreach ($settings as $key => $value) { + SettingModel::createOrUpdate(strtolower($key), $value); + } + $this->flush(); + return true; + } + + /** + * 删除配置信息 + */ + public function remove(string $key): bool + { + SettingModel::where('name', $key)->delete(); + $this->flush(); + return true; + } + + /** + * 更新单个设置项 + */ + public function update(string $key, $value): bool + { + return $this->set($key, $value); + } + + /** + * 批量获取配置项 + */ + public function getBatch(array $keys): array + { + $this->load(); + $result = []; + + foreach ($keys as $index => $item) { + $isNumericIndex = is_numeric($index); + $key = strtolower($isNumericIndex ? $item : $index); + $default = $isNumericIndex ? config('v2board.' . $item) : (config('v2board.' . $key) ?? $item); + + $result[$item] = Arr::get($this->loadedSettings, $key, $default); + } + + return $result; + } + + /** + * 将所有设置转换为数组 + */ + public function toArray(): array + { + $this->load(); + return $this->loadedSettings; + } + + /** + * 加载配置到请求内缓存 + */ + private function load(): void + { + if ($this->loadedSettings !== null) { + return; + } + + try { + $settings = $this->cache->rememberForever(self::CACHE_KEY, function (): array { + return array_change_key_case( + SettingModel::pluck('value', 'name')->toArray(), + CASE_LOWER + ); + }); + + // 处理JSON格式的值 + foreach ($settings as $key => $value) { + if (is_string($value)) { + $decoded = json_decode($value, true); + if (json_last_error() === JSON_ERROR_NONE) { + $settings[$key] = $decoded; + } + } + } + + $this->loadedSettings = $settings; + } catch (\Throwable) { + $this->loadedSettings = []; + } + } + + /** + * 清空缓存 + */ + private function flush(): void + { + $this->cache->forget(self::CACHE_KEY); + $this->loadedSettings = null; + } +} diff --git a/Xboard/app/Traits/HasPluginConfig.php b/Xboard/app/Traits/HasPluginConfig.php new file mode 100644 index 0000000..a44e2fc --- /dev/null +++ b/Xboard/app/Traits/HasPluginConfig.php @@ -0,0 +1,144 @@ +getPluginConfig(); + + if ($key) { + return $config[$key] ?? $default; + } + + return $config; + } + + /** + * 获取完整的插件配置 + */ + protected function getPluginConfig(): array + { + if ($this->pluginConfig === null) { + $pluginCode = $this->getPluginCode(); + + \Log::channel('daily')->info('Telegram Login: 获取插件配置', [ + 'plugin_code' => $pluginCode + ]); + + $this->pluginConfig = Cache::remember( + "plugin_config_{$pluginCode}", + 3600, + function () use ($pluginCode) { + $plugin = Plugin::where('code', $pluginCode) + ->where('is_enabled', true) + ->first(); + + if (!$plugin || !$plugin->config) { + return []; + } + + return json_decode($plugin->config, true) ?? []; + } + ); + } + + return $this->pluginConfig; + } + + /** + * 获取插件代码 + */ + public function getPluginCode(): string + { + if ($this->pluginCode === null) { + $this->pluginCode = $this->autoDetectPluginCode(); + } + + return $this->pluginCode; + } + + /** + * 设置插件代码(如果自动检测不准确可以手动设置) + */ + public function setPluginCode(string $pluginCode): void + { + $this->pluginCode = $pluginCode; + $this->pluginConfig = null; // 重置配置缓存 + $this->pluginEnabled = null; + } + + /** + * 自动检测插件代码 + */ + protected function autoDetectPluginCode(): string + { + $reflection = new \ReflectionClass($this); + $namespace = $reflection->getNamespaceName(); + + // 从命名空间提取插件代码 + // 例如: Plugin\TelegramLogin\Controllers => telegram_login + if (preg_match('/^Plugin\\\\(.+?)\\\\/', $namespace, $matches)) { + return $this->convertToKebabCase($matches[1]); + } + + throw new \RuntimeException('Unable to detect plugin code from namespace: ' . $namespace); + } + + /** + * 将 StudlyCase 转换为 kebab-case + */ + protected function convertToKebabCase(string $string): string + { + return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $string)); + } + + /** + * 检查插件是否启用 + */ + public function isPluginEnabled(): bool + { + if ($this->pluginEnabled !== null) { + return $this->pluginEnabled; + } + + $pluginCode = $this->getPluginCode(); + $isEnabled = Plugin::where('code', $pluginCode)->value('is_enabled'); + $this->pluginEnabled = (bool) $isEnabled; + + return $this->pluginEnabled; + } + + /** + * 清除插件配置缓存 + */ + public function clearConfigCache(): void + { + $pluginCode = $this->getPluginCode(); + Cache::forget("plugin_config_{$pluginCode}"); + $this->pluginConfig = null; + $this->pluginEnabled = null; + } +} \ No newline at end of file diff --git a/Xboard/app/Traits/QueryOperators.php b/Xboard/app/Traits/QueryOperators.php new file mode 100644 index 0000000..706d5c5 --- /dev/null +++ b/Xboard/app/Traits/QueryOperators.php @@ -0,0 +1,68 @@ + '=', + 'gt' => '>', + 'gte' => '>=', + 'lt' => '<', + 'lte' => '<=', + 'like' => 'like', + 'notlike' => 'not like', + 'null' => 'null', + 'notnull' => 'notnull', + default => 'like' + }; + } + + /** + * 获取查询值格式化 + * + * @param string $operator + * @param mixed $value + * @return mixed + */ + protected function formatQueryValue(string $operator, mixed $value): mixed + { + return match (strtolower($operator)) { + 'like', 'notlike' => "%{$value}%", + 'null', 'notnull' => null, + default => $value + }; + } + + /** + * 应用查询条件 + * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param string $field + * @param string $operator + * @param mixed $value + * @return void + */ + protected function applyQueryCondition($query, array|Expression|string $field, string $operator, mixed $value): void + { + $queryOperator = $this->getQueryOperator($operator); + + if ($queryOperator === 'null') { + $query->whereNull($field); + } elseif ($queryOperator === 'notnull') { + $query->whereNotNull($field); + } else { + $query->where($field, $queryOperator, $this->formatQueryValue($operator, $value)); + } + } +} \ No newline at end of file diff --git a/Xboard/app/Utils/CacheKey.php b/Xboard/app/Utils/CacheKey.php new file mode 100644 index 0000000..6795c6c --- /dev/null +++ b/Xboard/app/Utils/CacheKey.php @@ -0,0 +1,69 @@ + '邮箱验证码', + 'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间', + 'TEMP_TOKEN' => '临时令牌', + 'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒', + 'SCHEDULE_LAST_CHECK_AT' => '计划任务最后检查时间', + 'REGISTER_IP_RATE_LIMIT' => '注册频率限制', + 'LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP' => '最后一次发送登入链接时间', + 'PASSWORD_ERROR_LIMIT' => '密码错误次数限制', + 'USER_SESSIONS' => '用户session', + 'FORGET_REQUEST_LIMIT' => '找回密码次数限制' + ]; + + // 允许的缓存键模式(支持通配符) + const ALLOWED_PATTERNS = [ + 'SERVER_*_ONLINE_USER', // 节点在线用户 + 'MULTI_SERVER_*_ONLINE_USER', // 多服务器在线用户 + 'SERVER_*_LAST_CHECK_AT', // 节点最后检查时间 + 'SERVER_*_LAST_PUSH_AT', // 节点最后推送时间 + 'SERVER_*_LOAD_STATUS', // 节点负载状态 + 'SERVER_*_LAST_LOAD_AT', // 节点最后负载提交时间 + 'SERVER_*_METRICS', // 节点指标数据 + 'USER_ONLINE_CONN_*_*', // 用户在线连接数 (特定节点类型_ID) + ]; + + /** + * 生成缓存键 + */ + public static function get(string $key, mixed $uniqueValue = null): string + { + // 检查是否为核心键 + if (array_key_exists($key, self::CORE_KEYS)) { + return $uniqueValue ? $key . '_' . $uniqueValue : $key; + } + + // 检查是否匹配允许的模式 + if (self::matchesPattern($key)) { + return $uniqueValue ? $key . '_' . $uniqueValue : $key; + } + + // 开发环境下记录警告,生产环境允许通过 + if (app()->environment('local', 'development')) { + logger()->warning("Unknown cache key used: {$key}"); + } + + return $uniqueValue ? $key . '_' . $uniqueValue : $key; + } + + /** + * 检查键名是否匹配允许的模式 + */ + private static function matchesPattern(string $key): bool + { + foreach (self::ALLOWED_PATTERNS as $pattern) { + $regex = '/^' . str_replace('*', '[A-Za-z0-9_]+', $pattern) . '$/'; + if (preg_match($regex, $key)) { + return true; + } + } + return false; + } +} diff --git a/Xboard/app/Utils/Dict.php b/Xboard/app/Utils/Dict.php new file mode 100644 index 0000000..e0e6fe6 --- /dev/null +++ b/Xboard/app/Utils/Dict.php @@ -0,0 +1,23 @@ +", "~", "+", "=", ",", "." + )); + } + + $charsLen = count($chars) - 1; + shuffle($chars); + $str = ''; + for ($i = 0; $i < $len; $i++) { + $str .= $chars[mt_rand(0, $charsLen)]; + } + return $str; + } + + public static function wrapIPv6($addr) { + if (filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return "[$addr]"; + } else { + return $addr; + } + } + + public static function multiPasswordVerify($algo, $salt, $password, $hash) + { + switch($algo) { + case 'md5': return md5($password) === $hash; + case 'sha256': return hash('sha256', $password) === $hash; + case 'md5salt': return md5($password . $salt) === $hash; + case 'sha256salt': return hash('sha256', $password . $salt) === $hash; + default: return password_verify($password, $hash); + } + } + + public static function emailSuffixVerify($email, $suffixs) + { + $suffix = preg_split('/@/', $email)[1]; + if (!$suffix) return false; + if (!is_array($suffixs)) { + $suffixs = preg_split('/,/', $suffixs); + } + if (!in_array($suffix, $suffixs)) return false; + return true; + } + + public static function trafficConvert(float $byte) + { + $kb = 1024; + $mb = 1048576; + $gb = 1073741824; + if ($byte > $gb) { + return round($byte / $gb, 2) . ' GB'; + } else if ($byte > $mb) { + return round($byte / $mb, 2) . ' MB'; + } else if ($byte > $kb) { + return round($byte / $kb, 2) . ' KB'; + } else if ($byte < 0) { + return 0; + } else { + return round($byte, 2) . ' B'; + } + } + + public static function getSubscribeUrl(string $token, $subscribeUrl = null) + { + $path = route('client.subscribe', ['token' => $token], false); + + if ($subscribeUrl) { + $finalUrl = rtrim($subscribeUrl, '/') . $path; + return HookManager::filter('subscribe.url', $finalUrl); + } + + $urlString = (string)admin_setting('subscribe_url', ''); + $subscribeUrlList = $urlString ? explode(',', $urlString) : []; + + if (empty($subscribeUrlList)) { + return HookManager::filter('subscribe.url', url($path)); + } + + $selectedUrl = self::replaceByPattern(Arr::random($subscribeUrlList)); + $finalUrl = rtrim($selectedUrl, '/') . $path; + + return HookManager::filter('subscribe.url', $finalUrl); + } + + public static function randomPort($range): int { + $portRange = explode('-', (string) $range, 2); + $min = (int) ($portRange[0] ?? 0); + $max = (int) ($portRange[1] ?? $portRange[0] ?? 0); + if ($min > $max) { + [$min, $max] = [$max, $min]; + } + return random_int($min, $max); + } + + public static function base64EncodeUrlSafe($data) + { + $encoded = base64_encode($data); + return str_replace(['+', '/', '='], ['-', '_', ''], $encoded); + } + + /** + * 根据规则替换域名中对应的字符串 + * + * @param string $input 用户输入的字符串 + * @return string 替换后的字符串 + */ + public static function replaceByPattern($input) + { + $patterns = [ + '/\[(\d+)-(\d+)\]/' => function ($matches) { + $min = intval($matches[1]); + $max = intval($matches[2]); + if ($min > $max) { + list($min, $max) = [$max, $min]; + } + $randomNumber = rand($min, $max); + return $randomNumber; + }, + '/\[uuid\]/' => function () { + return self::guid(true); + } + ]; + foreach ($patterns as $pattern => $callback) { + $input = preg_replace_callback($pattern, $callback, $input); + } + return $input; + } + + public static function getIpByDomainName($domain) { + return gethostbynamel($domain) ?: []; + } + + public static function getTlsFingerprint($utls = null) + { + + if (is_array($utls) || is_object($utls)) { + if (!data_get($utls, 'enabled')) { + return null; + } + $fingerprint = data_get($utls, 'fingerprint', 'chrome'); + if ($fingerprint !== 'random') { + return $fingerprint; + } + } + + $fingerprints = ['chrome', 'firefox', 'safari', 'ios', 'edge', 'qq']; + return Arr::random($fingerprints); + } + + public static function encodeURIComponent($str) { + $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'", '%28'=>'(', '%29'=>')'); + return strtr(rawurlencode($str), $revert); + } + + public static function getEmailSuffix(): array|bool + { + $suffix = admin_setting('email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT); + if (!is_array($suffix)) { + return preg_split('/,/', $suffix); + } + return $suffix; + } + + /** + * convert the transfer_enable to GB + * @param float $transfer_enable + * @return float + */ + public static function transferToGB(float $transfer_enable): float + { + return $transfer_enable / 1073741824; + } + + /** + * 转义 Telegram Markdown 特殊字符 + * @param string $text + * @return string + */ + public static function escapeMarkdown(string $text): string + { + return str_replace(['_', '*', '`', '['], ['\_', '\*', '\`', '\['], $text); + } +} diff --git a/Xboard/app/WebSocket/NodeEventHandlers.php b/Xboard/app/WebSocket/NodeEventHandlers.php new file mode 100644 index 0000000..75521f8 --- /dev/null +++ b/Xboard/app/WebSocket/NodeEventHandlers.php @@ -0,0 +1,144 @@ +type); + Cache::put(\App\Utils\CacheKey::get('SERVER_' . $nodeType . '_LAST_CHECK_AT', $nodeId), time(), 3600); + ServerService::updateMetrics($node, $data); + + Log::debug("[WS] Node#{$nodeId} status updated"); + } + + /** + * Handle device report from node + * + * 数据格式: {"event": "report.devices", "data": {userId: [ip1, ip2, ...], ...}} + */ + public static function handleDeviceReport(TcpConnection $conn, int $nodeId, array $data): void + { + $service = app(DeviceStateService::class); + + // Get old data + $oldDevices = $service->getNodeDevices($nodeId); + + // Calculate diff + $removedUsers = array_diff_key($oldDevices, $data); + $newDevices = []; + + foreach ($data as $userId => $ips) { + if (is_numeric($userId) && is_array($ips)) { + $newDevices[(int) $userId] = $ips; + } + } + + // Handle removed users + foreach ($removedUsers as $userId => $ips) { + $service->removeNodeDevices($nodeId, $userId); + $service->notifyUpdate($userId); + } + + // Handle new/updated users + foreach ($newDevices as $userId => $ips) { + $service->setDevices($userId, $nodeId, $ips); + } + + // Mark for push + Redis::sadd('device:push_pending_nodes', $nodeId); + + Log::debug("[WS] Node#{$nodeId} synced " . count($newDevices) . " users, removed " . count($removedUsers)); + } + + /** + * Handle device state request from node + */ + public static function handleDeviceRequest(TcpConnection $conn, int $nodeId, array $data = []): void + { + $node = Server::find($nodeId); + if (!$node) return; + + $users = ServerService::getAvailableUsers($node); + $userIds = $users->pluck('id')->toArray(); + + $service = app(DeviceStateService::class); + $devices = $service->getUsersDevices($userIds); + + $conn->send(json_encode([ + 'event' => 'sync.devices', + 'data' => ['users' => $devices], + ])); + + Log::debug("[WS] Node#{$nodeId} requested devices, sent " . count($devices) . " users"); + } + + /** + * Push device state to node + */ + public static function pushDeviceStateToNode(int $nodeId, DeviceStateService $service): void + { + $node = Server::find($nodeId); + if (!$node) return; + + $users = ServerService::getAvailableUsers($node); + $userIds = $users->pluck('id')->toArray(); + $devices = $service->getUsersDevices($userIds); + + NodeRegistry::send($nodeId, 'sync.devices', [ + 'users' => $devices + ]); + + Log::debug("[WS] Pushed device state to node#{$nodeId}: " . count($devices) . " users"); + } + + /** + * Push full config + users to newly connected node + */ + public static function pushFullSync(TcpConnection $conn, Server $node): void + { + $nodeId = $conn->nodeId; + + // Push config + $config = ServerService::buildNodeConfig($node); + $conn->send(json_encode([ + 'event' => 'sync.config', + 'data' => ['config' => $config] + ])); + + // Push users + $users = ServerService::getAvailableUsers($node)->toArray(); + $conn->send(json_encode([ + 'event' => 'sync.users', + 'data' => ['users' => $users] + ])); + + Log::info("[WS] Full sync pushed to node#{$nodeId}", [ + 'users' => count($users), + ]); + } +} diff --git a/Xboard/app/WebSocket/NodeWorker.php b/Xboard/app/WebSocket/NodeWorker.php new file mode 100644 index 0000000..d45dd22 --- /dev/null +++ b/Xboard/app/WebSocket/NodeWorker.php @@ -0,0 +1,249 @@ + [NodeEventHandlers::class, 'handlePong'], + 'node.status' => [NodeEventHandlers::class, 'handleNodeStatus'], + 'report.devices' => [NodeEventHandlers::class, 'handleDeviceReport'], + 'request.devices' => [NodeEventHandlers::class, 'handleDeviceRequest'], + ]; + + public function __construct(string $host, int $port) + { + $this->worker = new Worker("websocket://{$host}:{$port}"); + $this->worker->count = 1; + $this->worker->name = 'xboard-ws-server'; + } + + public function run(): void + { + $this->setupLogging(); + $this->setupCallbacks(); + Worker::runAll(); + } + + private function setupLogging(): void + { + $logPath = storage_path('logs'); + if (!is_dir($logPath)) { + mkdir($logPath, 0777, true); + } + Worker::$logFile = $logPath . '/xboard-ws-server.log'; + Worker::$pidFile = $logPath . '/xboard-ws-server.pid'; + } + + private function setupCallbacks(): void + { + $this->worker->onWorkerStart = [$this, 'onWorkerStart']; + $this->worker->onConnect = [$this, 'onConnect']; + $this->worker->onWebSocketConnect = [$this, 'onWebSocketConnect']; + $this->worker->onMessage = [$this, 'onMessage']; + $this->worker->onClose = [$this, 'onClose']; + } + + public function onWorkerStart(Worker $worker): void + { + Log::info("[WS] Worker started, pid={$worker->id}"); + $this->subscribeRedis(); + $this->setupTimers(); + } + + private function setupTimers(): void + { + // Ping timer + Timer::add(self::PING_INTERVAL, function () { + foreach (NodeRegistry::getConnectedNodeIds() as $nodeId) { + $conn = NodeRegistry::get($nodeId); + if ($conn) { + $conn->send(json_encode(['event' => 'ping'])); + } + } + }); + + // Device state push timer + Timer::add(10, function () { + $pendingNodeIds = Redis::spop('device:push_pending_nodes', 100); + if (empty($pendingNodeIds)) { + return; + } + + $service = app(DeviceStateService::class); + foreach ($pendingNodeIds as $nodeId) { + $nodeId = (int) $nodeId; + if (NodeRegistry::get($nodeId) !== null) { + NodeEventHandlers::pushDeviceStateToNode($nodeId, $service); + } + } + }); + } + + public function onConnect(TcpConnection $conn): void + { + $conn->authTimer = Timer::add(self::AUTH_TIMEOUT, function () use ($conn) { + if (empty($conn->nodeId)) { + $conn->close(json_encode([ + 'event' => 'error', + 'data' => ['message' => 'auth timeout'], + ])); + } + }, [], false); + } + + public function onWebSocketConnect(TcpConnection $conn, $httpMessage): void + { + $queryString = ''; + if (is_string($httpMessage)) { + $queryString = parse_url($httpMessage, PHP_URL_QUERY) ?? ''; + } elseif ($httpMessage instanceof \Workerman\Protocols\Http\Request) { + $queryString = $httpMessage->queryString(); + } + + parse_str($queryString, $params); + $token = $params['token'] ?? ''; + $nodeId = (int) ($params['node_id'] ?? 0); + + // Authenticate + $serverToken = admin_setting('server_token', ''); + if ($token === '' || $serverToken === '' || !hash_equals($serverToken, $token)) { + $conn->close(json_encode([ + 'event' => 'error', + 'data' => ['message' => 'invalid token'], + ])); + return; + } + + $node = ServerService::getServer($nodeId, null); + if (!$node) { + $conn->close(json_encode([ + 'event' => 'error', + 'data' => ['message' => 'node not found'], + ])); + return; + } + + // Auth passed + if (isset($conn->authTimer)) { + Timer::del($conn->authTimer); + } + + $conn->nodeId = $nodeId; + NodeRegistry::add($nodeId, $conn); + Cache::put("node_ws_alive:{$nodeId}", true, 86400); + + // Clear old device data + app(DeviceStateService::class)->clearAllNodeDevices($nodeId); + + Log::debug("[WS] Node#{$nodeId} connected", [ + 'remote' => $conn->getRemoteIp(), + 'total' => NodeRegistry::count(), + ]); + + // Send auth success + $conn->send(json_encode([ + 'event' => 'auth.success', + 'data' => ['node_id' => $nodeId], + ])); + + // Push full sync + NodeEventHandlers::pushFullSync($conn, $node); + } + + public function onMessage(TcpConnection $conn, $data): void + { + $msg = json_decode($data, true); + if (!is_array($msg)) { + return; + } + + $event = $msg['event'] ?? ''; + $nodeId = $conn->nodeId ?? null; + + if (isset($this->handlers[$event]) && $nodeId) { + $handler = $this->handlers[$event]; + $handler($conn, $nodeId, $msg['data'] ?? []); + } + } + + public function onClose(TcpConnection $conn): void + { + if (!empty($conn->nodeId)) { + $nodeId = $conn->nodeId; + NodeRegistry::remove($nodeId); + Cache::forget("node_ws_alive:{$nodeId}"); + + $service = app(DeviceStateService::class); + $affectedUserIds = $service->clearAllNodeDevices($nodeId); + foreach ($affectedUserIds as $userId) { + $service->notifyUpdate($userId); + } + + Log::debug("[WS] Node#{$nodeId} disconnected", [ + 'total' => NodeRegistry::count(), + 'affected_users' => count($affectedUserIds), + ]); + } + } + + private function subscribeRedis(): void + { + $host = config('database.redis.default.host', '127.0.0.1'); + $port = config('database.redis.default.port', 6379); + + if (str_starts_with($host, '/')) { + $redisUri = "unix://{$host}"; + } else { + $redisUri = "redis://{$host}:{$port}"; + } + + $redis = new \Workerman\Redis\Client($redisUri); + + $password = config('database.redis.default.password'); + if ($password) { + $redis->auth($password); + } + + $prefix = config('database.redis.options.prefix', ''); + $channel = $prefix . 'node:push'; + + $redis->subscribe([$channel], function ($chan, $message) { + $payload = json_decode($message, true); + if (!is_array($payload)) { + return; + } + + $nodeId = $payload['node_id'] ?? null; + $event = $payload['event'] ?? ''; + $data = $payload['data'] ?? []; + + if (!$nodeId || !$event) { + return; + } + + $sent = NodeRegistry::send((int) $nodeId, $event, $data); + if ($sent) { + Log::debug("[WS] Pushed {$event} to node#{$nodeId}"); + } + }); + + Log::info("[WS] Subscribed to Redis channel: {$channel}"); + } +} diff --git a/Xboard/artisan b/Xboard/artisan new file mode 100644 index 0000000..5c23e2e --- /dev/null +++ b/Xboard/artisan @@ -0,0 +1,53 @@ +#!/usr/bin/env php +make(Illuminate\Contracts\Console\Kernel::class); + +$status = $kernel->handle( + $input = new Symfony\Component\Console\Input\ArgvInput, + new Symfony\Component\Console\Output\ConsoleOutput +); + +/* +|-------------------------------------------------------------------------- +| Shutdown The Application +|-------------------------------------------------------------------------- +| +| Once Artisan has finished running, we will fire off the shutdown events +| so that any final work may be done by the application before we shut +| down the process. This is the last thing to happen to the request. +| +*/ + +$kernel->terminate($input, $status); + +exit($status); diff --git a/Xboard/bootstrap/app.php b/Xboard/bootstrap/app.php new file mode 100644 index 0000000..037e17d --- /dev/null +++ b/Xboard/bootstrap/app.php @@ -0,0 +1,55 @@ +singleton( + Illuminate\Contracts\Http\Kernel::class, + App\Http\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Console\Kernel::class, + App\Console\Kernel::class +); + +$app->singleton( + Illuminate\Contracts\Debug\ExceptionHandler::class, + App\Exceptions\Handler::class +); + +/* +|-------------------------------------------------------------------------- +| Return The Application +|-------------------------------------------------------------------------- +| +| This script returns the application instance. The instance is given to +| the calling script so we can separate the building of the instances +| from the actual running of the application and sending responses. +| +*/ + +return $app; diff --git a/Xboard/bootstrap/cache/.gitignore b/Xboard/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/Xboard/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Xboard/compose.sample.yaml b/Xboard/compose.sample.yaml new file mode 100644 index 0000000..b955027 --- /dev/null +++ b/Xboard/compose.sample.yaml @@ -0,0 +1,44 @@ +services: + web: + image: ghcr.io/cedar2025/xboard:new + volumes: + - redis-data:/data + - ./:/www/ + environment: + - docker=true + depends_on: + - redis + network_mode: host + command: php artisan octane:start --port=7001 --host=0.0.0.0 + restart: always + horizon: + image: ghcr.io/cedar2025/xboard:new + volumes: + - redis-data:/data + - ./:/www/ + restart: always + network_mode: host + command: php artisan horizon + depends_on: + - redis + ws-server: + image: ghcr.io/cedar2025/xboard:new + volumes: + - redis-data:/data + - ./:/www/ + restart: always + network_mode: host + command: php artisan ws-server start + depends_on: + - redis + redis: + image: redis:7-alpine + command: redis-server --unixsocket /data/redis.sock --unixsocketperm 777 + restart: unless-stopped + volumes: + - redis-data:/data + sysctls: + net.core.somaxconn: 1024 + +volumes: + redis-data: diff --git a/Xboard/composer.json b/Xboard/composer.json new file mode 100644 index 0000000..ce7762d --- /dev/null +++ b/Xboard/composer.json @@ -0,0 +1,98 @@ +{ + "name": "xboard/xboard", + "type": "project", + "description": "xboard is a proxy protocol manage.", + "keywords": [ + "xboard", + "v2ray", + "shadowsocks", + "trojan", + "laravel" + ], + "license": "MIT", + "require": { + "php": "^8.2", + "bacon/bacon-qr-code": "^2.0", + "doctrine/dbal": "^4.0", + "google/cloud-storage": "^1.35", + "google/recaptcha": "^1.2", + "guzzlehttp/guzzle": "^7.8", + "laravel/framework": "^12.0", + "laravel/horizon": "^5.30", + "laravel/octane": "2.11.*", + "laravel/prompts": "^0.3", + "laravel/sanctum": "^4.0", + "laravel/tinker": "^2.10", + "linfo/linfo": "^4.0", + "paragonie/sodium_compat": "^1.20", + "php-curl-class/php-curl-class": "^8.6", + "spatie/db-dumper": "^3.4", + "stripe/stripe-php": "^7.36.1", + "symfony/http-client": "^7.0", + "symfony/mailgun-mailer": "^7.0", + "symfony/yaml": "*", + "webmozart/assert": "*", + "workerman/redis": "^2.0", + "workerman/workerman": "^5.1", + "zoujingli/ip2region": "^2.0" + }, + "require-dev": { + "barryvdh/laravel-debugbar": "^3.9", + "fakerphp/faker": "^1.9.1", + "larastan/larastan": "^3.0", + "mockery/mockery": "^1.6", + "nunomaduro/collision": "^8.0", + "orangehill/iseed": "^3.0", + "phpunit/phpunit": "^11.0", + "spatie/laravel-ignition": "^2.4" + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Library\\": "library/", + "Plugin\\": "plugins/" + }, + "classmap": [ + "database/seeders", + "database/factories" + ], + "files": [ + "app/Helpers/Functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "minimum-stability": "stable", + "prefer-stable": true, + "scripts": { + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi" + ] + }, + "repositories": [ + { + "type": "composer", + "url": "https://packagist.org" + } + ] +} diff --git a/Xboard/config/app.php b/Xboard/config/app.php new file mode 100644 index 0000000..3ed7fe4 --- /dev/null +++ b/Xboard/config/app.php @@ -0,0 +1,193 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + 'asset_url' => env('ASSET_URL', null), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => 'Asia/Shanghai', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => 'zh-CN', + + /* + |-------------------------------------------------------------------------- + | Application Fallback Locale + |-------------------------------------------------------------------------- + | + | The fallback locale determines the locale to use when the current one + | is not available. You may change the value to correspond to any of + | the language folders that are provided through your application. + | + */ + + 'fallback_locale' => 'zh-CN', + + /* + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | + */ + + 'faker_locale' => 'zh-CN', + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + + 'key' => env('APP_KEY'), + + 'cipher' => 'AES-256-CBC', + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => [ + + /* + * Laravel Framework Service Providers... + */ + Illuminate\Auth\AuthServiceProvider::class, + Illuminate\Broadcasting\BroadcastServiceProvider::class, + Illuminate\Bus\BusServiceProvider::class, + Illuminate\Cache\CacheServiceProvider::class, + Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, + Illuminate\Cookie\CookieServiceProvider::class, + Illuminate\Database\DatabaseServiceProvider::class, + Illuminate\Encryption\EncryptionServiceProvider::class, + Illuminate\Filesystem\FilesystemServiceProvider::class, + Illuminate\Foundation\Providers\FoundationServiceProvider::class, + Illuminate\Hashing\HashServiceProvider::class, + Illuminate\Mail\MailServiceProvider::class, + Illuminate\Notifications\NotificationServiceProvider::class, + Illuminate\Pagination\PaginationServiceProvider::class, + Illuminate\Pipeline\PipelineServiceProvider::class, + Illuminate\Queue\QueueServiceProvider::class, + Illuminate\Redis\RedisServiceProvider::class, + Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, + Illuminate\Session\SessionServiceProvider::class, + Illuminate\Translation\TranslationServiceProvider::class, + Illuminate\Validation\ValidationServiceProvider::class, + Illuminate\View\ViewServiceProvider::class, + + /* + * Package Service Providers... + */ + + /* + * Application Service Providers... + */ + App\Providers\AuthServiceProvider::class, + // App\Providers\BroadcastServiceProvider::class, + App\Providers\EventServiceProvider::class, + App\Providers\HorizonServiceProvider::class, + App\Providers\RouteServiceProvider::class, + App\Providers\SettingServiceProvider::class, + App\Providers\OctaneServiceProvider::class, + App\Providers\PluginServiceProvider::class, + App\Providers\ProtocolServiceProvider::class, + + ], + + /* + |-------------------------------------------------------------------------- + | V2board version + |-------------------------------------------------------------------------- + | + | The only modification by laravel config + | + */ + 'version' => '1.0.0' +]; diff --git a/Xboard/config/auth.php b/Xboard/config/auth.php new file mode 100644 index 0000000..c75105f --- /dev/null +++ b/Xboard/config/auth.php @@ -0,0 +1,103 @@ + [ + 'guard' => 'api', + 'passwords' => 'users', + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | here which uses session storage and the Eloquent user provider. + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | Supported: "session", "token" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + + 'api' => [ + 'driver' => 'sanctum', + 'provider' => 'users', + 'hash' => false, + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication drivers have a user provider. This defines how the + | users are actually retrieved out of your database or other storage + | mechanisms used by this application to persist your user's data. + | + | If you have multiple user tables or models you may configure multiple + | sources which represent each model / table. These sources may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => App\Models\User::class, + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | You may specify multiple password reset configurations if you have more + | than one user table or model in the application and you want to have + | separate password reset settings based on the specific user types. + | + | The expire time is the number of minutes that the reset token should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => 'password_resets', + 'expire' => 60, + ], + ], + +]; diff --git a/Xboard/config/broadcasting.php b/Xboard/config/broadcasting.php new file mode 100644 index 0000000..3bba110 --- /dev/null +++ b/Xboard/config/broadcasting.php @@ -0,0 +1,59 @@ + env('BROADCAST_DRIVER', 'null'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over websockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'cluster' => env('PUSHER_APP_CLUSTER'), + 'useTLS' => true, + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + ], + + 'log' => [ + 'driver' => 'log', + ], + + 'null' => [ + 'driver' => 'null', + ], + + ], + +]; diff --git a/Xboard/config/cache.php b/Xboard/config/cache.php new file mode 100644 index 0000000..b97535c --- /dev/null +++ b/Xboard/config/cache.php @@ -0,0 +1,107 @@ + env('CACHE_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + */ + + 'stores' => [ + + 'apc' => [ + 'driver' => 'apc', + ], + + 'array' => [ + 'driver' => 'array', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'cache', + 'connection' => null, + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'cache', + ], + + 'octane' => [ + 'driver' => 'octane' + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_V2BOARD_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing a RAM based store such as APC or Memcached, there might + | be other applications utilizing the same cache. So, we'll specify a + | value to get prefixed to all our keys so we can avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache'), + +]; diff --git a/Xboard/config/cloud_storage.php b/Xboard/config/cloud_storage.php new file mode 100644 index 0000000..7ba0747 --- /dev/null +++ b/Xboard/config/cloud_storage.php @@ -0,0 +1,10 @@ + [ + 'key_file' => env('GOOGLE_CLOUD_KEY_FILE') ? base_path(env('GOOGLE_CLOUD_KEY_FILE')) : null, + 'storage_bucket' => env('GOOGLE_CLOUD_STORAGE_BUCKET'), + ], + +]; \ No newline at end of file diff --git a/Xboard/config/cors.php b/Xboard/config/cors.php new file mode 100644 index 0000000..558369d --- /dev/null +++ b/Xboard/config/cors.php @@ -0,0 +1,34 @@ + ['api/*'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; diff --git a/Xboard/config/database.php b/Xboard/config/database.php new file mode 100644 index 0000000..1929d5e --- /dev/null +++ b/Xboard/config/database.php @@ -0,0 +1,157 @@ + env('DB_CONNECTION', 'mysql'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DATABASE_URL'), + 'database' => env('DB_DATABASE') ? base_path(env('DB_DATABASE')) : database_path('database.sqlite'), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => env('DB_BUSY_TIMEOUT', 30000), + 'journal_mode' => env('DB_JOURNAL_MODE', 'wal'), + 'synchronous' => env('DB_SYNCHRONOUS', 'normal'), + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => (extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + PDO::ATTR_PERSISTENT => false, + ]) : []), + 'pool' => [ + 'min_connections' => 1, + 'max_connections' => 10, + 'idle_timeout' => 60, + ], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DATABASE_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_DB', 0), + 'persistent' => true, // 开启持久连接 + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => env('REDIS_CACHE_DB', 1), + ], + + ], + +]; diff --git a/Xboard/config/debugbar.php b/Xboard/config/debugbar.php new file mode 100644 index 0000000..fe3b192 --- /dev/null +++ b/Xboard/config/debugbar.php @@ -0,0 +1,275 @@ + env('DEBUGBAR_ENABLED', null), + 'except' => [ + 'telescope*', + 'horizon*', + ], + + /* + |-------------------------------------------------------------------------- + | Storage settings + |-------------------------------------------------------------------------- + | + | DebugBar stores data for session/ajax requests. + | You can disable this, so the debugbar stores data in headers/session, + | but this can cause problems with large data collectors. + | By default, file storage (in the storage folder) is used. Redis and PDO + | can also be used. For PDO, run the package migrations first. + | + */ + 'storage' => [ + 'enabled' => true, + 'driver' => 'file', // redis, file, pdo, socket, custom + 'path' => storage_path('debugbar'), // For file driver + 'connection' => null, // Leave null for default connection (Redis/PDO) + 'provider' => '', // Instance of StorageInterface for custom driver + 'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver + 'port' => 2304, // Port to use with the "socket" driver + ], + + /* + |-------------------------------------------------------------------------- + | Editor + |-------------------------------------------------------------------------- + | + | Choose your preferred editor to use when clicking file name. + | + | Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote", + | "vscode-insiders-remote", "vscodium", "textmate", "emacs", + | "sublime", "atom", "nova", "macvim", "idea", "netbeans", + | "xdebug", "espresso" + | + */ + + 'editor' => env('DEBUGBAR_EDITOR', 'phpstorm'), + + /* + |-------------------------------------------------------------------------- + | Remote Path Mapping + |-------------------------------------------------------------------------- + | + | If you are using a remote dev server, like Laravel Homestead, Docker, or + | even a remote VPS, it will be necessary to specify your path mapping. + | + | Leaving one, or both of these, empty or null will not trigger the remote + | URL changes and Debugbar will treat your editor links as local files. + | + | "remote_sites_path" is an absolute base path for your sites or projects + | in Homestead, Vagrant, Docker, or another remote development server. + | + | Example value: "/home/vagrant/Code" + | + | "local_sites_path" is an absolute base path for your sites or projects + | on your local computer where your IDE or code editor is running on. + | + | Example values: "/Users//Code", "C:\Users\\Documents\Code" + | + */ + + 'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH', ''), + 'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', ''), + + /* + |-------------------------------------------------------------------------- + | Vendors + |-------------------------------------------------------------------------- + | + | Vendor files are included by default, but can be set to false. + | This can also be set to 'js' or 'css', to only include javascript or css vendor files. + | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) + | and for js: jquery and and highlight.js + | So if you want syntax highlighting, set it to true. + | jQuery is set to not conflict with existing jQuery scripts. + | + */ + + 'include_vendors' => true, + + /* + |-------------------------------------------------------------------------- + | Capture Ajax Requests + |-------------------------------------------------------------------------- + | + | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), + | you can use this option to disable sending the data through the headers. + | + | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. + | + | Note for your request to be identified as ajax requests they must either send the header + | X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header. + */ + + 'capture_ajax' => true, + 'add_ajax_timing' => false, + + /* + |-------------------------------------------------------------------------- + | Custom Error Handler for Deprecated warnings + |-------------------------------------------------------------------------- + | + | When enabled, the Debugbar shows deprecated warnings for Symfony components + | in the Messages tab. + | + */ + 'error_handler' => false, + + /* + |-------------------------------------------------------------------------- + | Clockwork integration + |-------------------------------------------------------------------------- + | + | The Debugbar can emulate the Clockwork headers, so you can use the Chrome + | Extension, without the server-side code. It uses Debugbar collectors instead. + | + */ + 'clockwork' => false, + + /* + |-------------------------------------------------------------------------- + | DataCollectors + |-------------------------------------------------------------------------- + | + | Enable/disable DataCollectors + | + */ + + 'collectors' => [ + 'phpinfo' => true, // Php version + 'messages' => true, // Messages + 'time' => true, // Time Datalogger + 'memory' => true, // Memory usage + 'exceptions' => true, // Exception displayer + 'log' => true, // Logs from Monolog (merged in messages if enabled) + 'db' => true, // Show database (PDO) queries and bindings + 'views' => true, // Views with their data + 'route' => true, // Current route information + 'auth' => false, // Display Laravel authentication status + 'gate' => true, // Display Laravel Gate checks + 'session' => true, // Display session data + 'symfony_request' => true, // Only one can be enabled.. + 'mail' => true, // Catch mail messages + 'laravel' => false, // Laravel version and environment + 'events' => false, // All events fired + 'default_request' => false, // Regular or special Symfony request logger + 'logs' => false, // Add the latest log messages + 'files' => false, // Show the included files + 'config' => false, // Display config settings + 'cache' => false, // Display cache events + 'models' => true, // Display models + 'livewire' => true, // Display Livewire (when available) + ], + + /* + |-------------------------------------------------------------------------- + | Extra options + |-------------------------------------------------------------------------- + | + | Configure some DataCollectors + | + */ + + 'options' => [ + 'auth' => [ + 'show_name' => true, // Also show the users name/email in the debugbar + ], + 'db' => [ + 'with_params' => true, // Render SQL with the parameters substituted + 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. + 'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults) + 'timeline' => false, // Add the queries to the timeline + 'duration_background' => true, // Show shaded background on each query relative to how long it took to execute. + 'explain' => [ // Show EXPLAIN output on queries + 'enabled' => false, + 'types' => ['SELECT'], // Deprecated setting, is always only SELECT + ], + 'hints' => false, // Show hints for common mistakes + 'show_copy' => false, // Show copy button next to the query + ], + 'mail' => [ + 'full_log' => false, + ], + 'views' => [ + 'timeline' => false, // Add the views to the timeline (Experimental) + 'data' => false, //Note: Can slow down the application, because the data can be quite large.. + ], + 'route' => [ + 'label' => true, // show complete route on bar + ], + 'logs' => [ + 'file' => null, + ], + 'cache' => [ + 'values' => true, // collect cache values + ], + ], + + /* + |-------------------------------------------------------------------------- + | Inject Debugbar in Response + |-------------------------------------------------------------------------- + | + | Usually, the debugbar is added just before , by listening to the + | Response after the App is done. If you disable this, you have to add them + | in your template yourself. See http://phpdebugbar.com/docs/rendering.html + | + */ + + 'inject' => true, + + /* + |-------------------------------------------------------------------------- + | DebugBar route prefix + |-------------------------------------------------------------------------- + | + | Sometimes you want to set route prefix to be used by DebugBar to load + | its resources from. Usually the need comes from misconfigured web server or + | from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 + | + */ + 'route_prefix' => '_debugbar', + + /* + |-------------------------------------------------------------------------- + | DebugBar route domain + |-------------------------------------------------------------------------- + | + | By default DebugBar route served from the same domain that request served. + | To override default domain, specify it as a non-empty value. + */ + 'route_domain' => null, + + /* + |-------------------------------------------------------------------------- + | DebugBar theme + |-------------------------------------------------------------------------- + | + | Switches between light and dark theme. If set to auto it will respect system preferences + | Possible values: auto, light, dark + */ + 'theme' => env('DEBUGBAR_THEME', 'auto'), + + /* + |-------------------------------------------------------------------------- + | Backtrace stack limit + |-------------------------------------------------------------------------- + | + | By default, the DebugBar limits the number of frames returned by the 'debug_backtrace()' function. + | If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit. + */ + 'debug_backtrace_limit' => 50, +]; diff --git a/Xboard/config/filesystems.php b/Xboard/config/filesystems.php new file mode 100644 index 0000000..925b69d --- /dev/null +++ b/Xboard/config/filesystems.php @@ -0,0 +1,69 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Default Cloud Filesystem Disk + |-------------------------------------------------------------------------- + | + | Many applications store files both locally and in the cloud. For this + | reason, you may specify a default "cloud" driver here. This driver + | will be bound as the Cloud disk implementation in the container. + | + */ + + 'cloud' => env('FILESYSTEM_CLOUD', 's3'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Here you may configure as many filesystem "disks" as you wish, and you + | may even configure multiple disks of the same driver. Defaults have + | been setup for each driver as an example of the required options. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL') . '/storage', + 'visibility' => 'public', + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_V2BOARD_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + ], + + ], + +]; diff --git a/Xboard/config/hashing.php b/Xboard/config/hashing.php new file mode 100644 index 0000000..9146bfd --- /dev/null +++ b/Xboard/config/hashing.php @@ -0,0 +1,52 @@ + 'bcrypt', + + /* + |-------------------------------------------------------------------------- + | Bcrypt Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Bcrypt algorithm. This will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'bcrypt' => [ + 'rounds' => env('BCRYPT_ROUNDS', 10), + ], + + /* + |-------------------------------------------------------------------------- + | Argon Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'argon' => [ + 'memory' => 8192, + 'threads' => 2, + 'time' => 2, + ], + +]; diff --git a/Xboard/config/hidden_features.php b/Xboard/config/hidden_features.php new file mode 100644 index 0000000..b676bed --- /dev/null +++ b/Xboard/config/hidden_features.php @@ -0,0 +1,5 @@ + (env('ENABLE_EXPOSED_USER_COUNT_FIX') === base64_decode('M2YwNmYxODI=')) +]; \ No newline at end of file diff --git a/Xboard/config/horizon.php b/Xboard/config/horizon.php new file mode 100644 index 0000000..fde7487 --- /dev/null +++ b/Xboard/config/horizon.php @@ -0,0 +1,228 @@ +getParser(); + +return [ + + /* + |-------------------------------------------------------------------------- + | Horizon Domain + |-------------------------------------------------------------------------- + | + | This is the subdomain where Horizon will be accessible from. If this + | setting is null, Horizon will reside under the same domain as the + | application. Otherwise, this value will serve as the subdomain. + | + */ + + 'domain' => null, + + /* + |-------------------------------------------------------------------------- + | Horizon Path + |-------------------------------------------------------------------------- + | + | This is the URI path where Horizon will be accessible from. Feel free + | to change this path to anything you like. Note that the URI will not + | affect the paths of its internal API that aren't exposed to users. + | + */ + + 'path' => 'monitor', + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Connection + |-------------------------------------------------------------------------- + | + | This is the name of the Redis connection where Horizon will store the + | meta information required for it to function. It includes the list + | of supervisors, failed jobs, job metrics, and other information. + | + */ + + 'use' => 'default', + + /* + |-------------------------------------------------------------------------- + | Horizon Redis Prefix + |-------------------------------------------------------------------------- + | + | This prefix will be used when storing all Horizon data in Redis. You + | may modify the prefix when you are running multiple installations + | of Horizon on the same server so that they don't have problems. + | + */ + + 'prefix' => env( + 'HORIZON_PREFIX', + Str::slug(env('APP_NAME', 'laravel'), '_') . '_horizon:' + ), + + /* + |-------------------------------------------------------------------------- + | Horizon Route Middleware + |-------------------------------------------------------------------------- + | + | These middleware will get attached onto each Horizon route, giving you + | the chance to add your own middleware to this list or change any of + | the existing middleware. Or, you can simply stick with this list. + | + */ + + 'middleware' => ['admin'], + + /* + |-------------------------------------------------------------------------- + | Queue Wait Time Thresholds + |-------------------------------------------------------------------------- + | + | This option allows you to configure when the LongWaitDetected event + | will be fired. Every connection / queue combination may have its + | own, unique threshold (in seconds) before this event is fired. + | + */ + + 'waits' => [ + 'redis:default' => 60, + ], + + /* + |-------------------------------------------------------------------------- + | Job Trimming Times + |-------------------------------------------------------------------------- + | + | Here you can configure for how long (in minutes) you desire Horizon to + | persist the recent and failed jobs. Typically, recent jobs are kept + | for one hour while all failed jobs are stored for an entire week. + | + */ + + 'trim' => [ + 'recent' => 60, + 'pending' => 60, + 'completed' => 60, + 'recent_failed' => 10080, + 'failed' => 10080, + 'monitored' => 10080, + ], + + /* + |-------------------------------------------------------------------------- + | Metrics + |-------------------------------------------------------------------------- + | + | Here you can configure how many snapshots should be kept to display in + | the metrics graph. This will get used in combination with Horizon's + | `horizon:snapshot` schedule to define how long to retain metrics. + | + */ + + 'metrics' => [ + 'trim_snapshots' => [ + 'job' => 24, + 'queue' => 24, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Fast Termination + |-------------------------------------------------------------------------- + | + | When this option is enabled, Horizon's "terminate" command will not + | wait on all of the workers to terminate unless the --wait option + | is provided. Fast termination can shorten deployment delay by + | allowing a new instance of Horizon to start while the last + | instance will continue to terminate each of its workers. + | + */ + + 'fast_termination' => false, + + /* + |-------------------------------------------------------------------------- + | Memory Limit (MB) + |-------------------------------------------------------------------------- + | + | This value describes the maximum amount of memory the Horizon worker + | may consume before it is terminated and restarted. You should set + | this value according to the resources available to your server. + | + */ + + 'memory_limit' => 256, + + /* + |-------------------------------------------------------------------------- + | Queue Worker Configuration + |-------------------------------------------------------------------------- + | + | Here you may define the queue worker settings used by your application + | in all environments. These supervisors and settings handle all your + | queued jobs and will be provisioned by Horizon during deployment. + | + */ + + 'environments' => [ + 'production' => [ + 'data-pipeline' => [ + 'connection' => 'redis', + 'queue' => ['traffic_fetch', 'stat', 'user_alive_sync'], + 'balance' => 'auto', + 'autoScalingStrategy' => 'time', + 'minProcesses' => 1, + 'maxProcesses' => 8, + 'balanceCooldown' => 1, + 'tries' => 3, + 'timeout' => 30, + ], + 'business' => [ + 'connection' => 'redis', + 'queue' => ['default', 'order_handle'], + 'balance' => 'simple', + 'minProcesses' => 1, + 'maxProcesses' => 3, + 'tries' => 3, + 'timeout' => 30, + ], + 'notification' => [ + 'connection' => 'redis', + 'queue' => ['send_email', 'send_telegram', 'send_email_mass', 'node_sync'], + 'balance' => 'auto', + 'autoScalingStrategy' => 'size', + 'minProcesses' => 1, + 'maxProcesses' => 3, + 'tries' => 3, + 'timeout' => 60, + 'backoff' => [3, 10, 30], + ], + ], + 'local' => [ + 'Xboard' => [ + 'connection' => 'redis', + 'queue' => [ + 'default', + 'order_handle', + 'traffic_fetch', + 'stat', + 'send_email', + 'send_email_mass', + 'send_telegram', + 'user_alive_sync', + 'node_sync' + ], + 'balance' => 'auto', + 'minProcesses' => 1, + 'maxProcesses' => 5, + 'tries' => 1, + 'timeout' => 60, + 'balanceCooldown' => 3, + ], + ], + ], +]; diff --git a/Xboard/config/logging.php b/Xboard/config/logging.php new file mode 100644 index 0000000..7ec1268 --- /dev/null +++ b/Xboard/config/logging.php @@ -0,0 +1,64 @@ + env('LOG_CHANNEL', 'daily'), + + 'channels' => [ + 'stack' => [ + 'driver' => 'stack', + 'channels' => ['daily'], + 'ignore_exceptions' => false, + ], + + 'backup' => [ + 'driver' => 'single', + 'path' => storage_path('logs/backup.log'), + 'level' => 'debug', + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => 14, + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'with' => [ + 'stream' => 'php://stderr', + ], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + ], + + 'deprecations' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/deprecations.log'), + 'level' => 'debug', + 'days' => 14, + ], + ], + +]; diff --git a/Xboard/config/mail.php b/Xboard/config/mail.php new file mode 100644 index 0000000..3c65eb3 --- /dev/null +++ b/Xboard/config/mail.php @@ -0,0 +1,136 @@ + env('MAIL_DRIVER', 'smtp'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Address + |-------------------------------------------------------------------------- + | + | Here you may provide the host address of the SMTP server used by your + | applications. A default option is provided that is compatible with + | the Mailgun mail service which will provide reliable deliveries. + | + */ + + 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), + + /* + |-------------------------------------------------------------------------- + | SMTP Host Port + |-------------------------------------------------------------------------- + | + | This is the SMTP port used by your application to deliver e-mails to + | users of the application. Like the host we have set this value to + | stay compatible with the Mailgun e-mail application by default. + | + */ + + 'port' => env('MAIL_PORT', 587), + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + + /* + |-------------------------------------------------------------------------- + | E-Mail Encryption Protocol + |-------------------------------------------------------------------------- + | + | Here you may specify the encryption protocol that should be used when + | the application send e-mail messages. A sensible default using the + | transport layer security protocol should provide great security. + | + */ + + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + + /* + |-------------------------------------------------------------------------- + | SMTP Server Username + |-------------------------------------------------------------------------- + | + | If your SMTP server requires a username for authentication, you should + | set it here. This will get used to authenticate with your server on + | connection. You may also set the "password" value below this one. + | + */ + + 'username' => env('MAIL_USERNAME'), + + 'password' => env('MAIL_PASSWORD'), + + /* + |-------------------------------------------------------------------------- + | Sendmail System Path + |-------------------------------------------------------------------------- + | + | When using the "sendmail" driver to send e-mails, we will need to know + | the path to where Sendmail lives on this server. A default path has + | been provided here, which will work well on most of your systems. + | + */ + + 'sendmail' => '/usr/sbin/sendmail -bs', + + /* + |-------------------------------------------------------------------------- + | Markdown Mail Settings + |-------------------------------------------------------------------------- + | + | If you are using Markdown based email rendering, you may configure your + | theme and component paths here, allowing you to customize the design + | of the emails. Or, you may simply stick with the Laravel defaults! + | + */ + + 'markdown' => [ + 'theme' => 'default', + + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Log Channel + |-------------------------------------------------------------------------- + | + | If you are using the "log" driver, you may specify the logging channel + | if you prefer to keep mail messages separate from other log entries + | for simpler reading. Otherwise, the default channel will be used. + | + */ + + 'log_channel' => env('MAIL_LOG_CHANNEL'), + +]; diff --git a/Xboard/config/octane.php b/Xboard/config/octane.php new file mode 100644 index 0000000..01190fb --- /dev/null +++ b/Xboard/config/octane.php @@ -0,0 +1,221 @@ + env('OCTANE_SERVER', 'swoole'), + + /* + |-------------------------------------------------------------------------- + | Force HTTPS + |-------------------------------------------------------------------------- + | + | When this configuration value is set to "true", Octane will inform the + | framework that all absolute links must be generated using the HTTPS + | protocol. Otherwise your links may be generated using plain HTTP. + | + */ + + 'https' => env('OCTANE_HTTPS', false), + + /* + |-------------------------------------------------------------------------- + | Octane Listeners + |-------------------------------------------------------------------------- + | + | All of the event listeners for Octane's events are defined below. These + | listeners are responsible for resetting your application's state for + | the next request. You may even add your own listeners to the list. + | + */ + + 'listeners' => [ + WorkerStarting::class => [ + EnsureUploadedFilesAreValid::class, + EnsureUploadedFilesCanBeMoved::class, + ], + + RequestReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + ...Octane::prepareApplicationForNextRequest(), + // + ], + + RequestHandled::class => [ + // + ], + + RequestTerminated::class => [ + FlushUploadedFiles::class, + ], + + TaskReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + // + ], + + TaskTerminated::class => [ + // + ], + + TickReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + // + ], + + TickTerminated::class => [ + // + ], + + OperationTerminated::class => [ + FlushTemporaryContainerInstances::class, + DisconnectFromDatabases::class, + CollectGarbage::class, + ], + + WorkerErrorOccurred::class => [ + ReportException::class, + StopWorkerIfNecessary::class, + ], + + WorkerStopping::class => [ + // + ], + ], + + /* + |-------------------------------------------------------------------------- + | Warm / Flush Bindings + |-------------------------------------------------------------------------- + | + | The bindings listed below will either be pre-warmed when a worker boots + | or they will be flushed before every new request. Flushing a binding + | will force the container to resolve that binding again when asked. + | + */ + + 'warm' => [ + ...Octane::defaultServicesToWarm(), + ], + + 'flush' => [ + \App\Services\Plugin\HookManager::class, + ], + + /* + |-------------------------------------------------------------------------- + | Octane Cache Table + |-------------------------------------------------------------------------- + | + | While using Swoole, you may leverage the Octane cache, which is powered + | by a Swoole table. You may set the maximum number of rows as well as + | the number of bytes per row using the configuration options below. + | + */ + + 'cache' => [ + 'rows' => 5000, + 'bytes' => 20000, + ], + + /* + |-------------------------------------------------------------------------- + | Octane Swoole Tables + |-------------------------------------------------------------------------- + | + | While using Swoole, you may define additional tables as required by the + | application. These tables can be used to store data that needs to be + | quickly accessed by other workers on the particular Swoole server. + | + */ + + 'tables' => [ + 'example:1000' => [ + 'name' => 'string:1000', + 'votes' => 'int', + ], + ], + + /* + |-------------------------------------------------------------------------- + | File Watching + |-------------------------------------------------------------------------- + | + | The following list of files and directories will be watched when using + | the --watch option offered by Octane. If any of the directories and + | files are changed, Octane will automatically reload your workers. + | + */ + + 'watch' => [ + 'app', + 'bootstrap', + 'config', + 'database', + 'public/**/*.php', + 'resources/**/*.php', + 'routes', + 'composer.lock', + '.env', + ], + + /* + |-------------------------------------------------------------------------- + | Garbage Collection Threshold + |-------------------------------------------------------------------------- + | + | When executing long-lived PHP scripts such as Octane, memory can build + | up before being cleared by PHP. You can force Octane to run garbage + | collection if your application consumes this amount of megabytes. + | + */ + + 'garbage' => 128, + + /* + |-------------------------------------------------------------------------- + | Maximum Execution Time + |-------------------------------------------------------------------------- + | + | The following setting configures the maximum execution time for requests + | being handled by Octane. You may set this value to 0 to indicate that + | there isn't a specific time limit on Octane request execution time. + | + */ + + 'max_execution_time' => 60, + +]; diff --git a/Xboard/config/queue.php b/Xboard/config/queue.php new file mode 100644 index 0000000..495c858 --- /dev/null +++ b/Xboard/config/queue.php @@ -0,0 +1,88 @@ + env('QUEUE_CONNECTION', 'sync'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'table' => 'jobs', + 'queue' => 'default', + 'retry_after' => 90, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + 'retry_after' => 90, + 'block_for' => 0, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'your-queue-name'), + 'region' => env('AWS_V2BOARD_REGION', 'us-east-1'), + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => 90, + 'block_for' => null, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database'), + 'database' => env('DB_CONNECTION', 'mysql'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/Xboard/config/sanctum.php b/Xboard/config/sanctum.php new file mode 100644 index 0000000..35d75b3 --- /dev/null +++ b/Xboard/config/sanctum.php @@ -0,0 +1,83 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, + 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, + ], + +]; diff --git a/Xboard/config/scribe.php b/Xboard/config/scribe.php new file mode 100644 index 0000000..b2e0c03 --- /dev/null +++ b/Xboard/config/scribe.php @@ -0,0 +1,270 @@ + for the generated documentation. If this is empty, Scribe will infer it from config('app.name'). + 'title' => null, + + // A short description of your API. Will be included in the docs webpage, Postman collection and OpenAPI spec. + 'description' => '', + + // The base URL displayed in the docs. If this is empty, Scribe will use the value of config('app.url') at generation time. + // If you're using `laravel` type, you can set this to a dynamic string, like '{{ config("app.tenant_url") }}' to get a dynamic base URL. + 'base_url' => null, + + 'routes' => [ + [ + // Routes that match these conditions will be included in the docs + 'match' => [ + // Match only routes whose paths match this pattern (use * as a wildcard to match any characters). Example: 'users/*'. + 'prefixes' => ['api/*'], + + // Match only routes whose domains match this pattern (use * as a wildcard to match any characters). Example: 'api.*'. + 'domains' => ['*'], + + // [Dingo router only] Match only routes registered under this version. Wildcards are NOT supported. + 'versions' => ['v1'], + ], + + // Include these routes even if they did not match the rules above. + 'include' => [ + // 'users.index', 'POST /new', '/auth/*' + ], + + // Exclude these routes even if they matched the rules above. + 'exclude' => [ + // 'GET /health', 'admin.*' + ], + ], + ], + + // The type of documentation output to generate. + // - "static" will generate a static HTMl page in the /public/docs folder, + // - "laravel" will generate the documentation as a Blade view, so you can add routing and authentication. + // - "external_static" and "external_laravel" do the same as above, but generate a basic template, + // passing the OpenAPI spec as a URL, allowing you to easily use the docs with an external generator + 'type' => 'static', + + // See https://scribe.knuckles.wtf/laravel/reference/config#theme for supported options + 'theme' => 'default', + + 'static' => [ + // HTML documentation, assets and Postman collection will be generated to this folder. + // Source Markdown will still be in resources/docs. + 'output_path' => 'public/docs', + ], + + 'laravel' => [ + // Whether to automatically create a docs endpoint for you to view your generated docs. + // If this is false, you can still set up routing manually. + 'add_routes' => true, + + // URL path to use for the docs endpoint (if `add_routes` is true). + // By default, `/docs` opens the HTML page, `/docs.postman` opens the Postman collection, and `/docs.openapi` the OpenAPI spec. + 'docs_url' => '/docs', + + // Directory within `public` in which to store CSS and JS assets. + // By default, assets are stored in `public/vendor/scribe`. + // If set, assets will be stored in `public/{{assets_directory}}` + 'assets_directory' => null, + + // Middleware to attach to the docs endpoint (if `add_routes` is true). + 'middleware' => [], + ], + + 'external' => [ + 'html_attributes' => [] + ], + + 'try_it_out' => [ + // Add a Try It Out button to your endpoints so consumers can test endpoints right from their browser. + // Don't forget to enable CORS headers for your endpoints. + 'enabled' => true, + + // The base URL for the API tester to use (for example, you can set this to your staging URL). + // Leave as null to use the current app URL when generating (config("app.url")). + 'base_url' => null, + + // [Laravel Sanctum] Fetch a CSRF token before each request, and add it as an X-XSRF-TOKEN header. + 'use_csrf' => false, + + // The URL to fetch the CSRF token from (if `use_csrf` is true). + 'csrf_url' => '/sanctum/csrf-cookie', + ], + + // How is your API authenticated? This information will be used in the displayed docs, generated examples and response calls. + 'auth' => [ + // Set this to true if ANY endpoints in your API use authentication. + 'enabled' => false, + + // Set this to true if your API should be authenticated by default. If so, you must also set `enabled` (above) to true. + // You can then use @unauthenticated or @authenticated on individual endpoints to change their status from the default. + 'default' => false, + + // Where is the auth value meant to be sent in a request? + // Options: query, body, basic, bearer, header (for custom header) + 'in' => 'bearer', + + // The name of the auth parameter (e.g. token, key, apiKey) or header (e.g. Authorization, Api-Key). + 'name' => 'key', + + // The value of the parameter to be used by Scribe to authenticate response calls. + // This will NOT be included in the generated documentation. If empty, Scribe will use a random value. + 'use_value' => env('SCRIBE_AUTH_KEY'), + + // Placeholder your users will see for the auth parameter in the example requests. + // Set this to null if you want Scribe to use a random value as placeholder instead. + 'placeholder' => '{YOUR_AUTH_KEY}', + + // Any extra authentication-related info for your users. Markdown and HTML are supported. + 'extra_info' => 'You can retrieve your token by visiting your dashboard and clicking Generate API token.', + ], + + // Text to place in the "Introduction" section, right after the `description`. Markdown and HTML are supported. + 'intro_text' => <<As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile). +You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile). +INTRO + , + + // Example requests for each endpoint will be shown in each of these languages. + // Supported options are: bash, javascript, php, python + // To add a language of your own, see https://scribe.knuckles.wtf/laravel/advanced/example-requests + 'example_languages' => [ + 'bash', + 'javascript', + ], + + // Generate a Postman collection (v2.1.0) in addition to HTML docs. + // For 'static' docs, the collection will be generated to public/docs/collection.json. + // For 'laravel' docs, it will be generated to storage/app/scribe/collection.json. + // Setting `laravel.add_routes` to true (above) will also add a route for the collection. + 'postman' => [ + 'enabled' => true, + + 'overrides' => [ + // 'info.version' => '2.0.0', + ], + ], + + // Generate an OpenAPI spec (v3.0.1) in addition to docs webpage. + // For 'static' docs, the collection will be generated to public/docs/openapi.yaml. + // For 'laravel' docs, it will be generated to storage/app/scribe/openapi.yaml. + // Setting `laravel.add_routes` to true (above) will also add a route for the spec. + 'openapi' => [ + 'enabled' => true, + + 'overrides' => [ + // 'info.version' => '2.0.0', + ], + ], + + 'groups' => [ + // Endpoints which don't have a @group will be placed in this default group. + 'default' => 'Endpoints', + + // By default, Scribe will sort groups alphabetically, and endpoints in the order their routes are defined. + // You can override this by listing the groups, subgroups and endpoints here in the order you want them. + // See https://scribe.knuckles.wtf/blog/laravel-v4#easier-sorting and https://scribe.knuckles.wtf/laravel/reference/config#order for details + 'order' => [], + ], + + // Custom logo path. This will be used as the value of the src attribute for the tag, + // so make sure it points to an accessible URL or path. Set to false to not use a logo. + // For example, if your logo is in public/img: + // - 'logo' => '../img/logo.png' // for `static` type (output folder is public/docs) + // - 'logo' => 'img/logo.png' // for `laravel` type + 'logo' => false, + + // Customize the "Last updated" value displayed in the docs by specifying tokens and formats. + // Examples: + // - {date:F j Y} => March 28, 2022 + // - {git:short} => Short hash of the last Git commit + // Available tokens are `{date:}` and `{git:}`. + // The format you pass to `date` will be passed to PHP's `date()` function. + // The format you pass to `git` can be either "short" or "long". + 'last_updated' => 'Last updated: {date:F j, Y}', + + 'examples' => [ + // Set this to any number (e.g. 1234) to generate the same example values for parameters on each run, + 'faker_seed' => null, + + // With API resources and transformers, Scribe tries to generate example models to use in your API responses. + // By default, Scribe will try the model's factory, and if that fails, try fetching the first from the database. + // You can reorder or remove strategies here. + 'models_source' => ['factoryCreate', 'factoryMake', 'databaseFirst'], + ], + + // The strategies Scribe will use to extract information about your routes at each stage. + // If you create or install a custom strategy, add it here. + 'strategies' => [ + 'metadata' => [ + Strategies\Metadata\GetFromDocBlocks::class, + Strategies\Metadata\GetFromMetadataAttributes::class, + ], + 'urlParameters' => [ + Strategies\UrlParameters\GetFromLaravelAPI::class, + Strategies\UrlParameters\GetFromUrlParamAttribute::class, + Strategies\UrlParameters\GetFromUrlParamTag::class, + ], + 'queryParameters' => [ + Strategies\QueryParameters\GetFromFormRequest::class, + Strategies\QueryParameters\GetFromInlineValidator::class, + Strategies\QueryParameters\GetFromQueryParamAttribute::class, + Strategies\QueryParameters\GetFromQueryParamTag::class, + ], + 'headers' => [ + Strategies\Headers\GetFromHeaderAttribute::class, + Strategies\Headers\GetFromHeaderTag::class, + [ + 'override', + [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + ] + ] + ], + 'bodyParameters' => [ + Strategies\BodyParameters\GetFromFormRequest::class, + Strategies\BodyParameters\GetFromInlineValidator::class, + Strategies\BodyParameters\GetFromBodyParamAttribute::class, + Strategies\BodyParameters\GetFromBodyParamTag::class, + ], + 'responses' => [ + Strategies\Responses\UseResponseAttributes::class, + Strategies\Responses\UseTransformerTags::class, + Strategies\Responses\UseApiResourceTags::class, + Strategies\Responses\UseResponseTag::class, + Strategies\Responses\UseResponseFileTag::class, + [ + Strategies\Responses\ResponseCalls::class, + [ + 'only' => ['GET *'], + // Disable debug mode when generating response calls to avoid error stack traces in responses + 'config' => [ + 'app.debug' => false, + ], + ] + ] + ], + 'responseFields' => [ + Strategies\ResponseFields\GetFromResponseFieldAttribute::class, + Strategies\ResponseFields\GetFromResponseFieldTag::class, + ], + ], + + // For response calls, API resource responses and transformer responses, + // Scribe will try to start database transactions, so no changes are persisted to your database. + // Tell Scribe which connections should be transacted here. If you only use one db connection, you can leave this as is. + 'database_connections_to_transact' => [config('database.default')], + + 'fractal' => [ + // If you are using a custom serializer with league/fractal, you can specify it here. + 'serializer' => null, + ], + + 'routeMatcher' => \Knuckles\Scribe\Matching\RouteMatcher::class, +]; diff --git a/Xboard/config/services.php b/Xboard/config/services.php new file mode 100644 index 0000000..950dc99 --- /dev/null +++ b/Xboard/config/services.php @@ -0,0 +1,33 @@ + [ + 'domain' => env('MAILGUN_DOMAIN'), + 'secret' => env('MAILGUN_SECRET'), + 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), + ], + + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_V2BOARD_REGION', 'us-east-1'), + ], + +]; diff --git a/Xboard/config/session.php b/Xboard/config/session.php new file mode 100644 index 0000000..406d50e --- /dev/null +++ b/Xboard/config/session.php @@ -0,0 +1,199 @@ + env('SESSION_DRIVER', 'file'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 120), + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it is stored. All encryption will be run + | automatically by Laravel and you can use the Session like normal. + | + */ + + 'encrypt' => false, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION', null), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using the "apc", "memcached", or "dynamodb" session drivers you may + | list a cache store that should be used for these sessions. This value + | must match with one of the application's configured cache "stores". + | + */ + + 'store' => env('SESSION_STORE', null), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_') . '_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => env('SESSION_DOMAIN', null), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you if it can not be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE', false), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. You are free to modify this option if needed. + | + */ + + 'http_only' => true, + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | do not enable this as other CSRF protection services are in place. + | + | Supported: "lax", "strict" + | + */ + + 'same_site' => null, + +]; diff --git a/Xboard/config/theme/.gitignore b/Xboard/config/theme/.gitignore new file mode 100644 index 0000000..9b8775c --- /dev/null +++ b/Xboard/config/theme/.gitignore @@ -0,0 +1,2 @@ +*.php +!.gitignore diff --git a/Xboard/config/view.php b/Xboard/config/view.php new file mode 100644 index 0000000..22b8a18 --- /dev/null +++ b/Xboard/config/view.php @@ -0,0 +1,36 @@ + [ + resource_path('views'), + ], + + /* + |-------------------------------------------------------------------------- + | Compiled View Path + |-------------------------------------------------------------------------- + | + | This option determines where all the compiled Blade templates will be + | stored for your application. Typically, this is within the storage + | directory. However, as usual, you are free to change this value. + | + */ + + 'compiled' => env( + 'VIEW_COMPILED_PATH', + realpath(storage_path('framework/views')) + ), + +]; diff --git a/Xboard/database/.gitignore b/Xboard/database/.gitignore new file mode 100644 index 0000000..97fc976 --- /dev/null +++ b/Xboard/database/.gitignore @@ -0,0 +1,2 @@ +*.sqlite +*.sqlite-journal diff --git a/Xboard/database/factories/UserFactory.php b/Xboard/database/factories/UserFactory.php new file mode 100644 index 0000000..741edea --- /dev/null +++ b/Xboard/database/factories/UserFactory.php @@ -0,0 +1,28 @@ +define(User::class, function (Faker $faker) { + return [ + 'name' => $faker->name, + 'email' => $faker->unique()->safeEmail, + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; +}); diff --git a/Xboard/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/Xboard/database/migrations/2019_08_19_000000_create_failed_jobs_table.php new file mode 100644 index 0000000..510e6e7 --- /dev/null +++ b/Xboard/database/migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -0,0 +1,37 @@ +bigIncrements('id'); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('failed_jobs'); + } +} diff --git a/Xboard/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/Xboard/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php new file mode 100644 index 0000000..542f19b --- /dev/null +++ b/Xboard/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -0,0 +1,34 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/Xboard/database/migrations/2023_03_19_000000_create_v2_tables.php b/Xboard/database/migrations/2023_03_19_000000_create_v2_tables.php new file mode 100644 index 0000000..6fa7080 --- /dev/null +++ b/Xboard/database/migrations/2023_03_19_000000_create_v2_tables.php @@ -0,0 +1,496 @@ +integer('id', true); + $table->integer('invite_user_id'); + $table->integer('user_id'); + $table->char('trade_no', 36); + $table->integer('order_amount'); + $table->integer('get_amount'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Invite Code + if (!Schema::hasTable('v2_invite_code')) { + Schema::create('v2_invite_code', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('user_id'); + $table->char('code', 32); + $table->boolean('status')->default(false); + $table->integer('pv')->default(0); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Knowledge + if (!Schema::hasTable('v2_knowledge')) { + Schema::create('v2_knowledge', function (Blueprint $table) { + $table->integer('id', true); + $table->char('language', 5)->comment('語言'); + $table->string('category')->comment('分類名'); + $table->string('title')->comment('標題'); + $table->text('body')->comment('內容'); + $table->integer('sort')->nullable()->comment('排序'); + $table->boolean('show')->default(false)->comment('顯示'); + $table->integer('created_at')->comment('創建時間'); + $table->integer('updated_at')->comment('更新時間'); + }); + } + + // Plan + if (!Schema::hasTable('v2_plan')) { + Schema::create('v2_plan', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('group_id'); + $table->integer('transfer_enable'); + $table->string('name'); + $table->integer('speed_limit')->nullable(); + $table->boolean('show')->default(false); + $table->integer('sort')->nullable(); + $table->boolean('renew')->default(true); + $table->text('content')->nullable(); + $table->integer('month_price')->nullable(); + $table->integer('quarter_price')->nullable(); + $table->integer('half_year_price')->nullable(); + $table->integer('year_price')->nullable(); + $table->integer('two_year_price')->nullable(); + $table->integer('three_year_price')->nullable(); + $table->integer('onetime_price')->nullable(); + $table->integer('reset_price')->nullable(); + $table->integer('reset_traffic_method')->nullable()->comment('重置流量方式:null跟随系统设置、0每月1号、1按月重置、2不重置、3每年1月1日、4按年重置'); + $table->integer('capacity_limit')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Server Group + if (!Schema::hasTable('v2_server_group')) { + Schema::create('v2_server_group', function (Blueprint $table) { + $table->integer('id', true); + $table->string('name'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Server Route + if (!Schema::hasTable('v2_server_route')) { + Schema::create('v2_server_route', function (Blueprint $table) { + $table->integer('id', true); + $table->string('remarks'); + $table->text('match'); + $table->string('action', 11); + $table->string('action_value')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // stat server + if (!Schema::hasTable('v2_stat_server')) { + Schema::create('v2_stat_server', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('server_id')->index('server_id')->comment('节点id'); + $table->char('server_type', 11)->comment('节点类型'); + $table->bigInteger('u'); + $table->bigInteger('d'); + $table->char('record_type', 1)->comment('d day m month'); + $table->integer('record_at')->index('record_at')->comment('记录时间'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->unique(['server_id', 'server_type', 'record_at'], 'server_id_server_type_record_at'); + }); + } + + // User + if (!Schema::hasTable('v2_user')) { + Schema::create('v2_user', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('invite_user_id')->nullable(); + $table->bigInteger('telegram_id')->nullable(); + $table->string('email', 64)->unique('email'); + $table->string('password', 64); + $table->char('password_algo', 10)->nullable(); + $table->char('password_salt', 10)->nullable(); + $table->integer('balance')->default(0); + $table->integer('discount')->nullable(); + $table->tinyInteger('commission_type')->default(0)->comment('0: system 1: period 2: onetime'); + $table->integer('commission_rate')->nullable(); + $table->integer('commission_balance')->default(0); + $table->integer('t')->default(0); + $table->bigInteger('u')->default(0); + $table->bigInteger('d')->default(0); + $table->bigInteger('transfer_enable')->default(0); + $table->boolean('banned')->default(false); + $table->boolean('is_admin')->default(false); + $table->integer('last_login_at')->nullable(); + $table->boolean('is_staff')->default(false); + $table->integer('last_login_ip')->nullable(); + $table->string('uuid', 36); + $table->integer('group_id')->nullable(); + $table->integer('plan_id')->nullable(); + $table->integer('speed_limit')->nullable(); + $table->tinyInteger('remind_expire')->nullable()->default(1); + $table->tinyInteger('remind_traffic')->nullable()->default(1); + $table->char('token', 32); + $table->bigInteger('expired_at')->nullable()->default(0); + $table->text('remarks')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Mail Log + if (!Schema::hasTable('v2_mail_log')) { + Schema::create('v2_mail_log', function (Blueprint $table) { + $table->integer('id', true); + $table->string('email', 64); + $table->string('subject'); + $table->string('template_name'); + $table->text('error')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Log + if (!Schema::hasTable('v2_log')) { + Schema::create('v2_log', function (Blueprint $table) { + $table->integer('id', true); + $table->text('title'); + $table->string('level', 11)->nullable(); + $table->string('host')->nullable(); + $table->string('uri'); + $table->string('method', 11); + $table->text('data')->nullable(); + $table->string('ip', 128)->nullable(); + $table->text('context')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Stat + if (!Schema::hasTable('v2_stat')) { + Schema::create('v2_stat', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('record_at'); + $table->char('record_type', 1); + $table->integer('order_count')->comment('订单数量'); + $table->integer('order_total')->comment('订单合计'); + $table->integer('commission_count'); + $table->integer('commission_total')->comment('佣金合计'); + $table->integer('paid_count'); + $table->integer('paid_total'); + $table->integer('register_count'); + $table->integer('invite_count'); + $table->string('transfer_used_total', 32); + $table->integer('created_at'); + $table->integer('updated_at'); + + if (config('database.default') !== 'sqlite') { + $table->unique(['record_at']); + } + }); + } + + // stat user + if (!Schema::hasTable('v2_stat_user')) { + Schema::create('v2_stat_user', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('user_id'); + $table->decimal('server_rate', 10); + $table->bigInteger('u'); + $table->bigInteger('d'); + $table->char('record_type', 2); + $table->integer('record_at'); + $table->integer('created_at'); + $table->integer('updated_at'); + + // 如果是不是sqlite才添加多个索引 + if (config('database.default') !== 'sqlite') { + $table->index(['user_id', 'server_rate', 'record_at']); + $table->unique(['server_rate', 'user_id', 'record_at'], 'server_rate_user_id_record_at'); + } + }); + } + + // ticket message + if (!Schema::hasTable('v2_ticket_message')) { + Schema::create('v2_ticket_message', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('user_id'); + $table->integer('ticket_id'); + $table->text('message'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Order + if (!Schema::hasTable('v2_order')) { + Schema::create('v2_order', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('invite_user_id')->nullable(); + $table->integer('user_id'); + $table->integer('plan_id'); + $table->integer('coupon_id')->nullable(); + $table->integer('payment_id')->nullable(); + $table->integer('type')->comment('1新购2续费3升级'); + $table->string('period'); + $table->string('trade_no', 36)->unique('trade_no'); + $table->string('callback_no')->nullable(); + $table->integer('total_amount'); + $table->integer('handling_amount')->nullable(); + $table->integer('discount_amount')->nullable(); + $table->integer('surplus_amount')->nullable()->comment('剩余价值'); + $table->integer('refund_amount')->nullable()->comment('退款金额'); + $table->integer('balance_amount')->nullable()->comment('使用余额'); + $table->text('surplus_order_ids')->nullable()->comment('折抵订单'); + $table->integer('status')->default(0)->comment('0待支付1开通中2已取消3已完成4已折抵'); + $table->integer('commission_status')->default(false)->comment('0待确认1发放中2有效3无效'); + $table->integer('commission_balance')->default(0); + $table->integer('actual_commission_balance')->nullable()->comment('实际支付佣金'); + $table->integer('paid_at')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Payment + if (!Schema::hasTable('v2_payment')) { + Schema::create('v2_payment', function (Blueprint $table) { + $table->integer('id', true); + $table->char('uuid', 32); + $table->string('payment', 16); + $table->string('name'); + $table->string('icon')->nullable(); + $table->text('config'); + $table->string('notify_domain', 128)->nullable(); + $table->integer('handling_fee_fixed')->nullable(); + $table->decimal('handling_fee_percent', 5)->nullable(); + $table->boolean('enable')->default(false); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Coupon + if (!Schema::hasTable('v2_coupon')) { + Schema::create('v2_coupon', function (Blueprint $table) { + $table->integer('id', true); + $table->string('code'); + $table->string('name'); + $table->integer('type'); + $table->integer('value'); + $table->boolean('show')->default(false); + $table->integer('limit_use')->nullable(); + $table->integer('limit_use_with_user')->nullable(); + $table->string('limit_plan_ids')->nullable(); + $table->string('limit_period')->nullable(); + $table->integer('started_at'); + $table->integer('ended_at'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Notice + if (!Schema::hasTable('v2_notice')) { + Schema::create('v2_notice', function (Blueprint $table) { + $table->integer('id', true); + $table->string('title'); + $table->text('content'); + $table->boolean('show')->default(false); + $table->string('img_url')->nullable(); + $table->string('tags')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Ticket + if (!Schema::hasTable('v2_ticket')) { + Schema::create('v2_ticket', function (Blueprint $table) { + $table->integer('id', true); + $table->integer('user_id'); + $table->string('subject'); + $table->integer('level'); + $table->integer('status')->default(0)->comment('0:已开启 1:已关闭'); + $table->integer('reply_status')->default(1)->comment('0:待回复 1:已回复'); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Server Hysteria + if (!Schema::hasTable('v2_server_hysteria')) { + Schema::create('v2_server_hysteria', function (Blueprint $table) { + $table->integer('id', true); + $table->string('group_id'); + $table->string('route_id')->nullable(); + $table->string('name'); + $table->integer('parent_id')->nullable(); + $table->string('host'); + $table->string('port', 11); + $table->integer('server_port'); + $table->string('tags')->nullable(); + $table->string('rate', 11); + $table->boolean('show')->default(false); + $table->integer('sort')->nullable(); + $table->integer('up_mbps'); + $table->integer('down_mbps'); + $table->string('server_name', 64)->nullable(); + $table->boolean('insecure')->default(false); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Server Shadowsocks + if (!Schema::hasTable('v2_server_shadowsocks')) { + autoIncrement: + Schema::create('v2_server_shadowsocks', function (Blueprint $table) { + $table->integer('id', true); + $table->string('group_id'); + $table->string('route_id')->nullable(); + $table->integer('parent_id')->nullable(); + $table->string('tags')->nullable(); + $table->string('name'); + $table->string('rate', 11); + $table->string('host'); + $table->string('port', 11); + $table->integer('server_port'); + $table->string('cipher'); + $table->char('obfs', 11)->nullable(); + $table->string('obfs_settings')->nullable(); + $table->tinyInteger('show')->default(0); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + // Server Trojan + if (!Schema::hasTable('v2_server_trojan')) { + Schema::create('v2_server_trojan', function (Blueprint $table) { + $table->integer('id', true)->comment('节点ID'); + $table->string('group_id')->comment('节点组'); + $table->string('route_id')->nullable(); + $table->integer('parent_id')->nullable()->comment('父节点'); + $table->string('tags')->nullable()->comment('节点标签'); + $table->string('name')->comment('节点名称'); + $table->string('rate', 11)->comment('倍率'); + $table->string('host')->comment('主机名'); + $table->string('port', 11)->comment('连接端口'); + $table->integer('server_port')->comment('服务端口'); + $table->boolean('allow_insecure')->default(false)->comment('是否允许不安全'); + $table->string('server_name')->nullable(); + $table->boolean('show')->default(false)->comment('是否显示'); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Server Vless + if (!Schema::hasTable('v2_server_vless')) { + Schema::create('v2_server_vless', function (Blueprint $table) { + $table->integer('id', true); + $table->text('group_id'); + $table->text('route_id')->nullable(); + $table->string('name'); + $table->integer('parent_id')->nullable(); + $table->string('host'); + $table->integer('port'); + $table->integer('server_port'); + $table->integer('tls'); + $table->text('tls_settings')->nullable(); + $table->string('flow', 64)->nullable(); + $table->string('network', 11); + $table->text('network_settings')->nullable(); + $table->text('tags')->nullable(); + $table->string('rate', 11); + $table->boolean('show')->default(false); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + // Server Vmess + if (!Schema::hasTable('v2_server_vmess')) { + Schema::create('v2_server_vmess', function (Blueprint $table) { + $table->integer('id', true); + $table->string('group_id'); + $table->string('route_id')->nullable(); + $table->string('name'); + $table->integer('parent_id')->nullable(); + $table->string('host'); + $table->string('port', 11); + $table->integer('server_port'); + $table->tinyInteger('tls')->default(0); + $table->string('tags')->nullable(); + $table->string('rate', 11); + $table->string('network', 11); + $table->text('rules')->nullable(); + $table->text('networkSettings')->nullable(); + $table->text('tlsSettings')->nullable(); + $table->text('ruleSettings')->nullable(); + $table->text('dnsSettings')->nullable(); + $table->boolean('show')->default(false); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + } + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('v2_commission_log'); + Schema::dropIfExists('v2_plan'); + Schema::dropIfExists('v2_user'); + Schema::dropIfExists('v2_mail_log'); + Schema::dropIfExists('v2_log'); + Schema::dropIfExists('v2_stat'); + Schema::dropIfExists('v2_order'); + Schema::dropIfExists('v2_coupon'); + Schema::dropIfExists('v2_notice'); + Schema::dropIfExists('v2_ticket'); + Schema::dropIfExists('v2_settings'); + Schema::dropIfExists('v2_ticket_message'); + Schema::dropIfExists('v2_invite_code'); + Schema::dropIfExists('v2_knowledge'); + Schema::dropIfExists('v2_server_group'); + Schema::dropIfExists('v2_server_route'); + Schema::dropIfExists('v2_stat_server'); + Schema::dropIfExists('v2_stat_user'); + Schema::dropIfExists('v2_server_hysteria'); + Schema::dropIfExists('v2_server_shadowsocks'); + Schema::dropIfExists('v2_server_trojan'); + Schema::dropIfExists('v2_server_vless'); + Schema::dropIfExists('v2_server_vmess'); + } +}; diff --git a/Xboard/database/migrations/2023_08_14_221234_create_v2_settings_table.php b/Xboard/database/migrations/2023_08_14_221234_create_v2_settings_table.php new file mode 100644 index 0000000..b6e788b --- /dev/null +++ b/Xboard/database/migrations/2023_08_14_221234_create_v2_settings_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('group')->comment('设置分组')->nullable(); + $table->string('type')->comment('设置类型')->nullable(); + $table->string('name')->comment('设置名称')->unique(); + $table->string('value')->comment('设置值')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('v2_settings'); + } +} diff --git a/Xboard/database/migrations/2023_09_04_190923_add_column_excludes_to_server_table.php b/Xboard/database/migrations/2023_09_04_190923_add_column_excludes_to_server_table.php new file mode 100644 index 0000000..ef1038e --- /dev/null +++ b/Xboard/database/migrations/2023_09_04_190923_add_column_excludes_to_server_table.php @@ -0,0 +1,56 @@ +text("excludes")->nullable()->after('tags'); + }); + Schema::table('v2_server_shadowsocks', function (Blueprint $table) { + $table->text("excludes")->nullable()->after('tags'); + }); + Schema::table('v2_server_trojan', function (Blueprint $table) { + $table->text("excludes")->nullable()->after('tags'); + }); + Schema::table('v2_server_vless', function (Blueprint $table) { + $table->text("excludes")->nullable()->after('tags'); + }); + Schema::table('v2_server_vmess', function (Blueprint $table) { + $table->text("excludes")->nullable()->after('tags'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('v2_server_hysteria', function (Blueprint $table) { + $table->dropColumn('excludes'); + }); + Schema::table('v2_server_shadowsocks', function (Blueprint $table) { + $table->dropColumn('excludes'); + }); + Schema::table('v2_server_trojan', function (Blueprint $table) { + $table->dropColumn('excludes'); + }); + Schema::table('v2_server_vless', function (Blueprint $table) { + $table->dropColumn('excludes'); + }); + Schema::table('v2_server_vmess', function (Blueprint $table) { + $table->dropColumn('excludes'); + }); + } +} diff --git a/Xboard/database/migrations/2023_09_06_195956_add_column_ips_to_server_table.php b/Xboard/database/migrations/2023_09_06_195956_add_column_ips_to_server_table.php new file mode 100644 index 0000000..be71dc9 --- /dev/null +++ b/Xboard/database/migrations/2023_09_06_195956_add_column_ips_to_server_table.php @@ -0,0 +1,56 @@ +string("ips")->nullable()->after('excludes'); + }); + Schema::table('v2_server_shadowsocks', function (Blueprint $table) { + $table->string("ips")->nullable()->after('excludes'); + }); + Schema::table('v2_server_trojan', function (Blueprint $table) { + $table->string("ips")->nullable()->after('excludes'); + }); + Schema::table('v2_server_vless', function (Blueprint $table) { + $table->string("ips")->nullable()->after('excludes'); + }); + Schema::table('v2_server_vmess', function (Blueprint $table) { + $table->string("ips")->nullable()->after('excludes'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('v2_server_hysteria', function (Blueprint $table) { + $table->dropColumn('ips'); + }); + Schema::table('v2_server_shadowsocks', function (Blueprint $table) { + $table->dropColumn('ips'); + }); + Schema::table('v2_server_trojan', function (Blueprint $table) { + $table->dropColumn('ips'); + }); + Schema::table('v2_server_vless', function (Blueprint $table) { + $table->dropColumn('ips'); + }); + Schema::table('v2_server_vmess', function (Blueprint $table) { + $table->dropColumn('ips'); + }); + } +} diff --git a/Xboard/database/migrations/2023_09_14_013244_add_column_alpn_to_server_hysteria_table.php b/Xboard/database/migrations/2023_09_14_013244_add_column_alpn_to_server_hysteria_table.php new file mode 100644 index 0000000..332666f --- /dev/null +++ b/Xboard/database/migrations/2023_09_14_013244_add_column_alpn_to_server_hysteria_table.php @@ -0,0 +1,32 @@ +tinyInteger('alpn',false,true)->default(0)->comment('ALPN,0:hysteria、1:http/1.1、2:h2、3:h3'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('v2_server_hysteria', function (Blueprint $table) { + $table->dropColumn('alpn'); + }); + } +} diff --git a/Xboard/database/migrations/2023_09_24_040317_add_column_network_and_network_settings_to_v2_server_trojan.php b/Xboard/database/migrations/2023_09_24_040317_add_column_network_and_network_settings_to_v2_server_trojan.php new file mode 100644 index 0000000..529cf68 --- /dev/null +++ b/Xboard/database/migrations/2023_09_24_040317_add_column_network_and_network_settings_to_v2_server_trojan.php @@ -0,0 +1,33 @@ +string('network', 11)->default('tcp')->after('server_name')->comment('传输协议'); + $table->text('networkSettings')->nullable()->after('network')->comment('传输协议配置'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('v2_server_trojan', function (Blueprint $table) { + $table->dropColumn(["network","networkSettings"]); + }); + } +} diff --git a/Xboard/database/migrations/2023_09_29_044957_add_column_version_and_is_obfs_to_server_hysteria_table.php b/Xboard/database/migrations/2023_09_29_044957_add_column_version_and_is_obfs_to_server_hysteria_table.php new file mode 100644 index 0000000..c2f7762 --- /dev/null +++ b/Xboard/database/migrations/2023_09_29_044957_add_column_version_and_is_obfs_to_server_hysteria_table.php @@ -0,0 +1,33 @@ +tinyInteger('version',false,true)->default(1)->comment('hysteria版本,Version:1\2'); + $table->boolean('is_obfs')->default(true)->comment('是否开启obfs'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('v2_server_hysteria', function (Blueprint $table) { + $table->dropColumn('version','is_obfs'); + }); + } +} diff --git a/Xboard/database/migrations/2023_11_19_205026_change_column_value_to_v2_settings_table.php b/Xboard/database/migrations/2023_11_19_205026_change_column_value_to_v2_settings_table.php new file mode 100644 index 0000000..97ad5d6 --- /dev/null +++ b/Xboard/database/migrations/2023_11_19_205026_change_column_value_to_v2_settings_table.php @@ -0,0 +1,28 @@ +text('value')->comment('设置值')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_settings', function (Blueprint $table) { + $table->string('value')->comment('设置值')->nullable()->change(); + }); + } +}; diff --git a/Xboard/database/migrations/2023_12_12_212239_add_index_to_v2_user_table.php b/Xboard/database/migrations/2023_12_12_212239_add_index_to_v2_user_table.php new file mode 100644 index 0000000..b06cbd3 --- /dev/null +++ b/Xboard/database/migrations/2023_12_12_212239_add_index_to_v2_user_table.php @@ -0,0 +1,28 @@ +index(['u','d','expired_at','group_id','banned','transfer_enable']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_user', function (Blueprint $table) { + $table->dropIndex(['u','d','expired_at','group_id','banned','transfer_enable']); + }); + } +}; diff --git a/Xboard/database/migrations/2024_03_19_103149_modify_icon_column_to_v2_payment_table.php b/Xboard/database/migrations/2024_03_19_103149_modify_icon_column_to_v2_payment_table.php new file mode 100644 index 0000000..3d129e7 --- /dev/null +++ b/Xboard/database/migrations/2024_03_19_103149_modify_icon_column_to_v2_payment_table.php @@ -0,0 +1,28 @@ +text('icon')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_payment', function (Blueprint $table) { + $table->string('icon')->nullable()->change(); + }); + } +}; diff --git a/Xboard/database/migrations/2025_01_01_130644_modify_commission_status_in_v2_order_table.php b/Xboard/database/migrations/2025_01_01_130644_modify_commission_status_in_v2_order_table.php new file mode 100644 index 0000000..916e226 --- /dev/null +++ b/Xboard/database/migrations/2025_01_01_130644_modify_commission_status_in_v2_order_table.php @@ -0,0 +1,28 @@ +integer('commission_status')->nullable()->default(null)->comment('0待确认1发放中2有效3无效')->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_order', function (Blueprint $table) { + $table->integer('commission_status')->default(false)->comment('0待确认1发放中2有效3无效')->change(); + }); + } +}; diff --git a/Xboard/database/migrations/2025_01_04_optimize_plan_table.php b/Xboard/database/migrations/2025_01_04_optimize_plan_table.php new file mode 100644 index 0000000..d6b1f47 --- /dev/null +++ b/Xboard/database/migrations/2025_01_04_optimize_plan_table.php @@ -0,0 +1,129 @@ +json('prices')->nullable()->after('name') + ->comment('Store different duration prices and reset traffic price'); + $table->boolean('sell')->default(false)->after('prices')->comment('is sell'); + }); + + // Step 2: Migrate data to new format + DB::table('v2_plan')->orderBy('id')->chunk(100, function ($plans) { + foreach ($plans as $plan) { + $prices = array_filter([ + 'monthly' => $plan->month_price !== null ? $plan->month_price / 100 : null, + 'quarterly' => $plan->quarter_price !== null ? $plan->quarter_price / 100 : null, + 'half_yearly' => $plan->half_year_price !== null ? $plan->half_year_price / 100 : null, + 'yearly' => $plan->year_price !== null ? $plan->year_price / 100 : null, + 'two_yearly' => $plan->two_year_price !== null ? $plan->two_year_price / 100 : null, + 'three_yearly' => $plan->three_year_price !== null ? $plan->three_year_price / 100 : null, + 'onetime' => $plan->onetime_price !== null ? $plan->onetime_price / 100 : null, + 'reset_traffic' => $plan->reset_price !== null ? $plan->reset_price / 100 : null + ], function ($price) { + return $price !== null; + }); + + DB::table('v2_plan') + ->where('id', $plan->id) + ->update([ + 'prices' => json_encode($prices), + 'sell' => $plan->show + ]); + } + }); + + // Step 3: Optimize existing columns + Schema::table('v2_plan', function (Blueprint $table) { + // Modify existing columns to be more efficient + $table->unsignedInteger('group_id')->nullable()->change(); + $table->unsignedBigInteger('transfer_enable')->nullable() + ->comment('Transfer limit in bytes')->change(); + $table->unsignedInteger('speed_limit')->nullable() + ->comment('Speed limit in Mbps, 0 for unlimited')->change(); + $table->integer('reset_traffic_method')->nullable()->default(0) + ->comment('重置流量方式:null跟随系统设置、0每月1号、1按月重置、2不重置、3每年1月1日、4按年重置')->change(); + $table->unsignedInteger('capacity_limit')->nullable()->default(0) + ->comment('0 for unlimited')->change(); + }); + + // Step 4: Drop old columns + Schema::table('v2_plan', function (Blueprint $table) { + $table->dropColumn([ + 'month_price', + 'quarter_price', + 'half_year_price', + 'year_price', + 'two_year_price', + 'three_year_price', + 'onetime_price', + 'reset_price', + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Step 1: Add back old columns + Schema::table('v2_plan', function (Blueprint $table) { + $table->integer('month_price')->nullable(); + $table->integer('quarter_price')->nullable(); + $table->integer('half_year_price')->nullable(); + $table->integer('year_price')->nullable(); + $table->integer('two_year_price')->nullable(); + $table->integer('three_year_price')->nullable(); + $table->integer('onetime_price')->nullable(); + $table->integer('reset_price')->nullable(); + }); + + // Step 2: Restore data from new format to old format + DB::table('v2_plan')->orderBy('id')->chunk(100, function ($plans) { + foreach ($plans as $plan) { + $prices = json_decode($plan->prices, true) ?? []; + + DB::table('v2_plan') + ->where('id', $plan->id) + ->update([ + 'month_price' => $prices['monthly'] * 100 ?? null, + 'quarter_price' => $prices['quarterly'] * 100 ?? null, + 'half_year_price' => $prices['half_yearly'] * 100 ?? null, + 'year_price' => $prices['yearly'] * 100 ?? null, + 'two_year_price' => $prices['two_yearly'] * 100 ?? null, + 'three_year_price' => $prices['three_yearly'] * 100 ?? null, + 'onetime_price' => $prices['onetime'] * 100 ?? null, + 'reset_price' => $prices['reset_traffic'] * 100 ?? null, + ]); + } + }); + + // Step 3: Drop new columns + Schema::table('v2_plan', function (Blueprint $table) { + $table->dropColumn([ + 'prices', + 'sell' + ]); + }); + + // Step 4: Restore column types to original + Schema::table('v2_plan', function (Blueprint $table) { + $table->integer('group_id')->change(); + $table->integer('transfer_enable')->change(); + $table->integer('speed_limit')->nullable()->change(); + $table->integer('reset_traffic_method')->nullable()->change(); + $table->integer('capacity_limit')->nullable()->change(); + }); + } +}; \ No newline at end of file diff --git a/Xboard/database/migrations/2025_01_05_131425_create_v2_server_table.php b/Xboard/database/migrations/2025_01_05_131425_create_v2_server_table.php new file mode 100644 index 0000000..f8fb84b --- /dev/null +++ b/Xboard/database/migrations/2025_01_05_131425_create_v2_server_table.php @@ -0,0 +1,523 @@ +id('id'); + $table->string('type')->comment('Server Type'); + $table->string('code')->nullable()->comment('Server Spectific Key'); + $table->unsignedInteger('parent_id')->nullable()->comment('Parent Server ID'); + $table->json('group_ids')->nullable()->comment('Group ID'); + $table->json('route_ids')->nullable()->comment('Route ID'); + $table->string('name')->comment('Server Name'); + $table->decimal('rate', 8, 2)->comment('Traffic Rate'); + $table->json('tags')->nullable()->comment('Server Tags'); + $table->string('host')->comment('Server Host'); + $table->string('port')->comment('Client Port'); + $table->integer('server_port')->comment('Server Port'); + $table->json('protocol_settings')->nullable(); + $table->boolean('show')->default(false)->comment('Show in List'); + $table->integer('sort')->nullable()->unsigned()->index(); + $table->timestamps(); + $table->unique(['type', 'code']); + }); + + // Migrate Trojan servers + $trojanServers = DB::table('v2_server_trojan')->get(); + foreach ($trojanServers as $server) { + DB::table('v2_server')->insert([ + 'type' => 'trojan', + 'code' => (string) $server->id, + 'parent_id' => $server->parent_id, + 'group_ids' => $server->group_id ?: "[]", + 'route_ids' => $server->route_id ?: "[]", + 'name' => $server->name, + 'rate' => $server->rate, + 'tags' => $server->tags ?: "[]", + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'protocol_settings' => json_encode([ + 'allow_insecure' => $server->allow_insecure, + 'server_name' => $server->server_name, + 'network' => $server->network, + 'network_settings' => $server->networkSettings + ]), + 'show' => $server->show, + 'sort' => $server->sort, + 'created_at' => date('Y-m-d H:i:s', $server->created_at), + 'updated_at' => date('Y-m-d H:i:s', $server->updated_at) + ]); + } + + // Migrate VMess servers + $vmessServers = DB::table('v2_server_vmess')->get(); + foreach ($vmessServers as $server) { + DB::table('v2_server')->insert([ + 'type' => 'vmess', + 'code' => (string) $server->id, + 'parent_id' => $server->parent_id, + 'group_ids' => $server->group_id ?: "[]", + 'route_ids' => $server->route_id ?: "[]", + 'name' => $server->name, + 'rate' => $server->rate, + 'tags' => $server->tags ?: "[]", + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'protocol_settings' => json_encode([ + 'tls' => $server->tls, + 'network' => $server->network, + 'rules' => json_decode($server->rules), + 'network_settings' => json_decode($server->networkSettings), + 'tls_settings' => json_decode($server->tlsSettings), + ]), + 'show' => $server->show, + 'sort' => $server->sort, + 'created_at' => date('Y-m-d H:i:s', $server->created_at), + 'updated_at' => date('Y-m-d H:i:s', $server->updated_at) + ]); + } + + // Migrate VLESS servers + $vlessServers = DB::table('v2_server_vless')->get(); + foreach ($vlessServers as $server) { + $tlsSettings = optional(json_decode($server->tls_settings)); + DB::table('v2_server')->insert([ + 'type' => 'vless', + 'code' => (string) $server->id, + 'parent_id' => $server->parent_id, + 'group_ids' => $server->group_id ?: "[]", + 'route_ids' => $server->route_id ?: "[]", + 'name' => $server->name, + 'rate' => $server->rate, + 'tags' => $server->tags ?: "[]", + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'protocol_settings' => json_encode([ + 'tls' => $server->tls, + 'tls_settings' => $tlsSettings, + 'flow' => $server->flow, + 'network' => $server->network, + 'network_settings' => json_decode($server->network_settings), + 'reality_settings' => ($tlsSettings && $tlsSettings->public_key && $tlsSettings->short_id && $tlsSettings->server_name) ? [ + 'public_key' => $tlsSettings->public_key, + 'short_id' => $tlsSettings->short_id, + 'server_name' => $tlsSettings->server_name, + 'server_port' => $tlsSettings->server_port, + 'private_key' => $tlsSettings->private_key, + ] : null + ]), + 'show' => $server->show, + 'sort' => $server->sort, + 'created_at' => date('Y-m-d H:i:s', $server->created_at), + 'updated_at' => date('Y-m-d H:i:s', $server->updated_at) + ]); + } + + // Migrate Shadowsocks servers + $ssServers = DB::table('v2_server_shadowsocks')->get(); + foreach ($ssServers as $server) { + DB::table('v2_server')->insert([ + 'type' => 'shadowsocks', + 'code' => (string) $server->id, + 'parent_id' => $server->parent_id, + 'group_ids' => $server->group_id ?: "[]", + 'route_ids' => $server->route_id ?: "[]", + 'name' => $server->name, + 'rate' => $server->rate, + 'tags' => $server->tags ?: "[]", + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'protocol_settings' => json_encode([ + 'cipher' => $server->cipher, + 'obfs' => $server->obfs, + 'obfs_settings' => json_decode($server->obfs_settings) + ]), + 'show' => (bool) $server->show, + 'sort' => $server->sort, + 'created_at' => date('Y-m-d H:i:s', $server->created_at), + 'updated_at' => date('Y-m-d H:i:s', $server->updated_at) + ]); + } + + // Migrate Hysteria servers + $hysteriaServers = DB::table(table: 'v2_server_hysteria')->get(); + foreach ($hysteriaServers as $server) { + DB::table('v2_server')->insert([ + 'type' => 'hysteria', + 'code' => (string) $server->id, + 'parent_id' => $server->parent_id, + 'group_ids' => $server->group_id ?: "[]", + 'route_ids' => $server->route_id ?: "[]", + 'name' => $server->name, + 'rate' => $server->rate, + 'tags' => $server->tags ?: "[]", + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'protocol_settings' => json_encode([ + 'version' => $server->version, + 'bandwidth' => [ + 'up' => $server->up_mbps, + 'down' => $server->down_mbps, + ], + 'obfs' => [ + 'open' => $server->is_obfs, + 'type' => 'salamander', + 'password' => Helper::getServerKey($server->created_at, 16), + ], + 'tls' => [ + 'server_name' => $server->server_name, + 'allow_insecure' => $server->insecure + ] + ]), + 'show' => $server->show, + 'sort' => $server->sort, + 'created_at' => date('Y-m-d H:i:s', $server->created_at), + 'updated_at' => date('Y-m-d H:i:s', $server->updated_at) + ]); + } + + // Update parent_id for all servers + $this->updateParentIds(); + + // Drop old tables + Schema::dropIfExists('v2_server_trojan'); + Schema::dropIfExists('v2_server_vmess'); + Schema::dropIfExists('v2_server_vless'); + Schema::dropIfExists('v2_server_shadowsocks'); + Schema::dropIfExists('v2_server_hysteria'); + } + + /** + * Update parent_id references for all servers + */ + private function updateParentIds(): void + { + // Get all servers that have a parent_id + $servers = DB::table('v2_server') + ->whereNotNull('parent_id') + ->get(); + + // Update each server's parent_id to reference the new table's id + foreach ($servers as $server) { + $parentId = DB::table('v2_server') + ->where('type', $server->type) + ->where('code', $server->parent_id) + ->value('id'); + + if ($parentId) { + DB::table('v2_server') + ->where('id', $server->id) + ->update(['parent_id' => $parentId]); + } + } + } + + /** + * Restore parent_id references when rolling back + */ + private function restoreParentIds(string $type, string $table): void + { + // Get all servers of the specified type that have a parent_id + $servers = DB::table($table) + ->whereNotNull('parent_id') + ->get(); + + // Update each server's parent_id to reference back to the original id + foreach ($servers as $server) { + $originalParentId = DB::table('v2_server') + ->where('type', $type) + ->where('id', $server->parent_id) + ->value('code'); + + if ($originalParentId) { + DB::table($table) + ->where('id', $server->id) + ->update(['parent_id' => $originalParentId]); + } + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Recreate old tables + Schema::create('v2_server_trojan', function (Blueprint $table) { + $table->integer('id', true)->comment('节点ID'); + $table->string('group_id')->comment('节点组'); + $table->string('route_id')->nullable(); + $table->string('ips')->nullable(); + $table->string('excludes')->nullable(); + $table->integer('parent_id')->nullable()->comment('父节点'); + $table->string('tags')->nullable()->comment('节点标签'); + $table->string('name')->comment('节点名称'); + $table->string('rate', 11)->comment('倍率'); + $table->string('host')->comment('主机名'); + $table->string('port', 11)->comment('连接端口'); + $table->integer('server_port')->comment('服务端口'); + $table->boolean('allow_insecure')->default(false)->comment('是否允许不安全'); + $table->string('server_name')->nullable(); + $table->string('network')->nullable(); + $table->text('networkSettings')->nullable(); + $table->boolean('show')->default(false)->comment('是否显示'); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('v2_server_vmess', function (Blueprint $table) { + $table->integer('id', true); + $table->string('group_id'); + $table->string('route_id')->nullable(); + $table->string('ips')->nullable(); + $table->string('excludes')->nullable(); + $table->string('name'); + $table->integer('parent_id')->nullable(); + $table->string('host'); + $table->string('port', 11); + $table->integer('server_port'); + $table->tinyInteger('tls')->default(0); + $table->string('tags')->nullable(); + $table->string('rate', 11); + $table->string('network', 11); + $table->text('rules')->nullable(); + $table->text('networkSettings')->nullable(); + $table->text('tlsSettings')->nullable(); + $table->boolean('show')->default(false); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('v2_server_vless', function (Blueprint $table) { + $table->integer('id', true); + $table->text('group_id'); + $table->text('route_id')->nullable(); + $table->string('ips')->nullable(); + $table->string('excludes')->nullable(); + $table->string('name'); + $table->integer('parent_id')->nullable(); + $table->string('host'); + $table->integer('port'); + $table->integer('server_port'); + $table->boolean('tls'); + $table->text('tls_settings')->nullable(); + $table->string('flow', 64)->nullable(); + $table->string('network', 11); + $table->text('network_settings')->nullable(); + $table->text('tags')->nullable(); + $table->string('rate', 11); + $table->boolean('show')->default(false); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('v2_server_shadowsocks', function (Blueprint $table) { + $table->integer('id', true); + $table->string('group_id'); + $table->string('route_id')->nullable(); + $table->string('ips')->nullable(); + $table->string('excludes')->nullable(); + $table->integer('parent_id')->nullable(); + $table->string('tags')->nullable(); + $table->string('name'); + $table->string('rate', 11); + $table->string('host'); + $table->string('port', 11); + $table->integer('server_port'); + $table->string('cipher'); + $table->char('obfs', 11)->nullable(); + $table->string('obfs_settings')->nullable(); + $table->tinyInteger('show')->default(0); + $table->integer('sort')->nullable(); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + Schema::create('v2_server_hysteria', function (Blueprint $table) { + $table->integer('id', true); + $table->string('group_id'); + $table->string('route_id')->nullable(); + $table->string('ips')->nullable(); + $table->string('excludes')->nullable(); + $table->string('name'); + $table->integer('parent_id')->nullable(); + $table->string('host'); + $table->string('port', 11); + $table->integer('server_port'); + $table->string('tags')->nullable(); + $table->string('rate', 11); + $table->boolean('show')->default(false); + $table->integer('sort')->nullable(); + $table->tinyInteger('version', false, true)->default(1)->comment('hysteria版本,Version:1\2'); + $table->boolean('is_obfs')->default(true)->comment('是否开启obfs'); + $table->string('alpn')->nullable(); + $table->integer('up_mbps'); + $table->integer('down_mbps'); + $table->string('server_name', 64)->nullable(); + $table->boolean('insecure')->default(false); + $table->integer('created_at'); + $table->integer('updated_at'); + }); + + // Migrate data back to old tables + $servers = DB::table('v2_server')->get(); + foreach ($servers as $server) { + $settings = json_decode($server->protocol_settings, true); + $timestamp = strtotime($server->created_at); + $updated = strtotime($server->updated_at); + switch ($server->type) { + case 'trojan': + DB::table('v2_server_trojan')->insert([ + 'id' => (int) $server->code, + 'group_id' => $server->group_ids, + 'route_id' => $server->route_ids, + 'parent_id' => $server->parent_id, + 'tags' => $server->tags, + 'name' => $server->name, + 'rate' => (string) $server->rate, + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'allow_insecure' => $settings['allow_insecure'], + 'server_name' => $settings['server_name'], + 'network' => $settings['network'] ?? null, + 'networkSettings' => $settings['network_settings'] ?? null, + 'show' => $server->show, + 'sort' => $server->sort, + 'created_at' => $timestamp, + 'updated_at' => $updated + ]); + break; + case 'vmess': + DB::table('v2_server_vmess')->insert([ + 'id' => (int) $server->code, + 'group_id' => $server->group_ids, + 'route_id' => $server->route_ids, + 'name' => $server->name, + 'parent_id' => $server->parent_id, + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'tls' => $settings['tls'], + 'tags' => $server->tags, + 'rate' => (string) $server->rate, + 'network' => $settings['network'], + 'rules' => json_encode($settings['rules']), + 'networkSettings' => json_encode($settings['network_settings']), + 'tlsSettings' => json_encode($settings['tls_settings']), + 'show' => $server->show, + 'sort' => $server->sort, + 'created_at' => $timestamp, + 'updated_at' => $updated + ]); + break; + case 'vless': + // 处理 reality settings + $tlsSettings = $settings['tls_settings'] ?? new \stdClass(); + if (isset($settings['reality_settings'])) { + $tlsSettings = array_merge((array) $tlsSettings, [ + 'public_key' => $settings['reality_settings']['public_key'], + 'short_id' => $settings['reality_settings']['short_id'], + 'server_name' => explode(':', $settings['reality_settings']['dest'])[0], + 'server_port' => explode(':', $settings['reality_settings']['dest'])[1] ?? null, + 'private_key' => $settings['reality_settings']['private_key'] + ]); + } + + DB::table('v2_server_vless')->insert([ + 'id' => (int) $server->code, + 'group_id' => $server->group_ids, + 'route_id' => $server->route_ids, + 'name' => $server->name, + 'parent_id' => $server->parent_id, + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'tls' => $settings['tls'], + 'tls_settings' => json_encode($tlsSettings), + 'flow' => $settings['flow'], + 'network' => $settings['network'], + 'network_settings' => json_encode($settings['network_settings']), + 'tags' => $server->tags, + 'rate' => (string) $server->rate, + 'show' => $server->show, + 'sort' => $server->sort, + 'created_at' => $timestamp, + 'updated_at' => $updated + ]); + break; + case 'shadowsocks': + DB::table('v2_server_shadowsocks')->insert([ + 'id' => (int) $server->code, + 'group_id' => $server->group_ids, + 'route_id' => $server->route_ids, + 'parent_id' => $server->parent_id, + 'tags' => $server->tags, + 'name' => $server->name, + 'rate' => (string) $server->rate, + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'cipher' => $settings['cipher'], + 'obfs' => $settings['obfs'], + 'obfs_settings' => json_encode($settings['obfs_settings']), + 'show' => (int) $server->show, + 'sort' => $server->sort, + 'created_at' => $timestamp, + 'updated_at' => $updated + ]); + break; + case 'hysteria': + DB::table('v2_server_hysteria')->insert([ + 'id' => (int) $server->code, + 'group_id' => $server->group_ids, + 'route_id' => $server->route_ids, + 'name' => $server->name, + 'parent_id' => $server->parent_id, + 'host' => $server->host, + 'port' => $server->port, + 'server_port' => $server->server_port, + 'tags' => $server->tags, + 'rate' => (string) $server->rate, + 'show' => $server->show, + 'sort' => $server->sort, + 'up_mbps' => $settings['bandwidth']['up'], + 'down_mbps' => $settings['bandwidth']['down'], + 'server_name' => $settings['tls']['server_name'], + 'insecure' => $settings['tls']['allow_insecure'], + 'created_at' => $timestamp, + 'updated_at' => $updated + ]); + break; + } + } + + // Restore parent_id references for each server type + $this->restoreParentIds('trojan', 'v2_server_trojan'); + $this->restoreParentIds('vmess', 'v2_server_vmess'); + $this->restoreParentIds('vless', 'v2_server_vless'); + $this->restoreParentIds('shadowsocks', 'v2_server_shadowsocks'); + $this->restoreParentIds('hysteria', 'v2_server_hysteria'); + + // Drop new table + Schema::dropIfExists('v2_server'); + } +}; diff --git a/Xboard/database/migrations/2025_01_10_152139_add_device_limit_column.php b/Xboard/database/migrations/2025_01_10_152139_add_device_limit_column.php new file mode 100644 index 0000000..8015d10 --- /dev/null +++ b/Xboard/database/migrations/2025_01_10_152139_add_device_limit_column.php @@ -0,0 +1,31 @@ +unsignedInteger('device_limit')->nullable()->after('speed_limit'); + }); + Schema::table('v2_user', function (Blueprint $table) { + $table->integer('device_limit')->nullable()->after('expired_at'); + $table->integer('online_count')->nullable()->after('device_limit'); + $table->timestamp('last_online_at')->nullable()->after('online_count'); + }); + } + + public function down(): void + { + Schema::table('v2_user', function (Blueprint $table) { + $table->dropColumn('device_limit'); + $table->dropColumn('online_count'); + $table->dropColumn('last_online_at'); + }); + Schema::table('v2_plan', function (Blueprint $table) { + $table->dropColumn('device_limit'); + }); + } +}; diff --git a/Xboard/database/migrations/2025_01_12_190315_add_sort_to_v2_notice_table.php b/Xboard/database/migrations/2025_01_12_190315_add_sort_to_v2_notice_table.php new file mode 100644 index 0000000..9cbef66 --- /dev/null +++ b/Xboard/database/migrations/2025_01_12_190315_add_sort_to_v2_notice_table.php @@ -0,0 +1,30 @@ +integer('sort')->nullable()->after('id')->index(); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_notice', function (Blueprint $table) { + $table->dropColumn('sort'); + }); + } +}; diff --git a/Xboard/database/migrations/2025_01_12_200936_modify_commission_status_in_v2_order_table.php b/Xboard/database/migrations/2025_01_12_200936_modify_commission_status_in_v2_order_table.php new file mode 100644 index 0000000..668ef17 --- /dev/null +++ b/Xboard/database/migrations/2025_01_12_200936_modify_commission_status_in_v2_order_table.php @@ -0,0 +1,33 @@ +where('commission_status', null)->update([ + 'commission_status' => 0 + ]); + Schema::table('v2_order', function (Blueprint $table) { + $table->integer('commission_status')->default(value: 0)->comment('0待确认1发放中2有效3无效')->change(); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_order', function (Blueprint $table) { + $table->integer('commission_status')->nullable()->comment('0待确认1发放中2有效3无效')->change(); + }); + } +}; diff --git a/Xboard/database/migrations/2025_01_13_000000_convert_order_period_fields.php b/Xboard/database/migrations/2025_01_13_000000_convert_order_period_fields.php new file mode 100644 index 0000000..dc6718c --- /dev/null +++ b/Xboard/database/migrations/2025_01_13_000000_convert_order_period_fields.php @@ -0,0 +1,56 @@ + 'monthly', + 'quarter_price' => 'quarterly', + 'half_year_price' => 'half_yearly', + 'year_price' => 'yearly', + 'two_year_price' => 'two_yearly', + 'three_year_price' => 'three_yearly', + 'onetime_price' => 'onetime', + 'reset_price' => 'reset_traffic' + ]; + + /** + * Run the migrations. + */ + public function up(): void + { + // 批量更新订单的周期字段 + foreach (self::PERIOD_MAPPING as $oldPeriod => $newPeriod) { + DB::table('v2_order') + ->where('period', $oldPeriod) + ->update(['period' => $newPeriod]); + } + + // 检查是否还有未转换的记录 + $unconvertedCount = DB::table('v2_order') + ->whereNotIn('period', array_values(self::PERIOD_MAPPING)) + ->count(); + + if ($unconvertedCount > 0) { + Log::warning("Found {$unconvertedCount} orders with unconverted period values"); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // 回滚操作 - 将新的周期值转换回旧的价格字段名 + foreach (self::PERIOD_MAPPING as $oldPeriod => $newPeriod) { + DB::table('v2_order') + ->where('period', $newPeriod) + ->update(['period' => $oldPeriod]); + } + } +}; \ No newline at end of file diff --git a/Xboard/database/migrations/2025_01_15_000002_add_stat_performance_indexes.php b/Xboard/database/migrations/2025_01_15_000002_add_stat_performance_indexes.php new file mode 100644 index 0000000..4640e7e --- /dev/null +++ b/Xboard/database/migrations/2025_01_15_000002_add_stat_performance_indexes.php @@ -0,0 +1,93 @@ +index('t'); + $table->index('online_count'); + $table->index('created_at'); + }); + + Schema::table('v2_order', function (Blueprint $table) { + $table->index('created_at'); + $table->index('status'); + $table->index('total_amount'); + $table->index('commission_status'); + $table->index('invite_user_id'); + $table->index('commission_balance'); + }); + + Schema::table('v2_stat_server', function (Blueprint $table) { + $table->index('server_id'); + $table->index('record_at'); + $table->index('u'); + $table->index('d'); + }); + + Schema::table('v2_stat_user', function (Blueprint $table) { + $table->index('u'); + $table->index('d'); + }); + + Schema::table('v2_commission_log', function (Blueprint $table) { + $table->index('created_at'); + $table->index('get_amount'); + }); + + Schema::table('v2_ticket', function (Blueprint $table) { + $table->index('status'); + $table->index('created_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_user', function (Blueprint $table) { + $table->dropIndex(['t']); + $table->dropIndex(['online_count']); + $table->dropIndex(['created_at']); + }); + + Schema::table('v2_order', function (Blueprint $table) { + $table->dropIndex(['created_at']); + $table->dropIndex(['status']); + $table->dropIndex(['total_amount']); + $table->dropIndex(['commission_status']); + $table->dropIndex(['invite_user_id']); + $table->dropIndex(['commission_balance']); + }); + + Schema::table('v2_stat_server', function (Blueprint $table) { + $table->dropIndex(['server_id']); + $table->dropIndex(['record_at']); + $table->dropIndex(['u']); + $table->dropIndex(['d']); + }); + + Schema::table('v2_stat_user', function (Blueprint $table) { + $table->dropIndex(['u']); + $table->dropIndex(['d']); + }); + + Schema::table('v2_commission_log', function (Blueprint $table) { + $table->dropIndex(['created_at']); + $table->dropIndex(['get_amount']); + }); + + Schema::table('v2_ticket', function (Blueprint $table) { + $table->dropIndex(['status']); + $table->dropIndex(['created_at']); + }); + } +}; \ No newline at end of file diff --git a/Xboard/database/migrations/2025_01_16_142320_add_updated_at_index_to_v2_order_table.php b/Xboard/database/migrations/2025_01_16_142320_add_updated_at_index_to_v2_order_table.php new file mode 100644 index 0000000..5278bbf --- /dev/null +++ b/Xboard/database/migrations/2025_01_16_142320_add_updated_at_index_to_v2_order_table.php @@ -0,0 +1,28 @@ +index('updated_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_order', function (Blueprint $table) { + $table->dropIndex(['updated_at']); + }); + } +}; diff --git a/Xboard/database/migrations/2025_01_18_140511_create_plugins_table.php b/Xboard/database/migrations/2025_01_18_140511_create_plugins_table.php new file mode 100644 index 0000000..be3d070 --- /dev/null +++ b/Xboard/database/migrations/2025_01_18_140511_create_plugins_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('name'); + $table->string('code')->unique(); + $table->string('version', 50); + $table->boolean('is_enabled')->default(false); + $table->json('config')->nullable(); + $table->timestamp('installed_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('v2_plugins'); + } +}; diff --git a/Xboard/database/migrations/2025_06_21_000001_optimize_v2_settings_table.php b/Xboard/database/migrations/2025_06_21_000001_optimize_v2_settings_table.php new file mode 100644 index 0000000..affce15 --- /dev/null +++ b/Xboard/database/migrations/2025_06_21_000001_optimize_v2_settings_table.php @@ -0,0 +1,37 @@ +mediumText('value')->nullable()->change(); + // 添加优化索引 + $table->index('name', 'idx_setting_name'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('v2_settings', function (Blueprint $table) { + $table->string('value')->nullable()->change(); + $table->dropIndex('idx_setting_name'); + }); + } +} \ No newline at end of file diff --git a/Xboard/database/migrations/2025_06_21_000002_create_traffic_reset_logs_table.php b/Xboard/database/migrations/2025_06_21_000002_create_traffic_reset_logs_table.php new file mode 100644 index 0000000..2d12fc4 --- /dev/null +++ b/Xboard/database/migrations/2025_06_21_000002_create_traffic_reset_logs_table.php @@ -0,0 +1,43 @@ +id(); + $table->bigInteger('user_id')->comment('用户ID'); + $table->string('reset_type', 50)->comment('重置类型'); + $table->timestamp('reset_time')->comment('重置时间'); + $table->bigInteger('old_upload')->default(0)->comment('重置前上传流量'); + $table->bigInteger('old_download')->default(0)->comment('重置前下载流量'); + $table->bigInteger('old_total')->default(0)->comment('重置前总流量'); + $table->bigInteger('new_upload')->default(0)->comment('重置后上传流量'); + $table->bigInteger('new_download')->default(0)->comment('重置后下载流量'); + $table->bigInteger('new_total')->default(0)->comment('重置后总流量'); + $table->string('trigger_source', 50)->comment('触发来源'); + $table->json('metadata')->nullable()->comment('额外元数据'); + $table->timestamps(); + + // 添加索引 + $table->index('user_id', 'idx_user_id'); + $table->index('reset_time', 'idx_reset_time'); + $table->index(['user_id', 'reset_time'], 'idx_user_reset_time'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('v2_traffic_reset_logs'); + } +} \ No newline at end of file diff --git a/Xboard/database/migrations/2025_06_21_000003_add_traffic_reset_fields_to_users.php b/Xboard/database/migrations/2025_06_21_000003_add_traffic_reset_fields_to_users.php new file mode 100644 index 0000000..004c666 --- /dev/null +++ b/Xboard/database/migrations/2025_06_21_000003_add_traffic_reset_fields_to_users.php @@ -0,0 +1,39 @@ +integer('next_reset_at')->nullable()->after('expired_at')->comment('下次流量重置时间'); + $table->integer('last_reset_at')->nullable()->after('next_reset_at')->comment('上次流量重置时间'); + $table->integer('reset_count')->default(0)->after('last_reset_at')->comment('流量重置次数'); + $table->index('next_reset_at', 'idx_next_reset_at'); + }); + } + + // Set initial reset time for existing users + Artisan::call('reset:traffic', ['--fix-null' => true]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_user', function (Blueprint $table) { + $table->dropIndex('idx_next_reset_at'); + $table->dropColumn(['next_reset_at', 'last_reset_at', 'reset_count']); + }); + } +} \ No newline at end of file diff --git a/Xboard/database/migrations/2025_07_01_081556_add_tags_to_v2_plan_table.php b/Xboard/database/migrations/2025_07_01_081556_add_tags_to_v2_plan_table.php new file mode 100644 index 0000000..7edf6d7 --- /dev/null +++ b/Xboard/database/migrations/2025_07_01_081556_add_tags_to_v2_plan_table.php @@ -0,0 +1,28 @@ +json('tags')->nullable()->after('content'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_plan', function (Blueprint $table) { + $table->dropColumn('tags'); + }); + } +}; diff --git a/Xboard/database/migrations/2025_07_01_122908_create_gift_card_tables.php b/Xboard/database/migrations/2025_07_01_122908_create_gift_card_tables.php new file mode 100644 index 0000000..290ce00 --- /dev/null +++ b/Xboard/database/migrations/2025_07_01_122908_create_gift_card_tables.php @@ -0,0 +1,98 @@ +id(); + $table->string('name')->comment('礼品卡名称'); + $table->text('description')->nullable()->comment('礼品卡描述'); + $table->tinyInteger('type')->comment('卡片类型:1余额 2有效期 3流量 4重置包 5套餐 6组合 7盲盒 8任务 9等级 10节日'); + $table->tinyInteger('status')->default(1)->comment('状态:0禁用 1启用'); + $table->json('conditions')->nullable()->comment('使用条件配置'); + $table->json('rewards')->comment('奖励配置'); + $table->json('limits')->nullable()->comment('限制条件'); + $table->json('special_config')->nullable()->comment('特殊配置(节日时间、等级倍率等)'); + $table->string('icon')->nullable()->comment('卡片图标'); + $table->string('background_image')->nullable()->comment('背景图片URL'); + $table->string('theme_color', 7)->default('#1890ff')->comment('主题色'); + $table->integer('sort')->default(0)->comment('排序'); + $table->integer('admin_id')->comment('创建管理员ID'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->index(['type', 'status'], 'idx_gift_template_type_status'); + $table->index('created_at', 'idx_gift_template_created_at'); + }); + + // 礼品卡兑换码表 + Schema::create('v2_gift_card_code', function (Blueprint $table) { + $table->id(); + $table->integer('template_id')->comment('模板ID'); + $table->string('code', 32)->unique()->comment('兑换码'); + $table->string('batch_id', 32)->nullable()->comment('批次ID'); + $table->tinyInteger('status')->default(0)->comment('状态:0未使用 1已使用 2已过期 3已禁用'); + $table->integer('user_id')->nullable()->comment('使用用户ID'); + $table->integer('used_at')->nullable()->comment('使用时间'); + $table->integer('expires_at')->nullable()->comment('过期时间'); + $table->json('actual_rewards')->nullable()->comment('实际获得的奖励(用于盲盒等)'); + $table->integer('usage_count')->default(0)->comment('使用次数(分享卡)'); + $table->integer('max_usage')->default(1)->comment('最大使用次数'); + $table->json('metadata')->nullable()->comment('额外数据'); + $table->integer('created_at'); + $table->integer('updated_at'); + + $table->index('template_id', 'idx_gift_code_template_id'); + $table->index('status', 'idx_gift_code_status'); + $table->index('user_id', 'idx_gift_code_user_id'); + $table->index('batch_id', 'idx_gift_code_batch_id'); + $table->index('expires_at', 'idx_gift_code_expires_at'); + $table->index(['code', 'status', 'expires_at'], 'idx_gift_code_lookup'); + }); + + // 礼品卡使用记录表 + Schema::create('v2_gift_card_usage', function (Blueprint $table) { + $table->id(); + $table->integer('code_id')->comment('兑换码ID'); + $table->integer('template_id')->comment('模板ID'); + $table->integer('user_id')->comment('使用用户ID'); + $table->integer('invite_user_id')->nullable()->comment('邀请人ID'); + $table->json('rewards_given')->comment('实际发放的奖励'); + $table->json('invite_rewards')->nullable()->comment('邀请人获得的奖励'); + $table->integer('user_level_at_use')->nullable()->comment('使用时用户等级'); + $table->integer('plan_id_at_use')->nullable()->comment('使用时用户套餐ID'); + $table->decimal('multiplier_applied', 3, 2)->default(1.00)->comment('应用的倍率'); + $table->string('ip_address', 45)->nullable()->comment('使用IP地址'); + $table->text('user_agent')->nullable()->comment('用户代理'); + $table->text('notes')->nullable()->comment('备注'); + $table->integer('created_at'); + + $table->index('code_id', 'idx_gift_usage_code_id'); + $table->index('template_id', 'idx_gift_usage_template_id'); + $table->index('user_id', 'idx_gift_usage_user_id'); + $table->index('invite_user_id', 'idx_gift_usage_invite_user_id'); + $table->index('created_at', 'idx_gift_usage_created_at'); + $table->index(['user_id', 'created_at'], 'idx_gift_usage_user_usage'); + $table->index(['template_id', 'created_at'], 'idx_gift_usage_template_stats'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('v2_gift_card_usage'); + Schema::dropIfExists('v2_gift_card_code'); + Schema::dropIfExists('v2_gift_card_template'); + } +}; diff --git a/Xboard/database/migrations/2025_07_13_224539_add_column_rate_time_ranges_to_v2_server_table.php b/Xboard/database/migrations/2025_07_13_224539_add_column_rate_time_ranges_to_v2_server_table.php new file mode 100644 index 0000000..aa5ad62 --- /dev/null +++ b/Xboard/database/migrations/2025_07_13_224539_add_column_rate_time_ranges_to_v2_server_table.php @@ -0,0 +1,29 @@ +boolean('rate_time_enable')->default(false)->comment('是否启用动态倍率')->after('rate'); + $table->json('rate_time_ranges')->nullable()->comment('动态倍率规则')->after('rate_time_enable'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_server', function (Blueprint $table) { + $table->dropColumn('rate_time_enable'); + $table->dropColumn('rate_time_ranges'); + }); + } +}; diff --git a/Xboard/database/migrations/2025_07_26_000001_add_type_to_plugins_table.php b/Xboard/database/migrations/2025_07_26_000001_add_type_to_plugins_table.php new file mode 100644 index 0000000..b372621 --- /dev/null +++ b/Xboard/database/migrations/2025_07_26_000001_add_type_to_plugins_table.php @@ -0,0 +1,24 @@ +string('type', 20)->default('feature')->after('code')->comment('插件类型:feature功能性,payment支付型'); + $table->index(['type', 'is_enabled']); + }); + } + + public function down(): void + { + Schema::table('v2_plugins', function (Blueprint $table) { + $table->dropIndex(['type', 'is_enabled']); + $table->dropColumn('type'); + }); + } +}; \ No newline at end of file diff --git a/Xboard/database/migrations/2025_07_27_000001_create_v2_subscribe_templates_table.php b/Xboard/database/migrations/2025_07_27_000001_create_v2_subscribe_templates_table.php new file mode 100644 index 0000000..873a3df --- /dev/null +++ b/Xboard/database/migrations/2025_07_27_000001_create_v2_subscribe_templates_table.php @@ -0,0 +1,91 @@ +id(); + $table->string('name')->unique()->comment('Template key, e.g. singbox, clash'); + $table->mediumText('content')->nullable()->comment('Template content'); + $table->timestamps(); + }); + + $this->seedDefaults(); + } + + public function down(): void + { + Schema::dropIfExists('v2_subscribe_templates'); + } + + private function seedDefaults(): void + { + // Fallback order matches original protocol class behavior + $protocols = [ + 'singbox' => [ + 'resources/rules/custom.sing-box.json', + 'resources/rules/default.sing-box.json', + ], + 'clash' => [ + 'resources/rules/custom.clash.yaml', + 'resources/rules/default.clash.yaml', + ], + 'clashmeta' => [ + 'resources/rules/custom.clashmeta.yaml', + 'resources/rules/custom.clash.yaml', + 'resources/rules/default.clash.yaml', + ], + 'stash' => [ + 'resources/rules/custom.stash.yaml', + 'resources/rules/custom.clash.yaml', + 'resources/rules/default.clash.yaml', + ], + 'surge' => [ + 'resources/rules/custom.surge.conf', + 'resources/rules/default.surge.conf', + ], + 'surfboard' => [ + 'resources/rules/custom.surfboard.conf', + 'resources/rules/default.surfboard.conf', + ], + ]; + + foreach ($protocols as $name => $fileFallbacks) { + $existing = DB::table('v2_settings') + ->where('name', "subscribe_template_{$name}") + ->value('value'); + + if ($existing !== null && $existing !== '') { + $content = $existing; + } else { + $content = ''; + foreach ($fileFallbacks as $file) { + $path = base_path($file); + if (File::exists($path)) { + $content = File::get($path); + break; + } + } + } + + DB::table('v2_subscribe_templates')->insert([ + 'name' => $name, + 'content' => $content, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + // Clean up old entries from v2_settings + DB::table('v2_settings') + ->where('name', 'like', 'subscribe_template_%') + ->delete(); + } +}; diff --git a/Xboard/database/migrations/2026_03_11_000001_replace_v2_log_with_admin_audit_log.php b/Xboard/database/migrations/2026_03_11_000001_replace_v2_log_with_admin_audit_log.php new file mode 100644 index 0000000..7f7b587 --- /dev/null +++ b/Xboard/database/migrations/2026_03_11_000001_replace_v2_log_with_admin_audit_log.php @@ -0,0 +1,28 @@ +id(); + $table->unsignedBigInteger('admin_id')->index(); + $table->string('action', 64)->index()->comment('Action identifier e.g. user.update'); + $table->string('method', 10); + $table->string('uri', 512); + $table->text('request_data')->nullable(); + $table->string('ip', 128)->nullable(); + $table->unsignedInteger('created_at'); + $table->unsignedInteger('updated_at'); + }); + } + + public function down(): void + { + Schema::dropIfExists('v2_admin_audit_log'); + } +}; diff --git a/Xboard/database/migrations/2026_03_11_000002_add_stat_user_record_at_index.php b/Xboard/database/migrations/2026_03_11_000002_add_stat_user_record_at_index.php new file mode 100644 index 0000000..2438855 --- /dev/null +++ b/Xboard/database/migrations/2026_03_11_000002_add_stat_user_record_at_index.php @@ -0,0 +1,22 @@ +index(['record_at', 'user_id'], 'idx_stat_user_record_user'); + }); + } + + public function down(): void + { + Schema::table('v2_stat_user', function (Blueprint $table) { + $table->dropIndex('idx_stat_user_record_user'); + }); + } +}; diff --git a/Xboard/database/migrations/2026_03_15_060035_add_custom_config_and_cert_to_v2_server_table.php b/Xboard/database/migrations/2026_03_15_060035_add_custom_config_and_cert_to_v2_server_table.php new file mode 100644 index 0000000..6f67ff7 --- /dev/null +++ b/Xboard/database/migrations/2026_03_15_060035_add_custom_config_and_cert_to_v2_server_table.php @@ -0,0 +1,30 @@ +json('custom_outbounds')->nullable()->after('protocol_settings'); + $table->json('custom_routes')->nullable()->after('custom_outbounds'); + $table->json('cert_config')->nullable()->after('custom_routes'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_server', function (Blueprint $table) { + $table->dropColumn(['custom_outbounds', 'custom_routes', 'cert_config']); + }); + } +}; diff --git a/Xboard/database/migrations/2026_03_28_161536_add_traffic_fields_to_servers.php b/Xboard/database/migrations/2026_03_28_161536_add_traffic_fields_to_servers.php new file mode 100644 index 0000000..ca585b8 --- /dev/null +++ b/Xboard/database/migrations/2026_03_28_161536_add_traffic_fields_to_servers.php @@ -0,0 +1,46 @@ +bigInteger('transfer_enable') + ->default(null) + ->nullable() + ->after('rate') + ->comment('Traffic limit , 0 or null=no limit'); + } + if (!Schema::hasColumn('v2_server', 'u')) { + $table->bigInteger('u') + ->default(0) + ->after('transfer_enable') + ->comment('upload traffic'); + } + if (!Schema::hasColumn('v2_server', 'd')) { + $table->bigInteger('d') + ->default(0) + ->after('u') + ->comment('donwload traffic'); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('v2_server', function (Blueprint $table) { + $table->dropColumn(['transfer_enable', 'u', 'd']); + }); + } +}; diff --git a/Xboard/database/seeders/DatabaseSeeder.php b/Xboard/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..34b89fa --- /dev/null +++ b/Xboard/database/seeders/DatabaseSeeder.php @@ -0,0 +1,17 @@ +call(UsersTableSeeder::class) + } +} diff --git a/Xboard/database/seeders/OriginV2bMigrationsTableSeeder.php b/Xboard/database/seeders/OriginV2bMigrationsTableSeeder.php new file mode 100644 index 0000000..5643bb5 --- /dev/null +++ b/Xboard/database/seeders/OriginV2bMigrationsTableSeeder.php @@ -0,0 +1,170 @@ +insert(array ( + 0 => + array ( + 'id' => 1, + 'migration' => '2019_08_19_000000_create_failed_jobs_table', + 'batch' => 1, + ), + 1 => + array ( + 'id' => 2, + 'migration' => '2023_08_07_205816_create_v2_commission_log_table', + 'batch' => 1, + ), + 2 => + array ( + 'id' => 3, + 'migration' => '2023_08_07_205816_create_v2_coupon_table', + 'batch' => 1, + ), + 3 => + array ( + 'id' => 4, + 'migration' => '2023_08_07_205816_create_v2_invite_code_table', + 'batch' => 1, + ), + 4 => + array ( + 'id' => 5, + 'migration' => '2023_08_07_205816_create_v2_knowledge_table', + 'batch' => 1, + ), + 5 => + array ( + 'id' => 6, + 'migration' => '2023_08_07_205816_create_v2_log_table', + 'batch' => 1, + ), + 6 => + array ( + 'id' => 7, + 'migration' => '2023_08_07_205816_create_v2_mail_log_table', + 'batch' => 1, + ), + 7 => + array ( + 'id' => 8, + 'migration' => '2023_08_07_205816_create_v2_notice_table', + 'batch' => 1, + ), + 8 => + array ( + 'id' => 9, + 'migration' => '2023_08_07_205816_create_v2_order_table', + 'batch' => 1, + ), + 9 => + array ( + 'id' => 10, + 'migration' => '2023_08_07_205816_create_v2_payment_table', + 'batch' => 1, + ), + 10 => + array ( + 'id' => 11, + 'migration' => '2023_08_07_205816_create_v2_plan_table', + 'batch' => 1, + ), + 11 => + array ( + 'id' => 12, + 'migration' => '2023_08_07_205816_create_v2_server_group_table', + 'batch' => 1, + ), + 12 => + array ( + 'id' => 13, + 'migration' => '2023_08_07_205816_create_v2_server_hysteria_table', + 'batch' => 1, + ), + 13 => + array ( + 'id' => 14, + 'migration' => '2023_08_07_205816_create_v2_server_route_table', + 'batch' => 1, + ), + 14 => + array ( + 'id' => 15, + 'migration' => '2023_08_07_205816_create_v2_server_shadowsocks_table', + 'batch' => 1, + ), + 15 => + array ( + 'id' => 16, + 'migration' => '2023_08_07_205816_create_v2_server_trojan_table', + 'batch' => 1, + ), + 16 => + array ( + 'id' => 17, + 'migration' => '2023_08_07_205816_create_v2_server_vless_table', + 'batch' => 1, + ), + 17 => + array ( + 'id' => 18, + 'migration' => '2023_08_07_205816_create_v2_server_vmess_table', + 'batch' => 1, + ), + 18 => + array ( + 'id' => 19, + 'migration' => '2023_08_07_205816_create_v2_stat_server_table', + 'batch' => 1, + ), + 19 => + array ( + 'id' => 20, + 'migration' => '2023_08_07_205816_create_v2_stat_table', + 'batch' => 1, + ), + 20 => + array ( + 'id' => 21, + 'migration' => '2023_08_07_205816_create_v2_stat_user_table', + 'batch' => 1, + ), + 21 => + array ( + 'id' => 22, + 'migration' => '2023_08_07_205816_create_v2_ticket_message_table', + 'batch' => 1, + ), + 22 => + array ( + 'id' => 23, + 'migration' => '2023_08_07_205816_create_v2_ticket_table', + 'batch' => 1, + ), + 23 => + array ( + 'id' => 24, + 'migration' => '2023_08_07_205816_create_v2_user_table', + 'batch' => 1, + ) + )); + } +} \ No newline at end of file diff --git a/Xboard/docs/en/development/device-limit.md b/Xboard/docs/en/development/device-limit.md new file mode 100644 index 0000000..f56b9e7 --- /dev/null +++ b/Xboard/docs/en/development/device-limit.md @@ -0,0 +1,176 @@ +# Online Device Limit Design + +## Overview + +This document describes the design and implementation of the online device limit feature in Xboard. + +## Design Goals + +1. Accurate Control + - Precise counting of online devices + - Real-time monitoring of device status + - Accurate device identification + +2. Performance Optimization + - Minimal impact on system performance + - Efficient device tracking + - Optimized resource usage + +3. User Experience + - Smooth connection experience + - Clear error messages + - Graceful handling of limit exceeded cases + +## Implementation Details + +### 1. Device Identification + +#### Device ID Generation +```php +public function generateDeviceId($user, $request) { + return md5( + $user->id . + $request->header('User-Agent') . + $request->ip() + ); +} +``` + +#### Device Information Storage +```php +[ + 'device_id' => 'unique_device_hash', + 'user_id' => 123, + 'ip' => '192.168.1.1', + 'user_agent' => 'Mozilla/5.0...', + 'last_active' => '2024-03-21 10:00:00' +] +``` + +### 2. Connection Management + +#### Connection Check +```php +public function checkDeviceLimit($user, $deviceId) { + $onlineDevices = $this->getOnlineDevices($user->id); + + if (count($onlineDevices) >= $user->device_limit) { + if (!in_array($deviceId, $onlineDevices)) { + throw new DeviceLimitExceededException(); + } + } + + return true; +} +``` + +#### Device Status Update +```php +public function updateDeviceStatus($userId, $deviceId) { + Redis::hset( + "user:{$userId}:devices", + $deviceId, + json_encode([ + 'last_active' => now(), + 'status' => 'online' + ]) + ); +} +``` + +### 3. Cleanup Mechanism + +#### Inactive Device Cleanup +```php +public function cleanupInactiveDevices() { + $inactiveThreshold = now()->subMinutes(30); + + foreach ($this->getUsers() as $user) { + $devices = $this->getOnlineDevices($user->id); + + foreach ($devices as $deviceId => $info) { + if ($info['last_active'] < $inactiveThreshold) { + $this->removeDevice($user->id, $deviceId); + } + } + } +} +``` + +## Error Handling + +### Error Types +1. Device Limit Exceeded + ```php + class DeviceLimitExceededException extends Exception { + protected $message = 'Device limit exceeded'; + protected $code = 4001; + } + ``` + +2. Invalid Device + ```php + class InvalidDeviceException extends Exception { + protected $message = 'Invalid device'; + protected $code = 4002; + } + ``` + +### Error Messages +```php +return [ + 'device_limit_exceeded' => 'Maximum number of devices reached', + 'invalid_device' => 'Device not recognized', + 'device_expired' => 'Device session expired' +]; +``` + +## Performance Considerations + +1. Cache Strategy + - Use Redis for device tracking + - Implement cache expiration + - Optimize cache structure + +2. Database Operations + - Minimize database queries + - Use batch operations + - Implement query optimization + +3. Memory Management + - Efficient data structure + - Regular cleanup of expired data + - Memory usage monitoring + +## Security Measures + +1. Device Verification + - Validate device information + - Check for suspicious patterns + - Implement rate limiting + +2. Data Protection + - Encrypt sensitive information + - Implement access control + - Regular security audits + +## Future Improvements + +1. Enhanced Features + - Device management interface + - Device activity history + - Custom device names + +2. Performance Optimization + - Improved caching strategy + - Better cleanup mechanism + - Reduced memory usage + +3. Security Enhancements + - Advanced device fingerprinting + - Fraud detection + - Improved encryption + +## Conclusion + +This design provides a robust and efficient solution for managing online device limits while maintaining good performance and user experience. Regular monitoring and updates will ensure the system remains effective and secure. \ No newline at end of file diff --git a/Xboard/docs/en/development/performance.md b/Xboard/docs/en/development/performance.md new file mode 100644 index 0000000..3741d49 --- /dev/null +++ b/Xboard/docs/en/development/performance.md @@ -0,0 +1,100 @@ +# Performance Comparison Report + +## Test Environment + +### Hardware Configuration +- CPU: AMD EPYC 7K62 48-Core Processor +- Memory: 4GB +- Disk: NVMe SSD +- Network: 1Gbps + +### Software Environment +- OS: Ubuntu 22.04 LTS +- PHP: 8.2 +- MySQL: 5.7 +- Redis: 7.0 +- Docker: Latest stable version + +## Test Scenarios + +### 1. User Login Performance +- Concurrent users: 100 +- Test duration: 60 seconds +- Request type: POST +- Target endpoint: `/api/v1/passport/auth/login` + +Results: +- Average response time: 156ms +- 95th percentile: 245ms +- Maximum response time: 412ms +- Requests per second: 642 + +### 2. User Dashboard Loading +- Concurrent users: 100 +- Test duration: 60 seconds +- Request type: GET +- Target endpoint: `/api/v1/user/dashboard` + +Results: +- Average response time: 89ms +- 95th percentile: 167ms +- Maximum response time: 289ms +- Requests per second: 1121 + +### 3. Node List Query +- Concurrent users: 100 +- Test duration: 60 seconds +- Request type: GET +- Target endpoint: `/api/v1/user/server/nodes` + +Results: +- Average response time: 134ms +- 95th percentile: 223ms +- Maximum response time: 378ms +- Requests per second: 745 + +## Performance Optimization Measures + +1. Database Optimization + - Added indexes for frequently queried fields + - Optimized slow queries + - Implemented query caching + +2. Cache Strategy + - Using Redis for session storage + - Caching frequently accessed data + - Implementing cache warming + +3. Code Optimization + - Reduced database queries + - Optimized database connection pool + - Improved error handling + +## Comparison with Previous Version + +| Metric | Previous Version | Current Version | Improvement | +|--------|-----------------|-----------------|-------------| +| Login Response | 289ms | 156ms | 46% | +| Dashboard Loading | 178ms | 89ms | 50% | +| Node List Query | 256ms | 134ms | 48% | + +## Future Optimization Plans + +1. Infrastructure Level + - Implement horizontal scaling + - Add load balancing + - Optimize network configuration + +2. Application Level + - Further optimize database queries + - Implement more efficient caching strategies + - Reduce memory usage + +3. Monitoring and Maintenance + - Add performance monitoring + - Implement automatic scaling + - Regular performance testing + +## Conclusion + +The current version shows significant performance improvements compared to the previous version, with an average improvement of 48% in response times. The optimization measures implemented have effectively enhanced the system's performance and stability. \ No newline at end of file diff --git a/Xboard/docs/en/development/plugin-development-guide.md b/Xboard/docs/en/development/plugin-development-guide.md new file mode 100644 index 0000000..a5163c5 --- /dev/null +++ b/Xboard/docs/en/development/plugin-development-guide.md @@ -0,0 +1,691 @@ +# XBoard Plugin Development Guide + +## 📦 Plugin Structure + +Each plugin is an independent directory with the following structure: + +``` +plugins/ +└── YourPlugin/ # Plugin directory (PascalCase naming) + ├── Plugin.php # Main plugin class (required) + ├── config.json # Plugin configuration (required) + ├── routes/ + │ └── api.php # API routes + ├── Controllers/ # Controllers directory + │ └── YourController.php + ├── Commands/ # Artisan commands directory + │ └── YourCommand.php + └── README.md # Documentation +``` + +## 🚀 Quick Start + +### 1. Create Configuration File `config.json` + +```json +{ + "name": "My Plugin", + "code": "my_plugin", // Corresponds to plugin directory (lowercase + underscore) + "version": "1.0.0", + "description": "Plugin functionality description", + "author": "Author Name", + "require": { + "xboard": ">=1.0.0" // Version not fully implemented yet + }, + "config": { + "api_key": { + "type": "string", + "default": "", + "label": "API Key", + "description": "API Key" + }, + "timeout": { + "type": "number", + "default": 300, + "label": "Timeout (seconds)", + "description": "Timeout in seconds" + } + } +} +``` + +### 2. Create Main Plugin Class `Plugin.php` + +```php +filter('guest_comm_config', function ($config) { + $config['my_plugin_enable'] = true; + $config['my_plugin_setting'] = $this->getConfig('api_key', ''); + return $config; + }); + } +} +``` + +### 3. Create Controller + +**Recommended approach: Extend PluginController** + +```php +getConfig('api_key'); + $timeout = $this->getConfig('timeout', 300); + + // Your business logic... + + return $this->success(['message' => 'Success']); + } +} +``` + +### 4. Create Routes `routes/api.php` + +```php + 'api/v1/your-plugin' +], function () { + Route::post('/handle', [YourController::class, 'handle']); +}); +``` + +## 🔧 Configuration Access + +In controllers, you can easily access plugin configuration: + +```php +// Get single configuration +$value = $this->getConfig('key', 'default_value'); + +// Get all configurations +$allConfig = $this->getConfig(); + +// Check if plugin is enabled +$enabled = $this->isPluginEnabled(); +``` + +## 🎣 Hook System + +### Popular Hooks (Recommended to follow) + +XBoard has built-in hooks for many business-critical nodes. Plugin developers can flexibly extend through `filter` or `listen` methods. Here are the most commonly used and valuable hooks: + +| Hook Name | Type | Typical Parameters | Description | +| ------------------------- | ------ | ----------------------- | ---------------- | +| user.register.before | action | Request | Before user registration | +| user.register.after | action | User | After user registration | +| user.login.after | action | User | After user login | +| user.password.reset.after | action | User | After password reset | +| order.cancel.before | action | Order | Before order cancellation | +| order.cancel.after | action | Order | After order cancellation | +| payment.notify.before | action | method, uuid, request | Before payment callback | +| payment.notify.verified | action | array | Payment callback verification successful | +| payment.notify.failed | action | method, uuid, request | Payment callback verification failed | +| traffic.reset.after | action | User | After traffic reset | +| ticket.create.after | action | Ticket | After ticket creation | +| ticket.reply.user.after | action | Ticket | After user replies to ticket | +| ticket.close.after | action | Ticket | After ticket closure | + +> ⚡️ The hook system will continue to expand. Developers can always follow this documentation and the `php artisan hook:list` command to get the latest supported hooks. + +### Filter Hooks + +Used to modify data: + +```php +// In Plugin.php boot() method +$this->filter('guest_comm_config', function ($config) { + // Add configuration for frontend + $config['my_setting'] = $this->getConfig('setting'); + return $config; +}); +``` + +### Action Hooks + +Used to execute operations: + +```php +$this->listen('user.created', function ($user) { + // Operations after user creation + $this->doSomething($user); +}); +``` + +## 📝 Real Example: Telegram Login Plugin + +Using TelegramLogin plugin as an example to demonstrate complete implementation: + +**Main Plugin Class** (23 lines): + +```php +filter('guest_comm_config', function ($config) { + $config['telegram_login_enable'] = true; + $config['telegram_login_domain'] = $this->getConfig('domain', ''); + $config['telegram_bot_username'] = $this->getConfig('bot_username', ''); + return $config; + }); + } +} +``` + +**Controller** (extends PluginController): + +```php +class TelegramLoginController extends PluginController +{ + public function telegramLogin(Request $request) + { + // Check plugin status + if ($error = $this->beforePluginAction()) { + return $error[1]; + } + + // Get configuration + $botToken = $this->getConfig('bot_token'); + $timeout = $this->getConfig('auth_timeout', 300); + + // Business logic... + + return $this->success($result); + } +} +``` + +## ⏰ Plugin Scheduled Tasks (Scheduler) + +Plugins can register their own scheduled tasks by implementing the `schedule(Schedule $schedule)` method in the main class. + +**Example:** + +```php +use Illuminate\Console\Scheduling\Schedule; + +class Plugin extends AbstractPlugin +{ + public function schedule(Schedule $schedule): void + { + // Execute every hour + $schedule->call(function () { + // Your scheduled task logic + \Log::info('Plugin scheduled task executed'); + })->hourly(); + } +} +``` + +- Just implement the `schedule()` method in Plugin.php. +- All plugin scheduled tasks will be automatically scheduled by the main program. +- Supports all Laravel scheduler usage. + +## 🖥️ Plugin Artisan Commands + +Plugins can automatically register Artisan commands by creating command classes in the `Commands/` directory. + +### Command Directory Structure + +``` +plugins/YourPlugin/ +├── Commands/ +│ ├── TestCommand.php # Test command +│ ├── BackupCommand.php # Backup command +│ └── CleanupCommand.php # Cleanup command +``` + +### Create Command Class + +**Example: TestCommand.php** + +```php +argument('action'); + $message = $this->option('message'); + + try { + return match ($action) { + 'ping' => $this->ping($message), + 'info' => $this->showInfo(), + default => $this->showHelp() + }; + } catch (\Exception $e) { + $this->error('Operation failed: ' . $e->getMessage()); + return 1; + } + } + + protected function ping(string $message): int + { + $this->info("✅ {$message}"); + return 0; + } + + protected function showInfo(): int + { + $this->info('Plugin Information:'); + $this->table( + ['Property', 'Value'], + [ + ['Plugin Name', 'YourPlugin'], + ['Version', '1.0.0'], + ['Status', 'Enabled'], + ] + ); + return 0; + } + + protected function showHelp(): int + { + $this->info('Usage:'); + $this->line(' php artisan your-plugin:test ping --message="Hello" # Test'); + $this->line(' php artisan your-plugin:test info # Show info'); + return 0; + } +} +``` + +### Automatic Command Registration + +- ✅ Automatically register all commands in `Commands/` directory when plugin is enabled +- ✅ Command namespace automatically set to `Plugin\YourPlugin\Commands` +- ✅ Supports all Laravel command features (arguments, options, interaction, etc.) + +### Usage Examples + +```bash +# Test command +php artisan your-plugin:test ping --message="Hello World" + +# Show information +php artisan your-plugin:test info + +# View help +php artisan your-plugin:test --help +``` + +### Best Practices + +1. **Command Naming**: Use `plugin-name:action` format, e.g., `telegram:test` +2. **Error Handling**: Wrap main logic with try-catch +3. **Return Values**: Return 0 for success, 1 for failure +4. **User Friendly**: Provide clear help information and error messages +5. **Type Declarations**: Use PHP 8.2 type declarations + +## 🛠️ Development Tools + +### Controller Base Class Selection + +**Method 1: Extend PluginController (Recommended)** + +- Automatic configuration access: `$this->getConfig()` +- Automatic status checking: `$this->beforePluginAction()` +- Unified error handling + +**Method 2: Use HasPluginConfig Trait** + +```php +use App\Http\Controllers\Controller; +use App\Traits\HasPluginConfig; + +class YourController extends Controller +{ + use HasPluginConfig; + + public function handle() + { + $config = $this->getConfig('key'); + // ... + } +} +``` + +### Configuration Types + +Supported configuration types: + +- `string` - String +- `number` - Number +- `boolean` - Boolean +- `json` - Array +- `yaml` + +## 🎯 Best Practices + +### 1. Concise Main Class + +- Plugin main class should be as concise as possible +- Mainly used for registering hooks and routes +- Complex logic should be placed in controllers or services + +### 2. Configuration Management + +- Define all configuration items in `config.json` +- Use `$this->getConfig()` to access configuration +- Provide default values for all configurations + +### 3. Route Design + +- Use semantic route prefixes +- Place API routes in `routes/api.php` +- Place Web routes in `routes/web.php` + +### 4. Error Handling + +```php +public function handle(Request $request) +{ + // Check plugin status + if ($error = $this->beforePluginAction()) { + return $error[1]; + } + + try { + // Business logic + return $this->success($result); + } catch (\Exception $e) { + return $this->fail([500, $e->getMessage()]); + } +} +``` + +## 🔍 Debugging Tips + +### 1. Logging + +```php +\Log::info('Plugin operation', ['data' => $data]); +\Log::error('Plugin error', ['error' => $e->getMessage()]); +``` + +### 2. Configuration Checking + +```php +// Check required configuration +if (!$this->getConfig('required_key')) { + return $this->fail([400, 'Missing configuration']); +} +``` + +### 3. Development Mode + +```php +if (config('app.debug')) { + // Detailed debug information for development environment +} +``` + +## 📋 Plugin Lifecycle + +1. **Installation**: Validate configuration, register to database +2. **Enable**: Load plugin, register hooks and routes +3. **Running**: Handle requests, execute business logic + +## 🎉 Summary + +Based on TelegramLogin plugin practical experience: + +- **Simplicity**: Main class only 23 lines, focused on core functionality +- **Practicality**: Extends PluginController, convenient configuration access +- **Maintainability**: Clear directory structure, standard development patterns +- **Extensibility**: Hook-based architecture, easy to extend functionality + +Following this guide, you can quickly develop plugins with complete functionality and concise code! 🚀 + +## 🖥️ Complete Plugin Artisan Commands Guide + +### Feature Highlights + +✅ **Auto Registration**: Automatically register all commands in `Commands/` directory when plugin is enabled +✅ **Namespace Isolation**: Each plugin's commands use independent namespaces +✅ **Type Safety**: Support PHP 8.2 type declarations +✅ **Error Handling**: Comprehensive exception handling and error messages +✅ **Configuration Integration**: Commands can access plugin configuration +✅ **Interaction Support**: Support user input and confirmation operations + +### Real Case Demonstrations + +#### 1. Telegram Plugin Commands + +```bash +# Test Bot connection +php artisan telegram:test ping + +# Send message +php artisan telegram:test send --message="Hello World" + +# Get Bot information +php artisan telegram:test info +``` + +#### 2. TelegramExtra Plugin Commands + +```bash +# Show all statistics +php artisan telegram-extra:stats all + +# User statistics +php artisan telegram-extra:stats users + +# JSON format output +php artisan telegram-extra:stats users --format=json +``` + +#### 3. Example Plugin Commands + +```bash +# Basic usage +php artisan example:hello + +# With arguments and options +php artisan example:hello Bear --message="Welcome!" +``` + +### Development Best Practices + +#### 1. Command Naming Conventions + +```php +// ✅ Recommended: Use plugin name as prefix +protected $signature = 'telegram:test {action}'; +protected $signature = 'telegram-extra:stats {type}'; +protected $signature = 'example:hello {name}'; + +// ❌ Avoid: Use generic names +protected $signature = 'test {action}'; +protected $signature = 'stats {type}'; +``` + +#### 2. Error Handling Pattern + +```php +public function handle(): int +{ + try { + // Main logic + return $this->executeAction(); + } catch (\Exception $e) { + $this->error('Operation failed: ' . $e->getMessage()); + return 1; + } +} +``` + +#### 3. User Interaction + +```php +// Get user input +$chatId = $this->ask('Please enter chat ID'); + +// Confirm operation +if (!$this->confirm('Are you sure you want to execute this operation?')) { + $this->info('Operation cancelled'); + return 0; +} + +// Choose operation +$action = $this->choice('Choose operation', ['ping', 'send', 'info']); +``` + +#### 4. Configuration Access + +```php +// Access plugin configuration in commands +protected function getConfig(string $key, $default = null): mixed +{ + // Get plugin instance through PluginManager + $plugin = app(\App\Services\Plugin\PluginManager::class) + ->getEnabledPlugins()['example_plugin'] ?? null; + + return $plugin ? $plugin->getConfig($key, $default) : $default; +} +``` + +### Advanced Usage + +#### 1. Multi-Command Plugins + +```php +// One plugin can have multiple commands +plugins/YourPlugin/Commands/ +├── BackupCommand.php # Backup command +├── CleanupCommand.php # Cleanup command +├── StatsCommand.php # Statistics command +└── TestCommand.php # Test command +``` + +#### 2. Inter-Command Communication + +```php +// Share data between commands through cache or database +Cache::put('plugin:backup:progress', $progress, 3600); +$progress = Cache::get('plugin:backup:progress'); +``` + +#### 3. Scheduled Task Integration + +```php +// Call commands in plugin's schedule method +public function schedule(Schedule $schedule): void +{ + $schedule->command('your-plugin:backup')->daily(); + $schedule->command('your-plugin:cleanup')->weekly(); +} +``` + +### Debugging Tips + +#### 1. Command Testing + +```bash +# View command help +php artisan your-plugin:command --help + +# Verbose output +php artisan your-plugin:command --verbose + +# Debug mode +php artisan your-plugin:command --debug +``` + +#### 2. Logging + +```php +// Log in commands +Log::info('Plugin command executed', [ + 'command' => $this->signature, + 'arguments' => $this->arguments(), + 'options' => $this->options() +]); +``` + +#### 3. Performance Monitoring + +```php +// Record command execution time +$startTime = microtime(true); +// ... execution logic +$endTime = microtime(true); +$this->info("Execution time: " . round(($endTime - $startTime) * 1000, 2) . "ms"); +``` + +### Common Issues + +#### Q: Commands not showing in list? + +A: Check if plugin is enabled and ensure `Commands/` directory exists and contains valid command classes. + +#### Q: Command execution failed? + +A: Check if command class namespace is correct and ensure it extends `Illuminate\Console\Command`. + +#### Q: How to access plugin configuration? + +A: Get plugin instance through `PluginManager`, then call `getConfig()` method. + +#### Q: Can commands call other commands? + +A: Yes, use `Artisan::call()` method to call other commands. + +```php +Artisan::call('other-plugin:command', ['arg' => 'value']); +``` + +### Summary + +The plugin command system provides powerful extension capabilities for XBoard: + +- 🚀 **Development Efficiency**: Quickly create management commands +- 🔧 **Operational Convenience**: Automate daily operations +- 📊 **Monitoring Capability**: Real-time system status viewing +- 🛠️ **Debug Support**: Convenient problem troubleshooting tools + +By properly using plugin commands, you can greatly improve system maintainability and user experience! 🎉 diff --git a/Xboard/docs/en/installation/1panel.md b/Xboard/docs/en/installation/1panel.md new file mode 100644 index 0000000..1237444 --- /dev/null +++ b/Xboard/docs/en/installation/1panel.md @@ -0,0 +1,210 @@ +# Quick Deployment Guide for 1Panel + +This guide explains how to deploy Xboard using 1Panel. + +## 1. Environment Preparation + +Install 1Panel: +```bash +curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && \ +sudo bash quick_start.sh +``` + +## 2. Environment Configuration + +1. Install from App Store: + - OpenResty (any version) + - ⚠️ Check "External Port Access" to open firewall + - MySQL 5.7 (Use MariaDB for ARM architecture) + +2. Create Database: + - Database name: `xboard` + - Username: `xboard` + - Access rights: All hosts (%) + - Save the database password for installation + +## 3. Deployment Steps + +1. Add Website: + - Go to "Website" > "Create Website" > "Reverse Proxy" + - Domain: Enter your domain + - Code: `xboard` + - Proxy address: `127.0.0.1:7001` + +2. Configure Reverse Proxy: +```nginx +location /ws/ { + proxy_pass http://127.0.0.1:8076; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 60s; +} + +location ^~ / { + proxy_pass http://127.0.0.1:7001; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Real-PORT $remote_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header Scheme $scheme; + proxy_set_header Server-Protocol $server_protocol; + proxy_set_header Server-Name $server_name; + proxy_set_header Server-Addr $server_addr; + proxy_set_header Server-Port $server_port; + proxy_cache off; +} +``` +> The `/ws/` location enables WebSocket real-time node synchronization via `ws-server`. This service is enabled by default and can be toggled in Admin Panel > System Settings > Server. + +3. Install Xboard: +```bash +# Enter site directory +cd /opt/1panel/apps/openresty/openresty/www/sites/xboard/index + +# Install Git (if not installed) +## Ubuntu/Debian +apt update && apt install -y git +## CentOS/RHEL +yum update && yum install -y git + +# Clone repository +git clone -b compose --depth 1 https://github.com/cedar2025/Xboard ./ + +# Configure Docker Compose +``` + +4. Edit compose.yaml: +```yaml +services: + web: + image: ghcr.io/cedar2025/xboard:new + volumes: + - redis-data:/data + - ./.env:/www/.env + - ./.docker/.data/:/www/.docker/.data + - ./storage/logs:/www/storage/logs + - ./storage/theme:/www/storage/theme + - ./plugins:/www/plugins + environment: + - docker=true + depends_on: + - redis + command: php artisan octane:start --host=0.0.0.0 --port=7001 + restart: on-failure + ports: + - 7001:7001 + networks: + - 1panel-network + + horizon: + image: ghcr.io/cedar2025/xboard:new + volumes: + - redis-data:/data + - ./.env:/www/.env + - ./.docker/.data/:/www/.docker/.data + - ./storage/logs:/www/storage/logs + - ./plugins:/www/plugins + restart: on-failure + command: php artisan horizon + networks: + - 1panel-network + depends_on: + - redis + ws-server: + image: ghcr.io/cedar2025/xboard:new + volumes: + - redis-data:/data + - ./.env:/www/.env + - ./.docker/.data/:/www/.docker/.data + - ./storage/logs:/www/storage/logs + - ./plugins:/www/plugins + restart: on-failure + ports: + - 8076:8076 + networks: + - 1panel-network + command: php artisan ws-server start + depends_on: + - redis + + redis: + image: redis:7-alpine + command: redis-server --unixsocket /data/redis.sock --unixsocketperm 777 + restart: unless-stopped + networks: + - 1panel-network + volumes: + - redis-data:/data + +volumes: + redis-data: + +networks: + 1panel-network: + external: true +``` + +5. Initialize Installation: +```bash +# Install dependencies and initialize +docker compose run -it --rm web php artisan xboard:install +``` + +⚠️ Important Configuration Notes: +1. Database Configuration + - Database Host: Choose based on your deployment: + 1. If database and Xboard are in the same network, use `mysql` + 2. If connection fails, go to: Database -> Select Database -> Connection Info -> Container Connection, and use the "Host" value + 3. If using external database, enter your actual database host + - Database Port: `3306` (default port unless configured otherwise) + - Database Name: `xboard` (the database created earlier) + - Database User: `xboard` (the user created earlier) + - Database Password: Enter the password saved earlier + +2. Redis Configuration + - Choose to use built-in Redis + - No additional configuration needed + +3. Administrator Information + - Save the admin credentials displayed after installation + - Note down the admin panel access URL + +After configuration, start the services: +```bash +docker compose up -d +``` + +6. Start Services: +```bash +docker compose up -d +``` + +## 4. Version Update + +> 💡 Important Note: The update command varies depending on your installation version: +> - If you installed recently (new version), use this command: +```bash +docker compose pull && \ +docker compose run -it --rm web php artisan xboard:update && \ +docker compose up -d +``` +> - If you installed earlier (old version), replace `web` with `xboard`: +```bash +docker compose pull && \ +docker compose run -it --rm xboard php artisan xboard:update && \ +docker compose up -d +``` +> 🤔 Not sure which to use? Try the new version command first, if it fails, use the old version command. + +## Important Notes + +- ⚠️ Ensure firewall is enabled to prevent port 7001 exposure to public +- Service restart is required after code modifications +- SSL certificate configuration is recommended for secure access + +> The node will automatically detect WebSocket availability during handshake. No extra configuration is needed on the node side. diff --git a/Xboard/docs/en/installation/aapanel-docker.md b/Xboard/docs/en/installation/aapanel-docker.md new file mode 100644 index 0000000..348ed98 --- /dev/null +++ b/Xboard/docs/en/installation/aapanel-docker.md @@ -0,0 +1,151 @@ +# Xboard Deployment Guide for aaPanel + Docker Environment + +## Table of Contents +1. [Requirements](#requirements) +2. [Quick Deployment](#quick-deployment) +3. [Detailed Configuration](#detailed-configuration) +4. [Maintenance Guide](#maintenance-guide) +5. [Troubleshooting](#troubleshooting) + +## Requirements + +### Hardware Requirements +- CPU: 1 core or above +- Memory: 2GB or above +- Storage: 10GB+ available space + +### Software Requirements +- Operating System: Ubuntu 20.04+ / CentOS 7+ / Debian 10+ +- Latest version of aaPanel +- Docker and Docker Compose +- Nginx (any version) +- MySQL 5.7+ + +## Quick Deployment + +### 1. Install aaPanel +```bash +curl -sSL https://www.aapanel.com/script/install_6.0_en.sh -o install_6.0_en.sh && \ +bash install_6.0_en.sh aapanel +``` + +### 2. Basic Environment Setup + +#### 2.1 Install Docker +```bash +# Install Docker +curl -sSL https://get.docker.com | bash + +# For CentOS systems, also run: +systemctl enable docker +systemctl start docker +``` + +#### 2.2 Install Required Components +In the aaPanel dashboard, install: +- Nginx (any version) +- MySQL 5.7 +- ⚠️ PHP and Redis are not required + +### 3. Site Configuration + +#### 3.1 Create Website +1. Navigate to: aaPanel > Website > Add site +2. Fill in the information: + - Domain: Enter your site domain + - Database: Select MySQL + - PHP Version: Select Pure Static + +#### 3.2 Deploy Xboard +```bash +# Enter site directory +cd /www/wwwroot/your-domain + +# Clean directory +chattr -i .user.ini +rm -rf .htaccess 404.html 502.html index.html .user.ini + +# Clone repository +git clone https://github.com/cedar2025/Xboard.git ./ + +# Prepare configuration file +cp compose.sample.yaml compose.yaml + +# Install dependencies and initialize +docker compose run -it --rm web sh init.sh +``` +> ⚠️ Please save the admin dashboard URL, username, and password shown after installation + +#### 3.3 Start Services +```bash +docker compose up -d +``` + +#### 3.4 Configure Reverse Proxy +Add the following content to your site configuration: +```nginx +location /ws/ { + proxy_pass http://127.0.0.1:8076; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 60s; +} + +location ^~ / { + proxy_pass http://127.0.0.1:7001; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Real-PORT $remote_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header Scheme $scheme; + proxy_set_header Server-Protocol $server_protocol; + proxy_set_header Server-Name $server_name; + proxy_set_header Server-Addr $server_addr; + proxy_set_header Server-Port $server_port; + proxy_cache off; +} +``` +> The `/ws/` location enables real-time node synchronization via `ws-server`. This service is enabled by default and can be toggled in Admin Panel > System Settings > Server. + +## Maintenance Guide + +### Version Updates + +> 💡 Important Note: Update commands may vary depending on your installed version: +> - For recent installations (new version), use: +```bash +docker compose pull && \ +docker compose run -it --rm web sh update.sh && \ +docker compose up -d +``` +> - For older installations, replace `web` with `xboard`: +```bash +git config --global --add safe.directory $(pwd) +git fetch --all && git reset --hard origin/master && git pull origin master +docker compose pull && \ +docker compose run -it --rm xboard sh update.sh && \ +docker compose up -d +``` +> 🤔 Not sure which to use? Try the new version command first, if it fails, use the old version command. + +### Routine Maintenance +- Regular log checking: `docker compose logs` +- Monitor system resource usage +- Regular backup of database and configuration files + +## Troubleshooting + +If you encounter any issues during installation or operation, please check: +1. **Empty Admin Dashboard**: If the admin panel is blank, run `git submodule update --init --recursive --force` to restore the theme files. +2. System requirements are met +3. All required ports are available +3. Docker services are running properly +4. Nginx configuration is correct +5. Check logs for detailed error messages + +> The node will automatically detect WebSocket availability during handshake. No extra configuration is needed on the node side. diff --git a/Xboard/docs/en/installation/aapanel.md b/Xboard/docs/en/installation/aapanel.md new file mode 100644 index 0000000..7394bc1 --- /dev/null +++ b/Xboard/docs/en/installation/aapanel.md @@ -0,0 +1,210 @@ +# Xboard Deployment Guide for aaPanel Environment + +## Table of Contents +1. [Requirements](#requirements) +2. [Quick Deployment](#quick-deployment) +3. [Detailed Configuration](#detailed-configuration) +4. [Maintenance Guide](#maintenance-guide) +5. [Troubleshooting](#troubleshooting) + +## Requirements + +### Hardware Requirements +- CPU: 1 core or above +- Memory: 2GB or above +- Storage: 10GB+ available space + +### Software Requirements +- Operating System: Ubuntu 20.04+ / Debian 10+ (⚠️ CentOS 7 is not recommended) +- Latest version of aaPanel +- PHP 8.2 +- MySQL 5.7+ +- Redis +- Nginx (any version) + +## Quick Deployment + +### 1. Install aaPanel +```bash +URL=https://www.aapanel.com/script/install_6.0_en.sh && \ +if [ -f /usr/bin/curl ];then curl -ksSO "$URL" ;else wget --no-check-certificate -O install_6.0_en.sh "$URL";fi && \ +bash install_6.0_en.sh aapanel +``` + +### 2. Basic Environment Setup + +#### 2.1 Install LNMP Environment +In the aaPanel dashboard, install: +- Nginx (any version) +- MySQL 5.7 +- PHP 8.2 + +#### 2.2 Install PHP Extensions +Required PHP extensions: +- redis +- fileinfo +- swoole +- readline +- event +- mbstring + +#### 2.3 Enable Required PHP Functions +Functions that need to be enabled: +- putenv +- proc_open +- pcntl_alarm +- pcntl_signal + +### 3. Site Configuration + +#### 3.1 Create Website +1. Navigate to: aaPanel > Website > Add site +2. Fill in the information: + - Domain: Enter your site domain + - Database: Select MySQL + - PHP Version: Select 8.2 + +#### 3.2 Deploy Xboard +```bash +# Enter site directory +cd /www/wwwroot/your-domain + +# Clean directory +chattr -i .user.ini +rm -rf .htaccess 404.html 502.html index.html .user.ini + +# Clone repository +git clone https://github.com/cedar2025/Xboard.git ./ + +# Install dependencies +sh init.sh +``` + +#### 3.3 Configure Site +1. Set running directory to `/public` +2. Add rewrite rules: +```nginx +location /downloads { +} + +location / { + try_files $uri $uri/ /index.php$is_args$query_string; +} + +location ~ .*\.(js|css)?$ +{ + expires 1h; + error_log off; + access_log /dev/null; +} +``` + +## Detailed Configuration + +### 1. Configure Daemon Process +1. Install Supervisor +2. Add queue daemon process: + - Name: `Xboard` + - Run User: `www` + - Running Directory: Site directory + - Start Command: `php artisan horizon` + - Process Count: 1 + +### 2. Configure Scheduled Tasks +- Type: Shell Script +- Task Name: v2board +- Run User: www +- Frequency: 1 minute +- Script Content: `php /www/wwwroot/site-directory/artisan schedule:run` + +### 3. Octane Configuration (Optional) +#### 3.1 Add Octane Daemon Process +- Name: Octane +- Run User: www +- Running Directory: Site directory +- Start Command: `/www/server/php/82/bin/php artisan octane:start --port 7010` +- Process Count: 1 + +#### 3.2 Octane-specific Rewrite Rules +```nginx +location ~* \.(jpg|jpeg|png|gif|js|css|svg|woff2|woff|ttf|eot|wasm|json|ico)$ { +} + +location ~ .* { + proxy_pass http://127.0.0.1:7010; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Real-PORT $remote_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header Scheme $scheme; + proxy_set_header Server-Protocol $server_protocol; + proxy_set_header Server-Name $server_name; + proxy_set_header Server-Addr $server_addr; + proxy_set_header Server-Port $server_port; +} +``` + +## Maintenance Guide + +### Version Updates +```bash +# Enter site directory +cd /www/wwwroot/your-domain + +# Execute update script +git fetch --all && git reset --hard origin/master && git pull origin master +sh update.sh + +# If Octane is enabled, restart the daemon process +# aaPanel > App Store > Tools > Supervisor > Restart Octane +``` + +### Routine Maintenance +- Regular log checking +- Monitor system resource usage +- Regular backup of database and configuration files + +## Troubleshooting + +### Common Issues +1. **Empty Admin Dashboard**: If the admin panel is blank, run `git submodule update --init --recursive --force` to restore the theme files. +2. Changes to admin path require service restart to take effect +3. Any code changes after enabling Octane require restart to take effect +3. When PHP extension installation fails, check if PHP version is correct +4. For database connection failures, check database configuration and permissions + +## Enable WebSocket Real-time Sync (Optional) + +WebSocket enables real-time synchronization of configurations and user changes to nodes. + +### 1. Start WS Server + +Add a WebSocket daemon process in aaPanel Supervisor: +- Name: `Xboard-WS` +- Run User: `www` +- Running Directory: Site directory +- Start Command: `php artisan ws-server start` +- Process Count: 1 + +### 2. Configure Nginx + +Add the WebSocket location **before** the main `location ^~ /` block in your site's Nginx configuration: +```nginx +location /ws/ { + proxy_pass http://127.0.0.1:8076; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 60s; +} +``` + +### 3. Restart Services + +Restart the Octane and WS Server processes in Supervisor. + +> The node will automatically detect WebSocket availability during handshake. No extra configuration is needed on the node side. diff --git a/Xboard/docs/en/installation/docker-compose.md b/Xboard/docs/en/installation/docker-compose.md new file mode 100644 index 0000000..c05818b --- /dev/null +++ b/Xboard/docs/en/installation/docker-compose.md @@ -0,0 +1,77 @@ +# Quick Deployment Guide with Docker Compose + +This guide explains how to quickly deploy Xboard using Docker Compose. By default, it uses SQLite database, eliminating the need for a separate MySQL installation. + +### 1. Environment Preparation + +Install Docker: +```bash +curl -sSL https://get.docker.com | bash + +# For CentOS systems, also run: +systemctl enable docker +systemctl start docker +``` + +### 2. Deployment Steps + +1. Get project files: +```bash +git clone -b compose --depth 1 https://github.com/cedar2025/Xboard +cd Xboard +``` + +2. Install database: + +- Quick installation (Recommended for beginners) +```bash +docker compose run -it --rm \ + -e ENABLE_SQLITE=true \ + -e ENABLE_REDIS=true \ + -e ADMIN_ACCOUNT=admin@demo.com \ + web php artisan xboard:install +``` +- Custom configuration installation (Advanced users) +```bash +docker compose run -it --rm web php artisan xboard:install +``` +> Please save the admin dashboard URL, username, and password shown after installation + +3. Start services: +```bash +docker compose up -d +``` + +4. Access the site: +- Default port: 7001 +- Website URL: http://your-server-ip:7001 + +### 3. Version Updates + +> 💡 Important Note: Update commands may vary depending on your installed version: +> - For recent installations (new version), use: +```bash +cd Xboard +docker compose pull && \ +docker compose run -it --rm web php artisan xboard:update && \ +docker compose up -d +``` +> - For older installations, replace `web` with `xboard`: +```bash +cd Xboard +docker compose pull && \ +docker compose run -it --rm xboard php artisan xboard:update && \ +docker compose up -d +``` +> 🤔 Not sure which to use? Try the new version command first, if it fails, use the old version command. + +### 4. Version Rollback + +1. Modify the version number in `docker-compose.yaml` to the version you want to roll back to +2. Execute: `docker compose up -d` + +### Important Notes + +- If you need to use MySQL, please install it separately and redeploy +- Code changes require service restart to take effect +- You can configure Nginx reverse proxy to use port 80 \ No newline at end of file diff --git a/Xboard/docs/en/migration/config.md b/Xboard/docs/en/migration/config.md new file mode 100644 index 0000000..0a91fdd --- /dev/null +++ b/Xboard/docs/en/migration/config.md @@ -0,0 +1,54 @@ +# Configuration Migration Guide + +This guide explains how to migrate configuration files from v2board to Xboard. Xboard stores configurations in the database instead of files. + +### 1. Docker Compose Environment + +1. Prepare configuration file: +```bash +# Create config directory +mkdir config + +# Copy old configuration file +cp old-project-path/config/v2board.php config/ +``` + +2. Modify `docker-compose.yaml`, uncomment the following line: +```yaml +- ./config/v2board.php:/www/config/v2board.php +``` + +3. Execute migration: +```bash +docker compose run -it --rm web php artisan migrateFromV2b config +``` + +### 2. aaPanel Environment + +1. Copy configuration file: +```bash +cp old-project-path/config/v2board.php config/v2board.php +``` + +2. Execute migration: +```bash +php artisan migrateFromV2b config +``` + +### 3. aaPanel + Docker Environment + +1. Copy configuration file: +```bash +cp old-project-path/config/v2board.php config/v2board.php +``` + +2. Execute migration: +```bash +docker compose run -it --rm web php artisan migrateFromV2b config +``` + +### Important Notes + +- After modifying the admin path, service restart is required: + - Docker environment: `docker compose restart` + - aaPanel environment: Restart the Octane daemon process \ No newline at end of file diff --git a/Xboard/docs/en/migration/v2board-1.7.3.md b/Xboard/docs/en/migration/v2board-1.7.3.md new file mode 100644 index 0000000..06ecedf --- /dev/null +++ b/Xboard/docs/en/migration/v2board-1.7.3.md @@ -0,0 +1,63 @@ +# V2board 1.7.3 Migration Guide + +This guide explains how to migrate from V2board version 1.7.3 to Xboard. + +### 1. Database Changes Overview + +- `v2_stat_order` table renamed to `v2_stat`: + - `order_amount` → `order_total` + - `commission_amount` → `commission_total` + - New fields added: + - `paid_count` (integer, nullable) + - `paid_total` (integer, nullable) + - `register_count` (integer, nullable) + - `invite_count` (integer, nullable) + - `transfer_used_total` (string(32), nullable) + +- New tables added: + - `v2_log` + - `v2_server_hysteria` + - `v2_server_vless` + +### 2. Prerequisites + +⚠️ Please complete the basic Xboard installation first (SQLite not supported): +- [Docker Compose Deployment](../installation/docker-compose.md) +- [aaPanel + Docker Deployment](../installation/aapanel-docker.md) +- [aaPanel Deployment](../installation/aapanel.md) + +### 3. Migration Steps + +#### Docker Environment + +```bash +# 1. Stop services +docker compose down + +# 2. Clear database +docker compose run -it --rm web php artisan db:wipe + +# 3. Import old database (Important) +# Please manually import the V2board 1.7.3 database + +# 4. Execute migration +docker compose run -it --rm web php artisan migratefromv2b 1.7.3 +``` + +#### aaPanel Environment + +```bash +# 1. Clear database +php artisan db:wipe + +# 2. Import old database (Important) +# Please manually import the V2board 1.7.3 database + +# 3. Execute migration +php artisan migratefromv2b 1.7.3 +``` + +### 4. Configuration Migration + +After completing the data migration, you need to migrate the configuration file: +- [Configuration Migration Guide](./config.md) \ No newline at end of file diff --git a/Xboard/docs/en/migration/v2board-1.7.4.md b/Xboard/docs/en/migration/v2board-1.7.4.md new file mode 100644 index 0000000..46c1588 --- /dev/null +++ b/Xboard/docs/en/migration/v2board-1.7.4.md @@ -0,0 +1,51 @@ +# V2board 1.7.4 Migration Guide + +This guide explains how to migrate from V2board version 1.7.4 to Xboard. + +### 1. Database Changes Overview + +- New table added: + - `v2_server_vless` + +### 2. Prerequisites + +⚠️ Please complete the basic Xboard installation first (SQLite not supported): +- [Docker Compose Deployment](../installation/docker-compose.md) +- [aaPanel + Docker Deployment](../installation/aapanel-docker.md) +- [aaPanel Deployment](../installation/aapanel.md) + +### 3. Migration Steps + +#### Docker Environment + +```bash +# 1. Stop services +docker compose down + +# 2. Clear database +docker compose run -it --rm web php artisan db:wipe + +# 3. Import old database (Important) +# Please manually import the V2board 1.7.4 database + +# 4. Execute migration +docker compose run -it --rm web php artisan migratefromv2b 1.7.4 +``` + +#### aaPanel Environment + +```bash +# 1. Clear database +php artisan db:wipe + +# 2. Import old database (Important) +# Please manually import the V2board 1.7.4 database + +# 3. Execute migration +php artisan migratefromv2b 1.7.4 +``` + +### 4. Configuration Migration + +After completing the data migration, you need to migrate the configuration file: +- [Configuration Migration Guide](./config.md) \ No newline at end of file diff --git a/Xboard/docs/en/migration/v2board-dev.md b/Xboard/docs/en/migration/v2board-dev.md new file mode 100644 index 0000000..31621ad --- /dev/null +++ b/Xboard/docs/en/migration/v2board-dev.md @@ -0,0 +1,61 @@ +# V2board Dev Migration Guide + +This guide explains how to migrate from V2board Dev version (2023/10/27) to Xboard. + +⚠️ Please upgrade to version 2023/10/27 following the official guide before proceeding with migration. + +### 1. Database Changes Overview + +- `v2_order` table: + - Added `surplus_order_ids` (text, nullable) - Deduction orders + +- `v2_plan` table: + - Removed `daily_unit_price` - Affects period value + - Removed `transfer_unit_price` - Affects traffic value + +- `v2_server_hysteria` table: + - Removed `ignore_client_bandwidth` - Affects bandwidth configuration + - Removed `obfs_type` - Affects obfuscation type configuration + +### 2. Prerequisites + +⚠️ Please complete the basic Xboard installation first (SQLite not supported): +- [Docker Compose Deployment](../installation/docker-compose.md) +- [aaPanel + Docker Deployment](../installation/aapanel-docker.md) +- [aaPanel Deployment](../installation/aapanel.md) + +### 3. Migration Steps + +#### Docker Environment + +```bash +# 1. Stop services +docker compose down + +# 2. Clear database +docker compose run -it --rm web php artisan db:wipe + +# 3. Import old database (Important) +# Please manually import the V2board Dev database + +# 4. Execute migration +docker compose run -it --rm web php artisan migratefromv2b dev231027 +``` + +#### aaPanel Environment + +```bash +# 1. Clear database +php artisan db:wipe + +# 2. Import old database (Important) +# Please manually import the V2board Dev database + +# 3. Execute migration +php artisan migratefromv2b dev231027 +``` + +### 4. Configuration Migration + +After completing the data migration, you need to migrate the configuration file: +- [Configuration Migration Guide](./config.md) \ No newline at end of file diff --git a/Xboard/docs/images/admin.png b/Xboard/docs/images/admin.png new file mode 100644 index 0000000..dd553ad Binary files /dev/null and b/Xboard/docs/images/admin.png differ diff --git a/Xboard/docs/images/user.png b/Xboard/docs/images/user.png new file mode 100644 index 0000000..66bb749 Binary files /dev/null and b/Xboard/docs/images/user.png differ diff --git a/Xboard/init.sh b/Xboard/init.sh new file mode 100644 index 0000000..4a6f797 --- /dev/null +++ b/Xboard/init.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +rm -rf composer.phar +wget https://github.com/composer/composer/releases/latest/download/composer.phar -O composer.phar +php composer.phar install -vvv +git submodule update --init --recursive --force +php artisan xboard:install + +if [ -f "/etc/init.d/bt" ] || [ -f "/.dockerenv" ]; then + chown -R www:www $(pwd); +fi + +if [ -d ".docker/.data" ]; then + chmod -R 777 .docker/.data +fi \ No newline at end of file diff --git a/Xboard/package.json b/Xboard/package.json new file mode 100644 index 0000000..b2a6ce6 --- /dev/null +++ b/Xboard/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "chokidar": "^4.0.3" + } +} \ No newline at end of file diff --git a/Xboard/phpstan.neon b/Xboard/phpstan.neon new file mode 100644 index 0000000..d14e701 --- /dev/null +++ b/Xboard/phpstan.neon @@ -0,0 +1,17 @@ +includes: + - vendor/larastan/larastan/extension.neon + - vendor/nesbot/carbon/extension.neon + +parameters: + + paths: + - app/ + + # Level 10 is the highest level + level: 5 + + ignoreErrors: + - '#Negated boolean expression is always false\.#' + +# excludePaths: +# - ./*/*/FileToBeExcluded.php \ No newline at end of file diff --git a/Xboard/plugins/.gitignore b/Xboard/plugins/.gitignore new file mode 100644 index 0000000..c73c1df --- /dev/null +++ b/Xboard/plugins/.gitignore @@ -0,0 +1,10 @@ +* +!.gitignore +!AlipayF2f/ +!Btcpay +!Coinbase +!Epay +!Mgate +!Telegram +!UserOnlineDevices/ +!UserOnlineDevices/** diff --git a/Xboard/plugins/UserOnlineDevices/Controllers/UserOnlineDevicesController.php b/Xboard/plugins/UserOnlineDevices/Controllers/UserOnlineDevicesController.php new file mode 100644 index 0000000..38c2821 --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/Controllers/UserOnlineDevicesController.php @@ -0,0 +1,164 @@ +beforePluginAction()) { + return $this->fail($error); + } + + return $this->success($this->buildPayload($request->user())); + } + + public function panelUrl(Request $request) + { + if ($error = $this->beforePluginAction()) { + return $this->fail($error); + } + + $user = $request->user(); + $ttlMinutes = max(1, (int) $this->getConfig('signed_url_ttl_minutes', 60)); + $expiresAt = now()->addMinutes($ttlMinutes); + + return $this->success([ + 'url' => URL::temporarySignedRoute( + 'user-online-devices.panel', + $expiresAt, + ['user' => $user->id] + ), + 'expires_at' => $expiresAt->timestamp, + ]); + } + + public function panel(Request $request, int $user): View + { + abort_unless($this->isPluginEnabled(), 404); + + $targetUser = User::query()->findOrFail($user); + $payload = $this->buildPayload($targetUser); + $ttlMinutes = max(1, (int) $this->getConfig('signed_url_ttl_minutes', 60)); + + return view('UserOnlineDevices::panel', [ + 'payload' => $payload, + 'snapshotUrl' => URL::temporarySignedRoute( + 'user-online-devices.snapshot', + now()->addMinutes($ttlMinutes), + ['user' => $targetUser->id] + ), + 'refreshInterval' => max(5, (int) $this->getConfig('refresh_interval_seconds', 15)), + ]); + } + + public function snapshot(Request $request, int $user) + { + abort_unless($this->isPluginEnabled(), 404); + + $targetUser = User::query()->findOrFail($user); + + return response()->json([ + 'data' => $this->buildPayload($targetUser), + ]); + } + + private function buildPayload(User $user): array + { + $devices = $this->deviceStateService->getUsersDevices([$user->id]); + $ips = collect($devices[$user->id] ?? []) + ->filter(fn ($ip) => is_string($ip) && $ip !== '') + ->unique() + ->values(); + + return [ + 'user' => [ + 'id' => $user->id, + 'email' => $user->email, + 'online_count' => $ips->count(), + 'last_online_at' => $user->last_online_at, + ], + 'online_devices' => $this->formatOnlineDevices($ips), + 'active_sessions' => $this->shouldShowActiveSessions() + ? $this->formatActiveSessions($user) + : [], + 'meta' => [ + 'generated_at' => time(), + 'refresh_interval_seconds' => max(5, (int) $this->getConfig('refresh_interval_seconds', 15)), + 'mask_ip' => (bool) $this->getConfig('mask_ip', false), + 'show_active_sessions' => $this->shouldShowActiveSessions(), + ], + ]; + } + + private function formatOnlineDevices(Collection $ips): array + { + return $ips->values()->map(function (string $ip, int $index) { + return [ + 'name' => 'Device ' . ($index + 1), + 'ip' => $this->maskIp($ip), + 'raw_ip' => $ip, + ]; + })->all(); + } + + private function formatActiveSessions(User $user): array + { + return $user->tokens() + ->orderByDesc('last_used_at') + ->get(['id', 'name', 'last_used_at', 'created_at', 'expires_at']) + ->map(function ($token) { + return [ + 'id' => $token->id, + 'device' => $token->name ?: 'Unknown Session', + 'last_used_at' => $token->last_used_at?->timestamp, + 'created_at' => $token->created_at?->timestamp, + 'expires_at' => $token->expires_at?->timestamp, + ]; + }) + ->all(); + } + + private function shouldShowActiveSessions(): bool + { + return (bool) $this->getConfig('show_active_sessions', true); + } + + private function maskIp(string $ip): string + { + if (!(bool) $this->getConfig('mask_ip', false)) { + return $ip; + } + + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + $parts = explode('.', $ip); + $parts[3] = '*'; + return implode('.', $parts); + } + + if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $parts = explode(':', $ip); + $count = count($parts); + if ($count > 2) { + $parts[$count - 1] = '****'; + $parts[$count - 2] = '****'; + } + return implode(':', $parts); + } + + return $ip; + } +} diff --git a/Xboard/plugins/UserOnlineDevices/Plugin.php b/Xboard/plugins/UserOnlineDevices/Plugin.php new file mode 100644 index 0000000..6d772ea --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/Plugin.php @@ -0,0 +1,36 @@ +filter('user.subscribe.response', function ($user) { + if (!$user || empty($user['id'])) { + return $user; + } + + $ttlMinutes = max(1, (int) $this->getConfig('signed_url_ttl_minutes', 60)); + $userId = (int) $user['id']; + + $user['user_online_devices_enabled'] = true; + $user['user_online_devices_panel_url'] = URL::temporarySignedRoute( + 'user-online-devices.panel', + now()->addMinutes($ttlMinutes), + ['user' => $userId] + ); + $user['user_online_devices_snapshot_url'] = URL::temporarySignedRoute( + 'user-online-devices.snapshot', + now()->addMinutes($ttlMinutes), + ['user' => $userId] + ); + + return $user; + }); + } +} diff --git a/Xboard/plugins/UserOnlineDevices/README.md b/Xboard/plugins/UserOnlineDevices/README.md new file mode 100644 index 0000000..0d37c17 --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/README.md @@ -0,0 +1,23 @@ +# User Online Devices Plugin + +This plugin adds a user-facing online device dashboard for Xboard. + +## Features + +- Shows the current online device count based on Xboard's built-in `DeviceStateService` +- Shows the current online IP list for the logged-in user +- Optionally shows Sanctum login sessions +- Provides an authenticated API endpoint for the frontend to fetch the dashboard URL +- Provides a temporary signed dashboard page that auto-refreshes + +## Routes + +- `GET /api/v1/user-online-devices/summary` +- `GET /api/v1/user-online-devices/panel-url` +- `GET /user-online-devices/panel/{user}` (temporary signed URL) + +## Notes + +- This plugin reuses Xboard's existing real-time device state data from Redis. +- In current Xboard releases, plugin development is primarily backend-oriented, so this plugin ships a standalone user-side page instead of patching the compiled SPA bundle directly. +- The "online device" count is effectively the number of unique online IPs reported by nodes. diff --git a/Xboard/plugins/UserOnlineDevices/config.json b/Xboard/plugins/UserOnlineDevices/config.json new file mode 100644 index 0000000..2feea1d --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/config.json @@ -0,0 +1,37 @@ +{ + "name": "User Online Devices", + "code": "user_online_devices", + "version": "1.0.0", + "description": "Show users their current online devices and online IP list with a signed dashboard page.", + "author": "OpenAI Codex", + "type": "feature", + "require": { + "xboard": ">=1.0.0" + }, + "config": { + "refresh_interval_seconds": { + "type": "number", + "default": 15, + "label": "Refresh Interval", + "description": "Dashboard auto-refresh interval in seconds." + }, + "signed_url_ttl_minutes": { + "type": "number", + "default": 60, + "label": "Signed URL TTL", + "description": "Temporary dashboard link validity in minutes." + }, + "mask_ip": { + "type": "boolean", + "default": false, + "label": "Mask IP Address", + "description": "Whether to partially mask IP addresses on the dashboard." + }, + "show_active_sessions": { + "type": "boolean", + "default": true, + "label": "Show Active Sessions", + "description": "Whether to also show Sanctum login sessions." + } + } +} diff --git a/Xboard/plugins/UserOnlineDevices/resources/views/panel.blade.php b/Xboard/plugins/UserOnlineDevices/resources/views/panel.blade.php new file mode 100644 index 0000000..2a5c9ea --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/resources/views/panel.blade.php @@ -0,0 +1,352 @@ + + + + + + + 在线设备与在线 IP + + + + +
+
+
Xboard Plugin Dashboard
+

在线设备与在线 IP

+
+ 当前页面由 User Online Devices 插件生成,会自动刷新并显示该账号最近上报到 Xboard 的在线 IP 列表。 +
+
+ +
+
+
账号
+
{{ $payload['user']['email'] }}
+
用户 ID: {{ $payload['user']['id'] }}
+
+
+
在线设备数
+
{{ $payload['user']['online_count'] }}
+
按实时在线 IP 去重统计
+
+
+
最后活跃
+
{{ $payload['user']['last_online_at'] ? date('Y-m-d H:i:s', $payload['user']['last_online_at']) : '暂无记录' }}
+
数据来源于 Xboard 设备状态服务
+
+
+ +
+
+
+

在线 IP 列表

+
每 {{ $refreshInterval }} 秒刷新
+
+
+ +
+ +
+
+

登录会话

+
{{ $payload['meta']['show_active_sessions'] ? '已开启' : '已关闭' }}
+
+
+
+
+
+ + + + + diff --git a/Xboard/plugins/UserOnlineDevices/routes/api.php b/Xboard/plugins/UserOnlineDevices/routes/api.php new file mode 100644 index 0000000..51f02a3 --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/routes/api.php @@ -0,0 +1,12 @@ + 'api/v1/user-online-devices', + 'middleware' => 'user', +], function () { + Route::get('/summary', [UserOnlineDevicesController::class, 'summary']); + Route::get('/panel-url', [UserOnlineDevicesController::class, 'panelUrl']); +}); diff --git a/Xboard/plugins/UserOnlineDevices/routes/web.php b/Xboard/plugins/UserOnlineDevices/routes/web.php new file mode 100644 index 0000000..093b1e9 --- /dev/null +++ b/Xboard/plugins/UserOnlineDevices/routes/web.php @@ -0,0 +1,18 @@ + 'user-online-devices', +], function () { + Route::get('/panel/{user}', [UserOnlineDevicesController::class, 'panel']) + ->whereNumber('user') + ->middleware('signed') + ->name('user-online-devices.panel'); + + Route::get('/snapshot/{user}', [UserOnlineDevicesController::class, 'snapshot']) + ->whereNumber('user') + ->middleware('signed') + ->name('user-online-devices.snapshot'); +}); diff --git a/Xboard/public/index.php b/Xboard/public/index.php new file mode 100644 index 0000000..4584cbc --- /dev/null +++ b/Xboard/public/index.php @@ -0,0 +1,60 @@ + + */ + +define('LARAVEL_START', microtime(true)); + +/* +|-------------------------------------------------------------------------- +| Register The Auto Loader +|-------------------------------------------------------------------------- +| +| Composer provides a convenient, automatically generated class loader for +| our application. We just need to utilize it! We'll simply require it +| into the script here so that we don't have to worry about manual +| loading any of our classes later on. It feels great to relax. +| +*/ + +require __DIR__.'/../vendor/autoload.php'; + +/* +|-------------------------------------------------------------------------- +| Turn On The Lights +|-------------------------------------------------------------------------- +| +| We need to illuminate PHP development, so let us turn on the lights. +| This bootstraps the framework and gets it ready for use, then it +| will load up this application so that we can run it and send +| the responses back to the browser and delight our users. +| +*/ + +$app = require_once __DIR__.'/../bootstrap/app.php'; + +/* +|-------------------------------------------------------------------------- +| Run The Application +|-------------------------------------------------------------------------- +| +| Once we have the application, we can handle the incoming request +| through the kernel, and send the associated response back to +| the client's browser allowing them to enjoy the creative +| and wonderful application we have prepared for them. +| +*/ + +$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); + +$response = $kernel->handle( + $request = Illuminate\Http\Request::capture() +); + +$response->send(); + +$kernel->terminate($request, $response); diff --git a/Xboard/public/robots.txt b/Xboard/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/Xboard/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/Xboard/public/theme/.gitignore b/Xboard/public/theme/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/Xboard/public/theme/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/Xboard/public/web.config b/Xboard/public/web.config new file mode 100644 index 0000000..d3711d7 --- /dev/null +++ b/Xboard/public/web.config @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Xboard/resources/js/app.js b/Xboard/resources/js/app.js new file mode 100644 index 0000000..40c55f6 --- /dev/null +++ b/Xboard/resources/js/app.js @@ -0,0 +1 @@ +require('./bootstrap'); diff --git a/Xboard/resources/js/bootstrap.js b/Xboard/resources/js/bootstrap.js new file mode 100644 index 0000000..d11586d --- /dev/null +++ b/Xboard/resources/js/bootstrap.js @@ -0,0 +1,28 @@ +window._ = require('lodash'); + +/** + * We'll load the axios HTTP library which allows us to easily issue requests + * to our Laravel back-end. This library automatically handles sending the + * CSRF token as a header based on the value of the "XSRF" token cookie. + */ + +window.axios = require('axios'); + +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; + +/** + * Echo exposes an expressive API for subscribing to channels and listening + * for events that are broadcast by Laravel. Echo and event broadcasting + * allows your team to easily build robust real-time web applications. + */ + +// import Echo from 'laravel-echo'; + +// window.Pusher = require('pusher-js'); + +// window.Echo = new Echo({ +// broadcaster: 'pusher', +// key: process.env.MIX_PUSHER_APP_KEY, +// cluster: process.env.MIX_PUSHER_APP_CLUSTER, +// encrypted: true +// }); diff --git a/Xboard/resources/lang/en-US.json b/Xboard/resources/lang/en-US.json new file mode 100644 index 0000000..26d935d --- /dev/null +++ b/Xboard/resources/lang/en-US.json @@ -0,0 +1,148 @@ +{ + "Article does not exist": "Article does not exist", + "Cancel failed": "Cancel failed", + "Close failed": "Close failed", + "Coupon cannot be empty": "Coupon cannot be empty", + "Coupon failed": "Coupon failed", + "Currency conversion has timed out, please try again later": "Currency conversion has timed out, please try again later", + "Email already exists": "Email already exists", + "Email suffix is not in the Whitelist": "Email suffix is not in the Whitelist", + "Email suffix is not in whitelist": "Email suffix is not in whitelist", + "Email verification code": "Email verification code", + "Email verification code cannot be empty": "Email verification code cannot be empty", + "Email verification code has been sent, please request again later": "Email verification code has been sent, please request again later", + "Failed to create order": "Failed to create order", + "Failed to open ticket": "Failed to open ticket", + "Gmail alias is not supported": "Gmail alias is not supported", + "Incorrect email or password": "Incorrect email or password", + "Incorrect email verification code": "Incorrect email verification code", + "Insufficient balance": "Insufficient balance", + "Insufficient commission balance": "Insufficient commission balance", + "Invalid code is incorrect": "Invalid code is incorrect", + "Invalid coupon": "Invalid coupon", + "Invalid invitation code": "Invalid invitation code", + "Invalid parameter": "Invalid parameter", + "Message cannot be empty": "Message cannot be empty", + "No active subscription. Unable to use our provided Apple ID": "No active subscription. Unable to use our provided Apple ID", + "Oops, there's a problem... Please refresh the page and try again later": "Oops, there's a problem... Please refresh the page and try again later", + "Order does not exist": "Order does not exist", + "Order does not exist or has been paid": "Order does not exist or has been paid", + "Payment failed. Please check your credit card information": "Payment failed. Please check your credit card information", + "Payment gateway request failed": "Payment gateway request failed", + "Payment method is not available": "Payment method is not available", + "Please wait for the technical enginneer to reply": "Please wait for the technical enginneer to reply", + "Register failed": "Register failed", + "Registration has closed": "Registration has closed", + "Reset failed": "Reset failed", + "Save failed": "Save failed", + "Subscription has expired or no active subscription, unable to purchase Data Reset Package": "Subscription has expired or no active subscription, unable to purchase Data Reset Package", + "Subscription plan does not exist": "Subscription plan does not exist", + "The coupon code cannot be used for this subscription": "The coupon code cannot be used for this subscription", + "The current required minimum withdrawal commission is :limit": "The current required minimum withdrawal commission is :limit", + "The maximum number of creations has been reached": "The maximum number of creations has been reached", + "The old password is wrong": "The old password is wrong", + "The ticket is closed and cannot be replied": "The ticket is closed and cannot be replied", + "The user does not exist": "The user does not exist", + "There are other unresolved tickets": "There are other unresolved tickets", + "This coupon has expired": "This coupon has expired", + "This coupon has not yet started": "This coupon has not yet started", + "This coupon is no longer available": "This coupon is no longer available", + "This email is not registered in the system": "This email is not registered in the system", + "This payment cycle cannot be purchased, please choose another cycle": "This payment cycle cannot be purchased, please choose another cycle", + "This subscription cannot be renewed, please change to another subscription": "This subscription cannot be renewed, please change to another subscription", + "This subscription has been sold out, please choose another subscription": "This subscription has been sold out, please choose another subscription", + "This subscription has expired, please change to another subscription": "This subscription has expired, please change to another subscription", + "Ticket does not exist": "Ticket does not exist", + "Ticket reply failed": "Ticket reply failed", + "Token error": "Token error", + "Transfer failed": "Transfer failed", + "Unsupported withdrawal": "Unsupported withdrawal", + "Unsupported withdrawal method": "Unsupported withdrawal method", + "Withdrawal account": "Withdrawal account", + "Withdrawal method": "Withdrawal method", + "You can only cancel pending orders": "You can only cancel pending orders", + "You have an unpaid or pending order, please try again later or cancel it": "You have an unpaid or pending order, please try again later or cancel it", + "You must have a valid subscription to view content in this area": "You must have a valid subscription to view content in this area", + "You must use the invitation code to register": "You must use the invitation code to register", + "Your account has been suspended": "Your account has been suspended", + "[Commission Withdrawal Request] This ticket is opened by the system": "[Commission Withdrawal Request] This ticket is opened by the system", + "Plan ID cannot be empty": "Plan ID cannot be empty", + "Plan cycle cannot be empty": "Plan cycle cannot be empty", + "Wrong plan cycle": "Wrong plan cycle", + "Ticket subject cannot be empty": "Ticket subject cannot be empty", + "Ticket level cannot be empty": "Ticket level cannot be empty", + "Incorrect ticket level format": "Incorrect ticket level format", + "The withdrawal method cannot be empty": "The withdrawal method cannot be empty", + "The withdrawal account cannot be empty": "The withdrawal account cannot be empty", + "Old password cannot be empty": "Old password cannot be empty", + "New password cannot be empty": "New password cannot be empty", + "Password must be greater than 8 digits": "Password must be greater than 8 digits", + "The transfer amount cannot be empty": "The transfer amount cannot be empty", + "The transfer amount parameter is wrong": "The transfer amount parameter is wrong", + "Incorrect format of expiration reminder": "Incorrect format of expiration reminder", + "Incorrect traffic alert format": "Incorrect traffic alert format", + "Email can not be empty": "Email can not be empty", + "Email format is incorrect": "Email format is incorrect", + "Password can not be empty": "Password can not be empty", + "The traffic usage in :app_name has reached 80%": "The traffic usage in :app_name has reached 80%", + "The service in :app_name is about to expire": "The service in :app_name is about to expire", + "The coupon can only be used :limit_use_with_user per person": "The coupon can only be used :limit_use_with_user per person", + "The coupon code cannot be used for this period": "The coupon code cannot be used for this period", + "Request failed, please try again later": "Request failed, please try again later", + "Register frequently, please try again after :minute minute": "Register frequently, please try again after :minute minute", + "Uh-oh, we've had some problems, we're working on it.": "Uh-oh, we've had some problems, we're working on it", + "This subscription reset package does not apply to your subscription": "This subscription reset package does not apply to your subscription", + "Login to :name": "Login to :name", + "Sending frequently, please try again later": "Sending frequently, please try again later", + "Current product is sold out": "Current product is sold out", + "There are too many password errors, please try again after :minute minutes.": "There are too many password errors, please try again after :minute minutes.", + "Reset failed, Please try again later": "Reset failed, Please try again later", + "Subscribe": "Subscribe", + "User Information": "User Information", + "Username": "Username", + "Status": "Status", + "Active": "Active", + "Inactive": "Inactive", + "Data Used": "Data Used", + "Data Limit": "Data Limit", + "Expiration Date": "Expiration Date", + "Reset In": "Reset In", + "Days": "Days", + "Subscription Link": "Subscription Link", + "Copy": "Copy", + "Copied": "Copied", + "QR Code": "QR Code", + "Unlimited": "Unlimited", + "Device Limit": "Device Limit", + "Devices": "Devices", + "No Limit": "No Limit", + "First Day of Month": "First Day of Month", + "Monthly": "Monthly", + "Never": "Never", + "First Day of Year": "First Day of Year", + "Yearly": "Yearly", + "update.local_newer": "Current version is newer than remote version, please commit your changes first", + "update.already_latest": "Already on the latest version", + "update.process_running": "Update process is already running", + "update.success": "Update successful, from :from to :to, system will restart automatically later", + "update.failed": "Update failed: :error", + "update.backup_failed": "Database backup failed: :error", + "update.code_update_failed": "Code update failed: :error", + "update.migration_failed": "Database migration failed: :error", + "update.cache_clear_failed": "Cache clearing failed: :error", + "update.flag_create_failed": "Failed to create update flag: :error", + "traffic_reset.reset_type.monthly": "Monthly Reset", + "traffic_reset.reset_type.first_day_month": "First Day of Month Reset", + "traffic_reset.reset_type.yearly": "Yearly Reset", + "traffic_reset.reset_type.first_day_year": "First Day of Year Reset", + "traffic_reset.reset_type.manual": "Manual Reset", + "traffic_reset.reset_type.purchase": "Purchase Reset Package", + "traffic_reset.source.auto": "Auto Trigger", + "traffic_reset.source.manual": "Manual Trigger", + "traffic_reset.source.api": "API Call", + "traffic_reset.source.cron": "Cron Job", + "traffic_reset.source.user_access": "User Access", + "traffic_reset.reset_success": "Traffic reset successful", + "traffic_reset.reset_failed": "Traffic reset failed, please check logs for details", + "traffic_reset.user_cannot_reset": "User cannot reset traffic (user not activated or no valid plan)" +} diff --git a/Xboard/resources/lang/ru-RU.json b/Xboard/resources/lang/ru-RU.json new file mode 100644 index 0000000..9ad67a5 --- /dev/null +++ b/Xboard/resources/lang/ru-RU.json @@ -0,0 +1,148 @@ +{ + "Article does not exist": "Статья не существует", + "Cancel failed": "Ошибка отмены", + "Close failed": "Ошибка закрытия", + "Coupon cannot be empty": "Купон не может быть пустым", + "Coupon failed": "Ошибка купона", + "Currency conversion has timed out, please try again later": "Время конвертации валюты истекло, попробуйте позже", + "Email already exists": "Эл. почта уже существует", + "Email suffix is not in the Whitelist": "Суффикс эл. почты не в белом списке", + "Email suffix is not in whitelist": "Суффикс эл. почты не в белом списке", + "Email verification code": "Код подтверждения эл. почты", + "Email verification code cannot be empty": "Код подтверждения эл. почты не может быть пустым", + "Email verification code has been sent, please request again later": "Код подтверждения отправлен, запросите повторно позже", + "Failed to create order": "Не удалось создать заказ", + "Failed to open ticket": "Не удалось открыть тикет", + "Gmail alias is not supported": "Псевдоним Gmail не поддерживается", + "Incorrect email or password": "Неверная эл. почта или пароль", + "Incorrect email verification code": "Неверный код подтверждения эл. почты", + "Insufficient balance": "Недостаточно средств", + "Insufficient commission balance": "Недостаточно комиссии", + "Invalid code is incorrect": "Неверный код", + "Invalid coupon": "Недействительный купон", + "Invalid invitation code": "Недействительный код приглашения", + "Invalid parameter": "Недопустимый параметр", + "Message cannot be empty": "Сообщение не может быть пустым", + "No active subscription. Unable to use our provided Apple ID": "Нет активной подписки. Невозможно использовать предоставленный Apple ID", + "Oops, there's a problem... Please refresh the page and try again later": "Ой, возникла проблема... Обновите страницу и попробуйте позже", + "Order does not exist": "Заказ не существует", + "Order does not exist or has been paid": "Заказ не существует или уже оплачен", + "Payment failed. Please check your credit card information": "Ошибка оплаты. Проверьте данные карты", + "Payment gateway request failed": "Ошибка запроса к платёжному шлюзу", + "Payment method is not available": "Способ оплаты недоступен", + "Please wait for the technical enginneer to reply": "Ожидайте ответа технического специалиста", + "Register failed": "Ошибка регистрации", + "Registration has closed": "Регистрация закрыта", + "Reset failed": "Ошибка сброса", + "Save failed": "Ошибка сохранения", + "Subscription has expired or no active subscription, unable to purchase Data Reset Package": "Подписка истекла или нет активной подписки, невозможно приобрести пакет сброса данных", + "Subscription plan does not exist": "Тарифный план не существует", + "The coupon code cannot be used for this subscription": "Код купона не может быть использован для этой подписки", + "The current required minimum withdrawal commission is :limit": "Текущая минимальная комиссия для вывода: :limit", + "The maximum number of creations has been reached": "Достигнуто максимальное количество созданий", + "The old password is wrong": "Неверный старый пароль", + "The ticket is closed and cannot be replied": "Тикет закрыт, ответ невозможен", + "The user does not exist": "Пользователь не существует", + "There are other unresolved tickets": "Есть другие нерешённые тикеты", + "This coupon has expired": "Этот купон истёк", + "This coupon has not yet started": "Этот купон ещё не начался", + "This coupon is no longer available": "Этот купон больше недоступен", + "This email is not registered in the system": "Эта эл. почта не зарегистрирована в системе", + "This payment cycle cannot be purchased, please choose another cycle": "Этот платёжный цикл нельзя приобрести, выберите другой", + "This subscription cannot be renewed, please change to another subscription": "Эту подписку нельзя продлить, выберите другую", + "This subscription has been sold out, please choose another subscription": "Эта подписка распродана, выберите другую", + "This subscription has expired, please change to another subscription": "Эта подписка истекла, выберите другую", + "Ticket does not exist": "Тикет не существует", + "Ticket reply failed": "Ошибка ответа на тикет", + "Token error": "Ошибка токена", + "Transfer failed": "Ошибка перевода", + "Unsupported withdrawal": "Вывод не поддерживается", + "Unsupported withdrawal method": "Способ вывода не поддерживается", + "Withdrawal account": "Счёт для вывода", + "Withdrawal method": "Способ вывода", + "You can only cancel pending orders": "Можно отменить только ожидающие заказы", + "You have an unpaid or pending order, please try again later or cancel it": "У вас есть неоплаченный или ожидающий заказ, попробуйте позже или отмените его", + "You must have a valid subscription to view content in this area": "Необходима действующая подписка для просмотра контента", + "You must use the invitation code to register": "Для регистрации необходимо использовать код приглашения", + "Your account has been suspended": "Ваш аккаунт приостановлен", + "[Commission Withdrawal Request] This ticket is opened by the system": "[Запрос на вывод комиссии] Тикет создан системой", + "Plan ID cannot be empty": "ID тарифа не может быть пустым", + "Plan cycle cannot be empty": "Цикл тарифа не может быть пустым", + "Wrong plan cycle": "Неверный цикл тарифа", + "Ticket subject cannot be empty": "Тема тикета не может быть пустой", + "Ticket level cannot be empty": "Уровень тикета не может быть пустым", + "Incorrect ticket level format": "Неверный формат уровня тикета", + "The withdrawal method cannot be empty": "Способ вывода не может быть пустым", + "The withdrawal account cannot be empty": "Счёт для вывода не может быть пустым", + "Old password cannot be empty": "Старый пароль не может быть пустым", + "New password cannot be empty": "Новый пароль не может быть пустым", + "Password must be greater than 8 digits": "Пароль должен быть длиннее 8 символов", + "The transfer amount cannot be empty": "Сумма перевода не может быть пустой", + "The transfer amount parameter is wrong": "Неверный параметр суммы перевода", + "Incorrect format of expiration reminder": "Неверный формат напоминания об истечении", + "Incorrect traffic alert format": "Неверный формат оповещения о трафике", + "Email can not be empty": "Эл. почта не может быть пустой", + "Email format is incorrect": "Неверный формат эл. почты", + "Password can not be empty": "Пароль не может быть пустым", + "The traffic usage in :app_name has reached 80%": "Использование трафика в :app_name достигло 80%", + "The service in :app_name is about to expire": "Сервис в :app_name скоро истекает", + "The coupon can only be used :limit_use_with_user per person": "Купон можно использовать только :limit_use_with_user раз на человека", + "The coupon code cannot be used for this period": "Код купона не может быть использован для этого периода", + "Request failed, please try again later": "Ошибка запроса, попробуйте позже", + "Register frequently, please try again after :minute minute": "Регистрация слишком частая, попробуйте через :minute минуту", + "Uh-oh, we've had some problems, we're working on it.": "Ой, у нас возникли проблемы, мы работаем над этим", + "This subscription reset package does not apply to your subscription": "Этот пакет сброса не适用于 вашей подписки", + "Login to :name": "Вход в :name", + "Sending frequently, please try again later": "Отправка слишком частая, попробуйте позже", + "Current product is sold out": "Товар распродан", + "There are too many password errors, please try again after :minute minutes.": "Слишком много ошибок пароля, попробуйте через :minute минут", + "Reset failed, Please try again later": "Ошибка сброса, попробуйте позже", + "Subscribe": "Подписаться", + "User Information": "Информация о пользователе", + "Username": "Имя пользователя", + "Status": "Статус", + "Active": "Активен", + "Inactive": "Неактивен", + "Data Used": "Использовано данных", + "Data Limit": "Лимит данных", + "Expiration Date": "Дата истечения", + "Reset In": "Сброс через", + "Days": "Дней", + "Subscription Link": "Ссылка подписки", + "Copy": "Копировать", + "Copied": "Скопировано", + "QR Code": "QR-код", + "Unlimited": "Без ограничений", + "Device Limit": "Лимит устройств", + "Devices": "Устройства", + "No Limit": "Без лимита", + "First Day of Month": "Первый день месяца", + "Monthly": "Ежемесячно", + "Never": "Никогда", + "First Day of Year": "Первый день года", + "Yearly": "Ежегодно", + "update.local_newer": "Текущая версия новее удалённой, сначала закоммитьте изменения", + "update.already_latest": "Уже установлена последняя версия", + "update.process_running": "Процесс обновления уже запущен", + "update.success": "Обновление успешно, с :from до :to, система перезагрузится автоматически", + "update.failed": "Ошибка обновления: :error", + "update.backup_failed": "Ошибка резервного копирования БД: :error", + "update.code_update_failed": "Ошибка обновления кода: :error", + "update.migration_failed": "Ошибка миграции БД: :error", + "update.cache_clear_failed": "Ошибка очистки кэша: :error", + "update.flag_create_failed": "Не удалось создать флаг обновления: :error", + "traffic_reset.reset_type.monthly": "Ежемесячный сброс", + "traffic_reset.reset_type.first_day_month": "Сброс в первый день месяца", + "traffic_reset.reset_type.yearly": "Ежегодный сброс", + "traffic_reset.reset_type.first_day_year": "Сброс в первый день года", + "traffic_reset.reset_type.manual": "Ручной сброс", + "traffic_reset.reset_type.purchase": "Приобретение пакета сброса", + "traffic_reset.source.auto": "Автоматический запуск", + "traffic_reset.source.manual": "Ручной запуск", + "traffic_reset.source.api": "Вызов API", + "traffic_reset.source.cron": "Cron-задача", + "traffic_reset.source.user_access": "Доступ пользователя", + "traffic_reset.reset_success": "Трафик успешно сброшен", + "traffic_reset.reset_failed": "Ошибка сброса трафика, подробности в логах", + "traffic_reset.user_cannot_reset": "Пользователь не может сбросить трафик (аккаунт не активен или нет действующего тарифа)" +} diff --git a/Xboard/resources/lang/zh-CN.json b/Xboard/resources/lang/zh-CN.json new file mode 100644 index 0000000..0826fb1 --- /dev/null +++ b/Xboard/resources/lang/zh-CN.json @@ -0,0 +1,148 @@ +{ + "The user does not exist": "该用户不存在", + "The old password is wrong": "旧密码有误", + "Save failed": "保存失败", + "Subscription plan does not exist": "订阅计划不存在", + "Reset failed": "重置失败", + "Invalid parameter": "参数错误", + "Insufficient commission balance": "推广佣金余额不足", + "Transfer failed": "划转失败", + "Ticket does not exist": "工单不存在", + "There are other unresolved tickets": "存在其它工单尚未处理", + "Failed to open ticket": "工单创建失败", + "Message cannot be empty": "消息不能为空", + "The ticket is closed and cannot be replied": "工单已关闭,无法回复", + "Please wait for the technical enginneer to reply": "请等待技术支持回复", + "Ticket reply failed": "工单回复失败", + "Close failed": "关闭失败", + "Unsupported withdrawal method": "不支持的提现方式", + "The current required minimum withdrawal commission is :limit": "当前系统要求的最少提现佣金为:¥:limitCNY", + "[Commission Withdrawal Request] This ticket is opened by the system": "[提现申请] 本工单由系统发出", + "Withdrawal method": "提现方式", + "Withdrawal account": "提现账号", + "Unsupported withdrawal": "不支持提现", + "Order does not exist": "订单不存在", + "You have an unpaid or pending order, please try again later or cancel it": "您有未付款或开通中的订单,请稍后再试或将其取消", + "This subscription has been sold out, please choose another subscription": "该订阅已售罄,请更换其它订阅", + "This subscription cannot be renewed, please change to another subscription": "该订阅无法续费,请更换其它订阅", + "This payment period cannot be purchased, please choose another period": "该订阅周期无法进行购买,请选择其它周期", + "Subscription has expired or no active subscription, unable to purchase Data Reset Package": "订阅已过期或无有效订阅,无法购买重置包", + "This subscription has expired, please change to another subscription": "订阅已过期,请更换其它订阅", + "Coupon failed": "优惠券使用失败", + "Insufficient balance": "余额不足", + "Failed to create order": "订单创建失败", + "Order does not exist or has been paid": "订单不存在或已支付", + "Payment method is not available": "支付方式不可用", + "You can only cancel pending orders": "只可以取消待支付订单", + "Cancel failed": "取消失败", + "Currency conversion has timed out, please try again later": "货币转换超时,请稍后再试", + "Payment gateway request failed": "支付网关请求失败", + "Oops, there's a problem... Please refresh the page and try again later": "出现了点问题,请刷新页面稍后再试", + "Payment failed. Please check your credit card information": "扣款失败,请检查信用卡信息", + "Article does not exist": "文章不存在", + "No active subscription. Unable to use our provided Apple ID": "无有效订阅,无法使用本站提供的 AppleID", + "You must have a valid subscription to view content in this area": "您必须拥有有效的订阅才可以查看该区域的内容", + "The maximum number of creations has been reached": "已达到创建数量上限", + "Coupon cannot be empty": "优惠券不能为空", + "This coupon is no longer available": "优惠券已无可用次数", + "This coupon has not yet started": "优惠券还未到可用时间", + "This coupon has expired": "优惠券已过期", + "The coupon code cannot be used for this subscription": "该订阅无法使用此优惠码", + "Invalid coupon": "优惠券无效", + "Invalid code is incorrect": "验证码有误", + "Email suffix is not in the Whitelist": "邮箱后缀不处于白名单中", + "Email suffix is not in whitelist": "邮箱后缀不在白名单中", + "Gmail alias is not supported": "不支持 Gmail 别名邮箱", + "Registration has closed": "本站已关闭注册", + "You must use the invitation code to register": "必须使用邀请码才可以注册", + "Email verification code cannot be empty": "邮箱验证码不能为空", + "Incorrect email verification code": "邮箱验证码有误", + "Email already exists": "邮箱已在系统中存在", + "Invalid invitation code": "邀请码无效", + "Register failed": "注册失败", + "Incorrect email or password": "邮箱或密码错误", + "Your account has been suspended": "该账户已被停止使用", + "Token error": "令牌有误", + "This email is not registered in the system": "该邮箱不存在系统中", + "Email verification code has been sent, please request again later": "验证码已发送,请过一会儿再请求", + "Email verification code": "邮箱验证码", + "Plan ID cannot be empty": "套餐 ID 不能为空", + "Plan period cannot be empty": "套餐周期不能为空", + "Wrong plan period": "套餐周期参数有误", + "Ticket subject cannot be empty": "工单主题不能为空", + "Ticket level cannot be empty": "工单等级不能为空", + "Incorrect ticket level format": "工单等级参数有误", + "The withdrawal method cannot be empty": "提现方式不能为空", + "The withdrawal account cannot be empty": "提现账号不能为空", + "Old password cannot be empty": "旧密码不能为空", + "New password cannot be empty": "新密码不能为空", + "Password must be greater than 8 digits": "密码必须大于 8 个字符", + "The transfer amount cannot be empty": "划转金额不能为空", + "The transfer amount parameter is wrong": "划转金额参数有误", + "Incorrect format of expiration reminder": "过期提醒参数有误", + "Incorrect traffic alert format": "流量提醒参数有误", + "Email can not be empty": "邮箱不能为空", + "Email format is incorrect": "邮箱格式不正确", + "Password can not be empty": "密码不能为空", + "The traffic usage in :app_name has reached 80%": "在 :app_name 的已用流量已达到 80%", + "The service in :app_name is about to expire": "在 :app_name 的服务即将到期", + "The coupon can only be used :limit_use_with_user per person": "该优惠券每人只能用 :limit_use_with_user 次", + "The coupon code cannot be used for this period": "此优惠券无法用于该付款周期", + "Request failed, please try again later": "请求失败,请稍后再试", + "Register frequently, please try again after :minute minute": "注册频繁,请等待 :minute 分钟后再次尝试", + "Uh-oh, we've had some problems, we're working on it.": "遇到了些问题,我们正在进行处理", + "This subscription reset package does not apply to your subscription": "该订阅重置包不适用于你的订阅", + "Login to :name": "登入到 :name", + "Sending frequently, please try again later": "发送频繁,请稍后再试", + "Current product is sold out": "当前商品已售罄", + "There are too many password errors, please try again after :minute minutes.": "密码错误次数过多,请 :minute 分钟后再试", + "Reset failed, Please try again later": "重置失败,请稍后再试", + "Subscribe": "订阅信息", + "User Information": "用户信息", + "Username": "用户名", + "Status": "状态", + "Active": "正常", + "Inactive": "未激活", + "Data Used": "已用流量", + "Data Limit": "流量限制", + "Expiration Date": "到期时间", + "Reset In": "距离重置", + "Days": "天", + "Subscription Link": "订阅链接", + "Copy": "复制", + "Copied": "已复制", + "QR Code": "二维码", + "Unlimited": "长期有效", + "Device Limit": "设备限制", + "Devices": "台设备", + "No Limit": "不限制", + "First Day of Month": "每月1号", + "Monthly": "按月", + "Never": "不重置", + "First Day of Year": "每年1月1日", + "Yearly": "按年", + "update.local_newer": "当前版本比远程版本更新,请先提交您的更改", + "update.already_latest": "当前已经是最新版本", + "update.process_running": "更新进程正在运行中", + "update.success": "更新成功,从 :from 更新到 :to, 系统将在稍后自动重启", + "update.failed": "更新失败: :error", + "update.backup_failed": "数据库备份失败: :error", + "update.code_update_failed": "代码更新失败: :error", + "update.migration_failed": "数据库迁移失败: :error", + "update.cache_clear_failed": "缓存清理失败: :error", + "update.flag_create_failed": "创建更新标记失败: :error", + "traffic_reset.reset_type.monthly": "按月重置", + "traffic_reset.reset_type.first_day_month": "每月1号重置", + "traffic_reset.reset_type.yearly": "按年重置", + "traffic_reset.reset_type.first_day_year": "每年1月1日重置", + "traffic_reset.reset_type.manual": "手动重置", + "traffic_reset.reset_type.purchase": "购买重置包", + "traffic_reset.source.auto": "自动触发", + "traffic_reset.source.manual": "手动触发", + "traffic_reset.source.api": "API调用", + "traffic_reset.source.cron": "定时任务", + "traffic_reset.source.user_access": "用户访问", + "traffic_reset.reset_success": "流量重置成功", + "traffic_reset.reset_failed": "流量重置失败,请查看日志获取详细信息", + "traffic_reset.user_cannot_reset": "该用户当前不能重置流量(用户未激活或无有效套餐)" +} diff --git a/Xboard/resources/lang/zh-TW.json b/Xboard/resources/lang/zh-TW.json new file mode 100644 index 0000000..c3c20b2 --- /dev/null +++ b/Xboard/resources/lang/zh-TW.json @@ -0,0 +1,148 @@ +{ + "The user does not exist": "該用戶不存在", + "The old password is wrong": "舊密碼有誤", + "Save failed": "保存失敗", + "Subscription plan does not exist": "訂閱計劃不存在", + "Reset failed": "重置失敗", + "Invalid parameter": "參數錯誤", + "Insufficient commission balance": "推廣佣金餘額不足", + "Transfer failed": "劃轉失敗", + "Ticket does not exist": "工單不存在", + "There are other unresolved tickets": "存在其他工單尚未處理", + "Failed to open ticket": "工單創建失敗", + "Message cannot be empty": "消息不能為空", + "The ticket is closed and cannot be replied": "工單已關閉,無法回復", + "Please wait for the technical enginneer to reply": "請等待技術支持回復", + "Ticket reply failed": "工單回復失敗", + "Close failed": "關閉失敗", + "Unsupported withdrawal method": "不支持的提現方式", + "The current required minimum withdrawal commission is :limit": "當前系統要求的最少提現佣金為:¥:limitCNY", + "[Commission Withdrawal Request] This ticket is opened by the system": "[提現申請] 本工單由系統發出", + "Withdrawal method": "提現方式", + "Withdrawal account": "提現賬號", + "Unsupported withdrawal": "不支持提現", + "Order does not exist": "訂單不存在", + "You have an unpaid or pending order, please try again later or cancel it": "您有未付款或開通中的訂單,請稍後再試或將其取消", + "This subscription has been sold out, please choose another subscription": "該訂閱已售罄,請更換其它訂閱", + "This subscription cannot be renewed, please change to another subscription": "該訂閱無法續費,請更換其它訂閱", + "This payment period cannot be purchased, please choose another period": "該訂閱週期無法進行購買,請選擇其它週期", + "Subscription has expired or no active subscription, unable to purchase Data Reset Package": "訂閱已過期或無有效訂閱,無法購買重置包", + "This subscription has expired, please change to another subscription": "訂閱已過期,請更換其它訂閱", + "Coupon failed": "優惠券使用失敗", + "Insufficient balance": "餘額不足", + "Failed to create order": "訂單創建失敗", + "Order does not exist or has been paid": "訂單不存在或已支付", + "Payment method is not available": "支付方式不可用", + "You can only cancel pending orders": "只可以取消待支付訂單", + "Cancel failed": "取消失敗", + "Currency conversion has timed out, please try again later": "貨幣轉換超時,請稍後再試", + "Payment gateway request failed": "支付網關請求失敗", + "Oops, there's a problem... Please refresh the page and try again later": "出現了點問題,請刷新頁面稍後再試", + "Payment failed. Please check your credit card information": "扣款失敗,請檢查信用卡信息", + "Article does not exist": "文章不存在", + "No active subscription. Unable to use our provided Apple ID": "無有效訂閱,無法使用本站提供的 Apple ID", + "You must have a valid subscription to view content in this area": "您必須擁有有效的訂閱才可以查看該區域的內容", + "The maximum number of creations has been reached": "已達到創建數量上限", + "Coupon cannot be empty": "優惠券不能為空", + "This coupon is no longer available": "優惠券已無可用次數", + "This coupon has not yet started": "優惠券還未到可用時間", + "This coupon has expired": "優惠券已過期", + "The coupon code cannot be used for this subscription": "該訂閱無法使用此優惠碼", + "Invalid coupon": "優惠券無效", + "Invalid code is incorrect": "驗證碼有誤", + "Email suffix is not in the Whitelist": "郵箱後綴不處於白名單中", + "Email suffix is not in whitelist": "郵箱後綴不在白名單中", + "Gmail alias is not supported": "不支持 Gmail 別名郵箱", + "Registration has closed": "本站已關閉註冊", + "You must use the invitation code to register": "必須使用邀請碼才可以註冊", + "Email verification code cannot be empty": "郵箱驗證碼不能為空", + "Incorrect email verification code": "郵箱驗證碼有誤", + "Email already exists": "郵箱已在系統中存在", + "Invalid invitation code": "邀請碼無效", + "Register failed": "註冊失敗", + "Incorrect email or password": "郵箱或密碼錯誤", + "Your account has been suspended": "該賬戶已被停止使用", + "Token error": "令牌有誤", + "This email is not registered in the system": "該郵箱不存在系統中", + "Email verification code has been sent, please request again later": "驗證碼已發送,請過一會兒再請求", + "Email verification code": "郵箱驗證碼", + "Plan ID cannot be empty": "套餐 ID 不能為空", + "Plan period cannot be empty": "套餐週期不能為空", + "Wrong plan period": "套餐週期參數有誤", + "Ticket subject cannot be empty": "工單主題不能為空", + "Ticket level cannot be empty": "工單等級不能為空", + "Incorrect ticket level format": "工單等級參數有誤", + "The withdrawal method cannot be empty": "提現方式不能為空", + "The withdrawal account cannot be empty": "提現賬號不能為空", + "Old password cannot be empty": "舊密碼不能為空", + "New password cannot be empty": "新密碼不能為空", + "Password must be greater than 8 digits": "密碼必須大於 8 個字符", + "The transfer amount cannot be empty": "劃轉金額不能為空", + "The transfer amount parameter is wrong": "劃轉金額參數有誤", + "Incorrect format of expiration reminder": "過期提醒參數有誤", + "Incorrect traffic alert format": "流量提醒參數有誤", + "Email can not be empty": "郵箱不能為空", + "Email format is incorrect": "郵箱格式不正確", + "Password can not be empty": "密碼不能為空", + "The traffic usage in :app_name has reached 80%": "在 :app_name 的已用流量已達到 80%", + "The service in :app_name is about to expire": "在 :app_name 的服務即將到期", + "The coupon can only be used :limit_use_with_user per person": "該優惠券每人只能用 :limit_use_with_user 次", + "The coupon code cannot be used for this period": "此優惠券無法用於該付款週期", + "Request failed, please try again later": "請求失敗,請稍後再試", + "Register frequently, please try again after :minute minute": "註冊頻繁,請等待 :minute 分鐘後再次嘗試", + "Uh-oh, we've had some problems, we're working on it.": "遇到了些問題,我們正在進行處理", + "This subscription reset package does not apply to your subscription": "該訂閱重置包不適用於你的訂閱", + "Login to :name": "登入到 :name", + "Sending frequently, please try again later": "發送頻繁,請稍後再試", + "Current product is sold out": "當前商品已售罄", + "There are too many password errors, please try again after :minute minutes.": "密碼錯誤次數過多,請 :minute 分鐘後再試", + "Reset failed, Please try again later": "重置失敗,請稍後再試", + "Subscribe": "訂閱資訊", + "User Information": "用戶資訊", + "Username": "用戶名", + "Status": "狀態", + "Active": "正常", + "Inactive": "未啟用", + "Data Used": "已用流量", + "Data Limit": "流量限制", + "Expiration Date": "到期時間", + "Reset In": "距離重置", + "Days": "天", + "Subscription Link": "訂閱連結", + "Copy": "複製", + "Copied": "已複製", + "QR Code": "二維碼", + "Unlimited": "長期有效", + "Device Limit": "設備限制", + "Devices": "台設備", + "No Limit": "不限制", + "First Day of Month": "每月1號", + "Monthly": "按月", + "Never": "不重置", + "First Day of Year": "每年1月1日", + "Yearly": "按年", + "update.local_newer": "當前版本比遠程版本更新,請先提交您的更改", + "update.already_latest": "當前已經是最新版本", + "update.process_running": "更新進程正在運行中", + "update.success": "更新成功,從 :from 更新到 :to, 系統將在稍後自動重啟", + "update.failed": "更新失敗: :error", + "update.backup_failed": "數據庫備份失敗: :error", + "update.code_update_failed": "代碼更新失敗: :error", + "update.migration_failed": "數據庫遷移失敗: :error", + "update.cache_clear_failed": "緩存清理失敗: :error", + "update.flag_create_failed": "創建更新標記失敗: :error", + "traffic_reset.reset_type.monthly": "按月重置", + "traffic_reset.reset_type.first_day_month": "每月1號重置", + "traffic_reset.reset_type.yearly": "按年重置", + "traffic_reset.reset_type.first_day_year": "每年1月1日重置", + "traffic_reset.reset_type.manual": "手動重置", + "traffic_reset.reset_type.purchase": "購買重置包", + "traffic_reset.source.auto": "自動觸發", + "traffic_reset.source.manual": "手動觸發", + "traffic_reset.source.api": "API調用", + "traffic_reset.source.cron": "定時任務", + "traffic_reset.source.user_access": "用戶訪問", + "traffic_reset.reset_success": "流量重置成功", + "traffic_reset.reset_failed": "流量重置失敗,請查看日誌獲取詳細信息", + "traffic_reset.user_cannot_reset": "該用戶當前不能重置流量(用戶未激活或無有效套餐)" +} diff --git a/Xboard/resources/rules/.gitignore b/Xboard/resources/rules/.gitignore new file mode 100644 index 0000000..8b806a3 --- /dev/null +++ b/Xboard/resources/rules/.gitignore @@ -0,0 +1 @@ +custom.* diff --git a/Xboard/resources/rules/app.clash.yaml b/Xboard/resources/rules/app.clash.yaml new file mode 100644 index 0000000..6d41444 --- /dev/null +++ b/Xboard/resources/rules/app.clash.yaml @@ -0,0 +1,557 @@ +# port: 7890 +# socks-port: 7891 +# redir-port: 7892 +# tproxy-port: 7893 +mixed-port: 7890 +allow-lan: true +bind-address: "*" +mode: rule +log-level: info +external-controller: 127.0.0.1:9090 + +proxies: + +proxy-groups: + - { name: "SELECT", type: select, proxies: ["自动选择", "故障转移"] } + - { name: "自动选择", type: url-test, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 86400 } + - { name: "故障转移", type: fallback, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 7200 } + +rules: + # 自定义规则 + ## 您可以在此处插入您补充的自定义规则(请注意保持缩进) + + # Google 中国服务 + - DOMAIN-SUFFIX,services.googleapis.cn,SELECT + - DOMAIN-SUFFIX,xn--ngstr-lra8j.com,SELECT + + # Apple + - DOMAIN,safebrowsing.urlsec.qq.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。 + - DOMAIN,safebrowsing.googleapis.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。 + - DOMAIN,developer.apple.com,SELECT + - DOMAIN-SUFFIX,digicert.com,SELECT + - DOMAIN,ocsp.apple.com,SELECT + - DOMAIN,ocsp.comodoca.com,SELECT + - DOMAIN,ocsp.usertrust.com,SELECT + - DOMAIN,ocsp.sectigo.com,SELECT + - DOMAIN,ocsp.verisign.net,SELECT + - DOMAIN-SUFFIX,apple-dns.net,SELECT + - DOMAIN,testflight.apple.com,SELECT + - DOMAIN,sandbox.itunes.apple.com,SELECT + - DOMAIN,itunes.apple.com,SELECT + - DOMAIN-SUFFIX,apps.apple.com,SELECT + - DOMAIN-SUFFIX,blobstore.apple.com,SELECT + - DOMAIN,cvws.icloud-content.com,SELECT + - DOMAIN-SUFFIX,mzstatic.com,DIRECT + - DOMAIN-SUFFIX,itunes.apple.com,DIRECT + - DOMAIN-SUFFIX,icloud.com,DIRECT + - DOMAIN-SUFFIX,icloud-content.com,DIRECT + - DOMAIN-SUFFIX,me.com,DIRECT + - DOMAIN-SUFFIX,aaplimg.com,DIRECT + - DOMAIN-SUFFIX,cdn20.com,DIRECT + - DOMAIN-SUFFIX,cdn-apple.com,DIRECT + - DOMAIN-SUFFIX,akadns.net,DIRECT + - DOMAIN-SUFFIX,akamaiedge.net,DIRECT + - DOMAIN-SUFFIX,edgekey.net,DIRECT + - DOMAIN-SUFFIX,mwcloudcdn.com,DIRECT + - DOMAIN-SUFFIX,mwcname.com,DIRECT + - DOMAIN-SUFFIX,apple.com,DIRECT + - DOMAIN-SUFFIX,apple-cloudkit.com,DIRECT + - DOMAIN-SUFFIX,apple-mapkit.com,DIRECT + # - DOMAIN,e.crashlytics.com,REJECT //注释此选项有助于大多数App开发者分析崩溃信息;如果您拒绝一切崩溃数据统计、搜集,请取消 # 注释。 + + # 国内网站 + - DOMAIN-SUFFIX,126.com,DIRECT + - DOMAIN-SUFFIX,126.net,DIRECT + - DOMAIN-SUFFIX,127.net,DIRECT + - DOMAIN-SUFFIX,163.com,DIRECT + - DOMAIN-SUFFIX,360buyimg.com,DIRECT + - DOMAIN-SUFFIX,36kr.com,DIRECT + - DOMAIN-SUFFIX,acfun.tv,DIRECT + - DOMAIN-SUFFIX,air-matters.com,DIRECT + - DOMAIN-SUFFIX,aixifan.com,DIRECT + - DOMAIN-KEYWORD,alicdn,DIRECT + - DOMAIN-KEYWORD,alipay,DIRECT + - DOMAIN-KEYWORD,taobao,DIRECT + - DOMAIN-SUFFIX,amap.com,DIRECT + - DOMAIN-SUFFIX,autonavi.com,DIRECT + - DOMAIN-KEYWORD,baidu,DIRECT + - DOMAIN-SUFFIX,bdimg.com,DIRECT + - DOMAIN-SUFFIX,bdstatic.com,DIRECT + - DOMAIN-SUFFIX,bilibili.com,DIRECT + - DOMAIN-SUFFIX,bilivideo.com,DIRECT + - DOMAIN-SUFFIX,caiyunapp.com,DIRECT + - DOMAIN-SUFFIX,clouddn.com,DIRECT + - DOMAIN-SUFFIX,cnbeta.com,DIRECT + - DOMAIN-SUFFIX,cnbetacdn.com,DIRECT + - DOMAIN-SUFFIX,cootekservice.com,DIRECT + - DOMAIN-SUFFIX,csdn.net,DIRECT + - DOMAIN-SUFFIX,ctrip.com,DIRECT + - DOMAIN-SUFFIX,dgtle.com,DIRECT + - DOMAIN-SUFFIX,dianping.com,DIRECT + - DOMAIN-SUFFIX,douban.com,DIRECT + - DOMAIN-SUFFIX,doubanio.com,DIRECT + - DOMAIN-SUFFIX,duokan.com,DIRECT + - DOMAIN-SUFFIX,easou.com,DIRECT + - DOMAIN-SUFFIX,ele.me,DIRECT + - DOMAIN-SUFFIX,feng.com,DIRECT + - DOMAIN-SUFFIX,fir.im,DIRECT + - DOMAIN-SUFFIX,frdic.com,DIRECT + - DOMAIN-SUFFIX,g-cores.com,DIRECT + - DOMAIN-SUFFIX,godic.net,DIRECT + - DOMAIN-SUFFIX,gtimg.com,DIRECT + - DOMAIN,cdn.hockeyapp.net,DIRECT + - DOMAIN-SUFFIX,hongxiu.com,DIRECT + - DOMAIN-SUFFIX,hxcdn.net,DIRECT + - DOMAIN-SUFFIX,iciba.com,DIRECT + - DOMAIN-SUFFIX,ifeng.com,DIRECT + - DOMAIN-SUFFIX,ifengimg.com,DIRECT + - DOMAIN-SUFFIX,ipip.net,DIRECT + - DOMAIN-SUFFIX,iqiyi.com,DIRECT + - DOMAIN-SUFFIX,jd.com,DIRECT + - DOMAIN-SUFFIX,jianshu.com,DIRECT + - DOMAIN-SUFFIX,knewone.com,DIRECT + - DOMAIN-SUFFIX,le.com,DIRECT + - DOMAIN-SUFFIX,lecloud.com,DIRECT + - DOMAIN-SUFFIX,lemicp.com,DIRECT + - DOMAIN-SUFFIX,licdn.com,DIRECT + - DOMAIN-SUFFIX,luoo.net,DIRECT + - DOMAIN-SUFFIX,meituan.com,DIRECT + - DOMAIN-SUFFIX,meituan.net,DIRECT + - DOMAIN-SUFFIX,mi.com,DIRECT + - DOMAIN-SUFFIX,miaopai.com,DIRECT + - DOMAIN-SUFFIX,microsoft.com,DIRECT + - DOMAIN-SUFFIX,microsoftonline.com,DIRECT + - DOMAIN-SUFFIX,miui.com,DIRECT + - DOMAIN-SUFFIX,miwifi.com,DIRECT + - DOMAIN-SUFFIX,mob.com,DIRECT + - DOMAIN-SUFFIX,netease.com,DIRECT + - DOMAIN-SUFFIX,office.com,DIRECT + - DOMAIN-SUFFIX,office365.com,DIRECT + - DOMAIN-KEYWORD,officecdn,DIRECT + - DOMAIN-SUFFIX,oschina.net,DIRECT + - DOMAIN-SUFFIX,ppsimg.com,DIRECT + - DOMAIN-SUFFIX,pstatp.com,DIRECT + - DOMAIN-SUFFIX,qcloud.com,DIRECT + - DOMAIN-SUFFIX,qdaily.com,DIRECT + - DOMAIN-SUFFIX,qdmm.com,DIRECT + - DOMAIN-SUFFIX,qhimg.com,DIRECT + - DOMAIN-SUFFIX,qhres.com,DIRECT + - DOMAIN-SUFFIX,qidian.com,DIRECT + - DOMAIN-SUFFIX,qihucdn.com,DIRECT + - DOMAIN-SUFFIX,qiniu.com,DIRECT + - DOMAIN-SUFFIX,qiniucdn.com,DIRECT + - DOMAIN-SUFFIX,qiyipic.com,DIRECT + - DOMAIN-SUFFIX,qq.com,DIRECT + - DOMAIN-SUFFIX,qqurl.com,DIRECT + - DOMAIN-SUFFIX,rarbg.to,DIRECT + - DOMAIN-SUFFIX,ruguoapp.com,DIRECT + - DOMAIN-SUFFIX,segmentfault.com,DIRECT + - DOMAIN-SUFFIX,sinaapp.com,DIRECT + - DOMAIN-SUFFIX,smzdm.com,DIRECT + - DOMAIN-SUFFIX,snapdrop.net,DIRECT + - DOMAIN-SUFFIX,sogou.com,DIRECT + - DOMAIN-SUFFIX,sogoucdn.com,DIRECT + - DOMAIN-SUFFIX,sohu.com,DIRECT + - DOMAIN-SUFFIX,soku.com,DIRECT + - DOMAIN-SUFFIX,speedtest.net,DIRECT + - DOMAIN-SUFFIX,sspai.com,DIRECT + - DOMAIN-SUFFIX,suning.com,DIRECT + - DOMAIN-SUFFIX,taobao.com,DIRECT + - DOMAIN-SUFFIX,tencent.com,DIRECT + - DOMAIN-SUFFIX,tenpay.com,DIRECT + - DOMAIN-SUFFIX,tianyancha.com,DIRECT + - DOMAIN-SUFFIX,tmall.com,DIRECT + - DOMAIN-SUFFIX,tudou.com,DIRECT + - DOMAIN-SUFFIX,umetrip.com,DIRECT + - DOMAIN-SUFFIX,upaiyun.com,DIRECT + - DOMAIN-SUFFIX,upyun.com,DIRECT + - DOMAIN-SUFFIX,veryzhun.com,DIRECT + - DOMAIN-SUFFIX,weather.com,DIRECT + - DOMAIN-SUFFIX,weibo.com,DIRECT + - DOMAIN-SUFFIX,xiami.com,DIRECT + - DOMAIN-SUFFIX,xiami.net,DIRECT + - DOMAIN-SUFFIX,xiaomicp.com,DIRECT + - DOMAIN-SUFFIX,ximalaya.com,DIRECT + - DOMAIN-SUFFIX,xmcdn.com,DIRECT + - DOMAIN-SUFFIX,xunlei.com,DIRECT + - DOMAIN-SUFFIX,yhd.com,DIRECT + - DOMAIN-SUFFIX,yihaodianimg.com,DIRECT + - DOMAIN-SUFFIX,yinxiang.com,DIRECT + - DOMAIN-SUFFIX,ykimg.com,DIRECT + - DOMAIN-SUFFIX,youdao.com,DIRECT + - DOMAIN-SUFFIX,youku.com,DIRECT + - DOMAIN-SUFFIX,zealer.com,DIRECT + - DOMAIN-SUFFIX,zhihu.com,DIRECT + - DOMAIN-SUFFIX,zhimg.com,DIRECT + - DOMAIN-SUFFIX,zimuzu.tv,DIRECT + - DOMAIN-SUFFIX,zoho.com,DIRECT + + # 抗 DNS 污染 + - DOMAIN-KEYWORD,amazon,SELECT + - DOMAIN-KEYWORD,google,SELECT + - DOMAIN-KEYWORD,gmail,SELECT + - DOMAIN-KEYWORD,youtube,SELECT + - DOMAIN-KEYWORD,facebook,SELECT + - DOMAIN-SUFFIX,fb.me,SELECT + - DOMAIN-SUFFIX,fbcdn.net,SELECT + - DOMAIN-KEYWORD,twitter,SELECT + - DOMAIN-KEYWORD,instagram,SELECT + - DOMAIN-KEYWORD,dropbox,SELECT + - DOMAIN-SUFFIX,twimg.com,SELECT + - DOMAIN-KEYWORD,blogspot,SELECT + - DOMAIN-SUFFIX,youtu.be,SELECT + - DOMAIN-KEYWORD,whatsapp,SELECT + + # 常见广告域名屏蔽 + - DOMAIN-KEYWORD,admarvel,REJECT + - DOMAIN-KEYWORD,admaster,REJECT + - DOMAIN-KEYWORD,adsage,REJECT + - DOMAIN-KEYWORD,adsmogo,REJECT + - DOMAIN-KEYWORD,adsrvmedia,REJECT + - DOMAIN-KEYWORD,adwords,REJECT + - DOMAIN-KEYWORD,adservice,REJECT + - DOMAIN-SUFFIX,appsflyer.com,REJECT + - DOMAIN-KEYWORD,domob,REJECT + - DOMAIN-SUFFIX,doubleclick.net,REJECT + - DOMAIN-KEYWORD,duomeng,REJECT + - DOMAIN-KEYWORD,dwtrack,REJECT + - DOMAIN-KEYWORD,guanggao,REJECT + - DOMAIN-KEYWORD,lianmeng,REJECT + - DOMAIN-SUFFIX,mmstat.com,REJECT + - DOMAIN-KEYWORD,mopub,REJECT + - DOMAIN-KEYWORD,omgmta,REJECT + - DOMAIN-KEYWORD,openx,REJECT + - DOMAIN-KEYWORD,partnerad,REJECT + - DOMAIN-KEYWORD,pingfore,REJECT + - DOMAIN-KEYWORD,supersonicads,REJECT + - DOMAIN-KEYWORD,uedas,REJECT + - DOMAIN-KEYWORD,umeng,REJECT + - DOMAIN-KEYWORD,usage,REJECT + - DOMAIN-SUFFIX,vungle.com,REJECT + - DOMAIN-KEYWORD,wlmonitor,REJECT + - DOMAIN-KEYWORD,zjtoolbar,REJECT + + # 国外网站 + - DOMAIN-SUFFIX,9to5mac.com,SELECT + - DOMAIN-SUFFIX,abpchina.org,SELECT + - DOMAIN-SUFFIX,adblockplus.org,SELECT + - DOMAIN-SUFFIX,adobe.com,SELECT + - DOMAIN-SUFFIX,akamaized.net,SELECT + - DOMAIN-SUFFIX,alfredapp.com,SELECT + - DOMAIN-SUFFIX,amplitude.com,SELECT + - DOMAIN-SUFFIX,ampproject.org,SELECT + - DOMAIN-SUFFIX,android.com,SELECT + - DOMAIN-SUFFIX,angularjs.org,SELECT + - DOMAIN-SUFFIX,aolcdn.com,SELECT + - DOMAIN-SUFFIX,apkpure.com,SELECT + - DOMAIN-SUFFIX,appledaily.com,SELECT + - DOMAIN-SUFFIX,appshopper.com,SELECT + - DOMAIN-SUFFIX,appspot.com,SELECT + - DOMAIN-SUFFIX,arcgis.com,SELECT + - DOMAIN-SUFFIX,archive.org,SELECT + - DOMAIN-SUFFIX,armorgames.com,SELECT + - DOMAIN-SUFFIX,aspnetcdn.com,SELECT + - DOMAIN-SUFFIX,att.com,SELECT + - DOMAIN-SUFFIX,awsstatic.com,SELECT + - DOMAIN-SUFFIX,azureedge.net,SELECT + - DOMAIN-SUFFIX,azurewebsites.net,SELECT + - DOMAIN-SUFFIX,bing.com,SELECT + - DOMAIN-SUFFIX,bintray.com,SELECT + - DOMAIN-SUFFIX,bit.com,SELECT + - DOMAIN-SUFFIX,bit.ly,SELECT + - DOMAIN-SUFFIX,bitbucket.org,SELECT + - DOMAIN-SUFFIX,bjango.com,SELECT + - DOMAIN-SUFFIX,bkrtx.com,SELECT + - DOMAIN-SUFFIX,blog.com,SELECT + - DOMAIN-SUFFIX,blogcdn.com,SELECT + - DOMAIN-SUFFIX,blogger.com,SELECT + - DOMAIN-SUFFIX,blogsmithmedia.com,SELECT + - DOMAIN-SUFFIX,blogspot.com,SELECT + - DOMAIN-SUFFIX,blogspot.hk,SELECT + - DOMAIN-SUFFIX,bloomberg.com,SELECT + - DOMAIN-SUFFIX,box.com,SELECT + - DOMAIN-SUFFIX,box.net,SELECT + - DOMAIN-SUFFIX,cachefly.net,SELECT + - DOMAIN-SUFFIX,chromium.org,SELECT + - DOMAIN-SUFFIX,cl.ly,SELECT + - DOMAIN-SUFFIX,cloudflare.com,SELECT + - DOMAIN-SUFFIX,cloudfront.net,SELECT + - DOMAIN-SUFFIX,cloudmagic.com,SELECT + - DOMAIN-SUFFIX,cmail19.com,SELECT + - DOMAIN-SUFFIX,cnet.com,SELECT + - DOMAIN-SUFFIX,cocoapods.org,SELECT + - DOMAIN-SUFFIX,comodoca.com,SELECT + - DOMAIN-SUFFIX,crashlytics.com,SELECT + - DOMAIN-SUFFIX,culturedcode.com,SELECT + - DOMAIN-SUFFIX,d.pr,SELECT + - DOMAIN-SUFFIX,danilo.to,SELECT + - DOMAIN-SUFFIX,dayone.me,SELECT + - DOMAIN-SUFFIX,db.tt,SELECT + - DOMAIN-SUFFIX,deskconnect.com,SELECT + - DOMAIN-SUFFIX,disq.us,SELECT + - DOMAIN-SUFFIX,disqus.com,SELECT + - DOMAIN-SUFFIX,disquscdn.com,SELECT + - DOMAIN-SUFFIX,dnsimple.com,SELECT + - DOMAIN-SUFFIX,docker.com,SELECT + - DOMAIN-SUFFIX,dribbble.com,SELECT + - DOMAIN-SUFFIX,droplr.com,SELECT + - DOMAIN-SUFFIX,duckduckgo.com,SELECT + - DOMAIN-SUFFIX,dueapp.com,SELECT + - DOMAIN-SUFFIX,dytt8.net,SELECT + - DOMAIN-SUFFIX,edgecastcdn.net,SELECT + - DOMAIN-SUFFIX,edgekey.net,SELECT + - DOMAIN-SUFFIX,edgesuite.net,SELECT + - DOMAIN-SUFFIX,engadget.com,SELECT + - DOMAIN-SUFFIX,entrust.net,SELECT + - DOMAIN-SUFFIX,eurekavpt.com,SELECT + - DOMAIN-SUFFIX,evernote.com,SELECT + - DOMAIN-SUFFIX,fabric.io,SELECT + - DOMAIN-SUFFIX,fast.com,SELECT + - DOMAIN-SUFFIX,fastly.net,SELECT + - DOMAIN-SUFFIX,fc2.com,SELECT + - DOMAIN-SUFFIX,feedburner.com,SELECT + - DOMAIN-SUFFIX,feedly.com,SELECT + - DOMAIN-SUFFIX,feedsportal.com,SELECT + - DOMAIN-SUFFIX,fiftythree.com,SELECT + - DOMAIN-SUFFIX,firebaseio.com,SELECT + - DOMAIN-SUFFIX,flexibits.com,SELECT + - DOMAIN-SUFFIX,flickr.com,SELECT + - DOMAIN-SUFFIX,flipboard.com,SELECT + - DOMAIN-SUFFIX,g.co,SELECT + - DOMAIN-SUFFIX,gabia.net,SELECT + - DOMAIN-SUFFIX,geni.us,SELECT + - DOMAIN-SUFFIX,gfx.ms,SELECT + - DOMAIN-SUFFIX,ggpht.com,SELECT + - DOMAIN-SUFFIX,ghostnoteapp.com,SELECT + - DOMAIN-SUFFIX,git.io,SELECT + - DOMAIN-KEYWORD,github,SELECT + - DOMAIN-SUFFIX,globalsign.com,SELECT + - DOMAIN-SUFFIX,gmodules.com,SELECT + - DOMAIN-SUFFIX,godaddy.com,SELECT + - DOMAIN-SUFFIX,golang.org,SELECT + - DOMAIN-SUFFIX,gongm.in,SELECT + - DOMAIN-SUFFIX,goo.gl,SELECT + - DOMAIN-SUFFIX,goodreaders.com,SELECT + - DOMAIN-SUFFIX,goodreads.com,SELECT + - DOMAIN-SUFFIX,gravatar.com,SELECT + - DOMAIN-SUFFIX,gstatic.com,SELECT + - DOMAIN-SUFFIX,gvt0.com,SELECT + - DOMAIN-SUFFIX,hockeyapp.net,SELECT + - DOMAIN-SUFFIX,hotmail.com,SELECT + - DOMAIN-SUFFIX,icons8.com,SELECT + - DOMAIN-SUFFIX,ifixit.com,SELECT + - DOMAIN-SUFFIX,ift.tt,SELECT + - DOMAIN-SUFFIX,ifttt.com,SELECT + - DOMAIN-SUFFIX,iherb.com,SELECT + - DOMAIN-SUFFIX,imageshack.us,SELECT + - DOMAIN-SUFFIX,img.ly,SELECT + - DOMAIN-SUFFIX,imgur.com,SELECT + - DOMAIN-SUFFIX,imore.com,SELECT + - DOMAIN-SUFFIX,instapaper.com,SELECT + - DOMAIN-SUFFIX,ipn.li,SELECT + - DOMAIN-SUFFIX,is.gd,SELECT + - DOMAIN-SUFFIX,issuu.com,SELECT + - DOMAIN-SUFFIX,itgonglun.com,SELECT + - DOMAIN-SUFFIX,itun.es,SELECT + - DOMAIN-SUFFIX,ixquick.com,SELECT + - DOMAIN-SUFFIX,j.mp,SELECT + - DOMAIN-SUFFIX,js.revsci.net,SELECT + - DOMAIN-SUFFIX,jshint.com,SELECT + - DOMAIN-SUFFIX,jtvnw.net,SELECT + - DOMAIN-SUFFIX,justgetflux.com,SELECT + - DOMAIN-SUFFIX,kat.cr,SELECT + - DOMAIN-SUFFIX,klip.me,SELECT + - DOMAIN-SUFFIX,libsyn.com,SELECT + - DOMAIN-SUFFIX,linkedin.com,SELECT + - DOMAIN-SUFFIX,line-apps.com,SELECT + - DOMAIN-SUFFIX,linode.com,SELECT + - DOMAIN-SUFFIX,lithium.com,SELECT + - DOMAIN-SUFFIX,littlehj.com,SELECT + - DOMAIN-SUFFIX,live.com,SELECT + - DOMAIN-SUFFIX,live.net,SELECT + - DOMAIN-SUFFIX,livefilestore.com,SELECT + - DOMAIN-SUFFIX,llnwd.net,SELECT + - DOMAIN-SUFFIX,macid.co,SELECT + - DOMAIN-SUFFIX,macromedia.com,SELECT + - DOMAIN-SUFFIX,macrumors.com,SELECT + - DOMAIN-SUFFIX,mashable.com,SELECT + - DOMAIN-SUFFIX,mathjax.org,SELECT + - DOMAIN-SUFFIX,medium.com,SELECT + - DOMAIN-SUFFIX,mega.co.nz,SELECT + - DOMAIN-SUFFIX,mega.nz,SELECT + - DOMAIN-SUFFIX,megaupload.com,SELECT + - DOMAIN-SUFFIX,microsofttranslator.com,SELECT + - DOMAIN-SUFFIX,mindnode.com,SELECT + - DOMAIN-SUFFIX,mobile01.com,SELECT + - DOMAIN-SUFFIX,modmyi.com,SELECT + - DOMAIN-SUFFIX,msedge.net,SELECT + - DOMAIN-SUFFIX,myfontastic.com,SELECT + - DOMAIN-SUFFIX,name.com,SELECT + - DOMAIN-SUFFIX,nextmedia.com,SELECT + - DOMAIN-SUFFIX,nsstatic.net,SELECT + - DOMAIN-SUFFIX,nssurge.com,SELECT + - DOMAIN-SUFFIX,nyt.com,SELECT + - DOMAIN-SUFFIX,nytimes.com,SELECT + - DOMAIN-SUFFIX,omnigroup.com,SELECT + - DOMAIN-SUFFIX,onedrive.com,SELECT + - DOMAIN-SUFFIX,onenote.com,SELECT + - DOMAIN-SUFFIX,ooyala.com,SELECT + - DOMAIN-SUFFIX,openvpn.net,SELECT + - DOMAIN-SUFFIX,openwrt.org,SELECT + - DOMAIN-SUFFIX,orkut.com,SELECT + - DOMAIN-SUFFIX,osxdaily.com,SELECT + - DOMAIN-SUFFIX,outlook.com,SELECT + - DOMAIN-SUFFIX,ow.ly,SELECT + - DOMAIN-SUFFIX,paddleapi.com,SELECT + - DOMAIN-SUFFIX,parallels.com,SELECT + - DOMAIN-SUFFIX,parse.com,SELECT + - DOMAIN-SUFFIX,pdfexpert.com,SELECT + - DOMAIN-SUFFIX,periscope.tv,SELECT + - DOMAIN-SUFFIX,pinboard.in,SELECT + - DOMAIN-SUFFIX,pinterest.com,SELECT + - DOMAIN-SUFFIX,pixelmator.com,SELECT + - DOMAIN-SUFFIX,pixiv.net,SELECT + - DOMAIN-SUFFIX,playpcesor.com,SELECT + - DOMAIN-SUFFIX,playstation.com,SELECT + - DOMAIN-SUFFIX,playstation.com.hk,SELECT + - DOMAIN-SUFFIX,playstation.net,SELECT + - DOMAIN-SUFFIX,playstationnetwork.com,SELECT + - DOMAIN-SUFFIX,pushwoosh.com,SELECT + - DOMAIN-SUFFIX,rime.im,SELECT + - DOMAIN-SUFFIX,servebom.com,SELECT + - DOMAIN-SUFFIX,sfx.ms,SELECT + - DOMAIN-SUFFIX,shadowsocks.org,SELECT + - DOMAIN-SUFFIX,sharethis.com,SELECT + - DOMAIN-SUFFIX,shazam.com,SELECT + - DOMAIN-SUFFIX,skype.com,SELECT + - DOMAIN-SUFFIX,smartdnsSELECT.com,SELECT + - DOMAIN-SUFFIX,smartmailcloud.com,SELECT + - DOMAIN-SUFFIX,sndcdn.com,SELECT + - DOMAIN-SUFFIX,sony.com,SELECT + - DOMAIN-SUFFIX,soundcloud.com,SELECT + - DOMAIN-SUFFIX,sourceforge.net,SELECT + - DOMAIN-SUFFIX,spotify.com,SELECT + - DOMAIN-SUFFIX,squarespace.com,SELECT + - DOMAIN-SUFFIX,sstatic.net,SELECT + - DOMAIN-SUFFIX,st.luluku.pw,SELECT + - DOMAIN-SUFFIX,stackoverflow.com,SELECT + - DOMAIN-SUFFIX,startpage.com,SELECT + - DOMAIN-SUFFIX,staticflickr.com,SELECT + - DOMAIN-SUFFIX,steamcommunity.com,SELECT + - DOMAIN-SUFFIX,symauth.com,SELECT + - DOMAIN-SUFFIX,symcb.com,SELECT + - DOMAIN-SUFFIX,symcd.com,SELECT + - DOMAIN-SUFFIX,tapbots.com,SELECT + - DOMAIN-SUFFIX,tapbots.net,SELECT + - DOMAIN-SUFFIX,tdesktop.com,SELECT + - DOMAIN-SUFFIX,techcrunch.com,SELECT + - DOMAIN-SUFFIX,techsmith.com,SELECT + - DOMAIN-SUFFIX,thepiratebay.org,SELECT + - DOMAIN-SUFFIX,theverge.com,SELECT + - DOMAIN-SUFFIX,time.com,SELECT + - DOMAIN-SUFFIX,timeinc.net,SELECT + - DOMAIN-SUFFIX,tiny.cc,SELECT + - DOMAIN-SUFFIX,tinypic.com,SELECT + - DOMAIN-SUFFIX,tmblr.co,SELECT + - DOMAIN-SUFFIX,todoist.com,SELECT + - DOMAIN-SUFFIX,trello.com,SELECT + - DOMAIN-SUFFIX,trustasiassl.com,SELECT + - DOMAIN-SUFFIX,tumblr.co,SELECT + - DOMAIN-SUFFIX,tumblr.com,SELECT + - DOMAIN-SUFFIX,tweetdeck.com,SELECT + - DOMAIN-SUFFIX,tweetmarker.net,SELECT + - DOMAIN-SUFFIX,twitch.tv,SELECT + - DOMAIN-SUFFIX,txmblr.com,SELECT + - DOMAIN-SUFFIX,typekit.net,SELECT + - DOMAIN-SUFFIX,ubertags.com,SELECT + - DOMAIN-SUFFIX,ublock.org,SELECT + - DOMAIN-SUFFIX,ubnt.com,SELECT + - DOMAIN-SUFFIX,ulyssesapp.com,SELECT + - DOMAIN-SUFFIX,urchin.com,SELECT + - DOMAIN-SUFFIX,usertrust.com,SELECT + - DOMAIN-SUFFIX,v.gd,SELECT + - DOMAIN-SUFFIX,v2ex.com,SELECT + - DOMAIN-SUFFIX,vimeo.com,SELECT + - DOMAIN-SUFFIX,vimeocdn.com,SELECT + - DOMAIN-SUFFIX,vine.co,SELECT + - DOMAIN-SUFFIX,vivaldi.com,SELECT + - DOMAIN-SUFFIX,vox-cdn.com,SELECT + - DOMAIN-SUFFIX,vsco.co,SELECT + - DOMAIN-SUFFIX,vultr.com,SELECT + - DOMAIN-SUFFIX,w.org,SELECT + - DOMAIN-SUFFIX,w3schools.com,SELECT + - DOMAIN-SUFFIX,webtype.com,SELECT + - DOMAIN-SUFFIX,wikiwand.com,SELECT + - DOMAIN-SUFFIX,wikileaks.org,SELECT + - DOMAIN-SUFFIX,wikimedia.org,SELECT + - DOMAIN-SUFFIX,wikipedia.com,SELECT + - DOMAIN-SUFFIX,wikipedia.org,SELECT + - DOMAIN-SUFFIX,windows.com,SELECT + - DOMAIN-SUFFIX,windows.net,SELECT + - DOMAIN-SUFFIX,wire.com,SELECT + - DOMAIN-SUFFIX,wordpress.com,SELECT + - DOMAIN-SUFFIX,workflowy.com,SELECT + - DOMAIN-SUFFIX,wp.com,SELECT + - DOMAIN-SUFFIX,wsj.com,SELECT + - DOMAIN-SUFFIX,wsj.net,SELECT + - DOMAIN-SUFFIX,xda-developers.com,SELECT + - DOMAIN-SUFFIX,xeeno.com,SELECT + - DOMAIN-SUFFIX,xiti.com,SELECT + - DOMAIN-SUFFIX,yahoo.com,SELECT + - DOMAIN-SUFFIX,yimg.com,SELECT + - DOMAIN-SUFFIX,ying.com,SELECT + - DOMAIN-SUFFIX,yoyo.org,SELECT + - DOMAIN-SUFFIX,ytimg.com,SELECT + + # Telegram + - DOMAIN-SUFFIX,telegra.ph,SELECT + - DOMAIN-SUFFIX,telegram.org,SELECT + - IP-CIDR,91.108.4.0/22,SELECT,no-resolve + - IP-CIDR,91.108.8.0/21,SELECT,no-resolve + - IP-CIDR,91.108.16.0/22,SELECT,no-resolve + - IP-CIDR,91.108.56.0/22,SELECT,no-resolve + - IP-CIDR,149.154.160.0/20,SELECT,no-resolve + - IP-CIDR6,2001:67c:4e8::/48,SELECT,no-resolve + - IP-CIDR6,2001:b28:f23d::/48,SELECT,no-resolve + - IP-CIDR6,2001:b28:f23f::/48,SELECT,no-resolve + + # Google 中国服务 services.googleapis.cn + - IP-CIDR,120.232.181.162/32,SELECT,no-resolve + - IP-CIDR,120.241.147.226/32,SELECT,no-resolve + - IP-CIDR,120.253.253.226/32,SELECT,no-resolve + - IP-CIDR,120.253.255.162/32,SELECT,no-resolve + - IP-CIDR,120.253.255.34/32,SELECT,no-resolve + - IP-CIDR,120.253.255.98/32,SELECT,no-resolve + - IP-CIDR,180.163.150.162/32,SELECT,no-resolve + - IP-CIDR,180.163.150.34/32,SELECT,no-resolve + - IP-CIDR,180.163.151.162/32,SELECT,no-resolve + - IP-CIDR,180.163.151.34/32,SELECT,no-resolve + - IP-CIDR,203.208.39.0/24,SELECT,no-resolve + - IP-CIDR,203.208.40.0/24,SELECT,no-resolve + - IP-CIDR,203.208.41.0/24,SELECT,no-resolve + - IP-CIDR,203.208.43.0/24,SELECT,no-resolve + - IP-CIDR,203.208.50.0/24,SELECT,no-resolve + - IP-CIDR,220.181.174.162/32,SELECT,no-resolve + - IP-CIDR,220.181.174.226/32,SELECT,no-resolve + - IP-CIDR,220.181.174.34/32,SELECT,no-resolve + + # LAN + - DOMAIN,injections.adguard.org,DIRECT + - DOMAIN,local.adguard.org,DIRECT + - DOMAIN-SUFFIX,local,DIRECT + - IP-CIDR,127.0.0.0/8,DIRECT + - IP-CIDR,172.16.0.0/12,DIRECT + - IP-CIDR,192.168.0.0/16,DIRECT + - IP-CIDR,10.0.0.0/8,DIRECT + - IP-CIDR,17.0.0.0/8,DIRECT + - IP-CIDR,100.64.0.0/10,DIRECT + - IP-CIDR,224.0.0.0/4,DIRECT + - IP-CIDR6,fe80::/10,DIRECT + + # 剩余未匹配的国内网站 + - DOMAIN-SUFFIX,cn,DIRECT + - DOMAIN-KEYWORD,-cn,DIRECT + + # 最终规则 + - GEOIP,CN,DIRECT + - MATCH,SELECT diff --git a/Xboard/resources/rules/default.clash.yaml b/Xboard/resources/rules/default.clash.yaml new file mode 100644 index 0000000..bda48c5 --- /dev/null +++ b/Xboard/resources/rules/default.clash.yaml @@ -0,0 +1,286 @@ +mixed-port: 7890 +allow-lan: true +bind-address: "*" +mode: rule +log-level: info +external-controller: 127.0.0.1:9090 +unified-delay: true +tcp-concurrent: true + +dns: + enable: true + ipv6: false + default-nameserver: + - 223.5.5.5 + - 119.29.29.29 + enhanced-mode: fake-ip + fake-ip-range: 198.18.0.1/16 + use-hosts: true + nameserver-policy: + "+.google.com": "https://dns.cloudflare.com/dns-query" + "+.googleapis.com": "https://dns.cloudflare.com/dns-query" + "+.googleapis.cn": "https://dns.cloudflare.com/dns-query" + "+.googlevideo.com": "https://dns.cloudflare.com/dns-query" + "+.gstatic.com": "https://dns.cloudflare.com/dns-query" + "+.youtube.com": "https://dns.cloudflare.com/dns-query" + "+.youtu.be": "https://dns.cloudflare.com/dns-query" + "+.facebook.com": "https://dns.cloudflare.com/dns-query" + "+.twitter.com": "https://dns.cloudflare.com/dns-query" + "+.x.com": "https://dns.cloudflare.com/dns-query" + "+.github.com": "https://dns.cloudflare.com/dns-query" + "+.githubusercontent.com": "https://dns.cloudflare.com/dns-query" + "+.openai.com": "https://dns.cloudflare.com/dns-query" + "+.chatgpt.com": "https://dns.cloudflare.com/dns-query" + "+.anthropic.com": "https://dns.cloudflare.com/dns-query" + nameserver: + - https://doh.pub/dns-query + - https://dns.alidns.com/dns-query + - tls://dot.pub:853 + - tls://dns.alidns.com:853 + fallback: + - https://dns.cloudflare.com/dns-query + - https://dns.google/dns-query + - tls://1.1.1.1:853 + - tls://8.8.8.8:853 + fallback-filter: + geoip: true + geoip-code: CN + ipcidr: + - 0.0.0.0/8 + - 10.0.0.0/8 + - 100.64.0.0/10 + - 127.0.0.0/8 + - 169.254.0.0/16 + - 172.16.0.0/12 + - 192.168.0.0/16 + - 224.0.0.0/4 + - 240.0.0.0/4 + domain: + - "+.google.com" + - "+.facebook.com" + - "+.youtube.com" + - "+.githubusercontent.com" + - "+.googlevideo.com" + - "+.googleapis.cn" + fake-ip-filter: + - "*.lan" + - "*.local" + - "*.localhost" + - "*.test" + - localhost.ptlogin2.qq.com + - "+.stun.*.*" + - "+.stun.*.*.*" + - "+.stun.*.*.*.*" + - "lens.l.google.com" + - "*.srv.nintendo.net" + - "+.stun.playstation.net" + - "xbox.*.*.microsoft.com" + - "*.*.xboxlive.com" + - "+.msftncsi.com" + - "+.msftconnecttest.com" + +proxies: + +proxy-groups: + - { name: "$app_name", type: select, proxies: ["自动选择", "故障转移", "DIRECT"] } + - { name: "自动选择", type: url-test, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 300, tolerance: 50 } + - { name: "故障转移", type: fallback, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 300 } + +rules: + # Custom + # Ad blocking + - DOMAIN-KEYWORD,admarvel,REJECT + - DOMAIN-KEYWORD,admaster,REJECT + - DOMAIN-KEYWORD,adsage,REJECT + - DOMAIN-KEYWORD,adsmogo,REJECT + - DOMAIN-KEYWORD,adsrvmedia,REJECT + - DOMAIN-KEYWORD,adwords,REJECT + - DOMAIN-KEYWORD,adservice,REJECT + - DOMAIN-KEYWORD,domob,REJECT + - DOMAIN-KEYWORD,duomeng,REJECT + - DOMAIN-KEYWORD,dwtrack,REJECT + - DOMAIN-KEYWORD,guanggao,REJECT + - DOMAIN-KEYWORD,lianmeng,REJECT + - DOMAIN-KEYWORD,omgmta,REJECT + - DOMAIN-KEYWORD,openx,REJECT + - DOMAIN-KEYWORD,partnerad,REJECT + - DOMAIN-KEYWORD,supersonicads,REJECT + - DOMAIN-KEYWORD,umeng,REJECT + - DOMAIN-KEYWORD,zjtoolbar,REJECT + - DOMAIN-SUFFIX,appsflyer.com,REJECT + - DOMAIN-SUFFIX,doubleclick.net,REJECT + - DOMAIN-SUFFIX,mmstat.com,REJECT + # LAN + - DOMAIN-SUFFIX,local,DIRECT + - DOMAIN-SUFFIX,localhost,DIRECT + - IP-CIDR,10.0.0.0/8,DIRECT,no-resolve + - IP-CIDR,17.0.0.0/8,DIRECT,no-resolve + - IP-CIDR,100.64.0.0/10,DIRECT,no-resolve + - IP-CIDR,127.0.0.0/8,DIRECT,no-resolve + - IP-CIDR,172.16.0.0/12,DIRECT,no-resolve + - IP-CIDR,192.168.0.0/16,DIRECT,no-resolve + - IP-CIDR,198.18.0.0/16,DIRECT,no-resolve + - IP-CIDR,224.0.0.0/4,DIRECT,no-resolve + - IP-CIDR6,::1/128,DIRECT,no-resolve + - IP-CIDR6,fc00::/7,DIRECT,no-resolve + - IP-CIDR6,fe80::/10,DIRECT,no-resolve + # Apple (App Store via proxy for foreign regions) + - DOMAIN-SUFFIX,apps.apple.com,$app_name + - DOMAIN-SUFFIX,itunes.apple.com,$app_name + - DOMAIN-SUFFIX,blobstore.apple.com,$app_name + - DOMAIN,safebrowsing.urlsec.qq.com,DIRECT + - DOMAIN-SUFFIX,apple.com,DIRECT + - DOMAIN-SUFFIX,apple-cloudkit.com,DIRECT + - DOMAIN-SUFFIX,icloud.com,DIRECT + - DOMAIN-SUFFIX,icloud-content.com,DIRECT + - DOMAIN-SUFFIX,mzstatic.com,DIRECT + - DOMAIN-SUFFIX,aaplimg.com,DIRECT + - DOMAIN-SUFFIX,cdn-apple.com,DIRECT + - DOMAIN-SUFFIX,akadns.net,DIRECT + # China direct (KEYWORD) + - DOMAIN-KEYWORD,baidu,DIRECT + - DOMAIN-KEYWORD,alibaba,DIRECT + - DOMAIN-KEYWORD,alicdn,DIRECT + - DOMAIN-KEYWORD,alipay,DIRECT + - DOMAIN-KEYWORD,taobao,DIRECT + - DOMAIN-KEYWORD,tencent,DIRECT + - DOMAIN-KEYWORD,bilibili,DIRECT + - DOMAIN-KEYWORD,weibo,DIRECT + - DOMAIN-KEYWORD,douyin,DIRECT + - DOMAIN-KEYWORD,bytedance,DIRECT + - DOMAIN-KEYWORD,xiaomi,DIRECT + - DOMAIN-KEYWORD,huawei,DIRECT + - DOMAIN-KEYWORD,netease,DIRECT + - DOMAIN-KEYWORD,meituan,DIRECT + - DOMAIN-KEYWORD,pinduoduo,DIRECT + - DOMAIN-KEYWORD,kuaishou,DIRECT + - DOMAIN-KEYWORD,jingdong,DIRECT + - DOMAIN-KEYWORD,officecdn,DIRECT + # China direct (SUFFIX) + - DOMAIN-SUFFIX,qq.com,DIRECT + - DOMAIN-SUFFIX,weixin.com,DIRECT + - DOMAIN-SUFFIX,wechat.com,DIRECT + - DOMAIN-SUFFIX,gtimg.com,DIRECT + - DOMAIN-SUFFIX,qcloud.com,DIRECT + - DOMAIN-SUFFIX,myqcloud.com,DIRECT + - DOMAIN-SUFFIX,qpic.cn,DIRECT + - DOMAIN-SUFFIX,tenpay.com,DIRECT + - DOMAIN-SUFFIX,tmall.com,DIRECT + - DOMAIN-SUFFIX,jd.com,DIRECT + - DOMAIN-SUFFIX,360buyimg.com,DIRECT + - DOMAIN-SUFFIX,iqiyi.com,DIRECT + - DOMAIN-SUFFIX,youku.com,DIRECT + - DOMAIN-SUFFIX,ykimg.com,DIRECT + - DOMAIN-SUFFIX,tudou.com,DIRECT + - DOMAIN-SUFFIX,acfun.tv,DIRECT + - DOMAIN-SUFFIX,hdslb.com,DIRECT + - DOMAIN-SUFFIX,sohu.com,DIRECT + - DOMAIN-SUFFIX,sogou.com,DIRECT + - DOMAIN-SUFFIX,zhihu.com,DIRECT + - DOMAIN-SUFFIX,zhimg.com,DIRECT + - DOMAIN-SUFFIX,douban.com,DIRECT + - DOMAIN-SUFFIX,doubanio.com,DIRECT + - DOMAIN-SUFFIX,163.com,DIRECT + - DOMAIN-SUFFIX,126.com,DIRECT + - DOMAIN-SUFFIX,126.net,DIRECT + - DOMAIN-SUFFIX,127.net,DIRECT + - DOMAIN-SUFFIX,yeah.net,DIRECT + - DOMAIN-SUFFIX,sina.com,DIRECT + - DOMAIN-SUFFIX,sinaimg.cn,DIRECT + - DOMAIN-SUFFIX,ximalaya.com,DIRECT + - DOMAIN-SUFFIX,xmcdn.com,DIRECT + - DOMAIN-SUFFIX,csdn.net,DIRECT + - DOMAIN-SUFFIX,gitee.com,DIRECT + - DOMAIN-SUFFIX,jianshu.com,DIRECT + - DOMAIN-SUFFIX,cnblogs.com,DIRECT + - DOMAIN-SUFFIX,oschina.net,DIRECT + - DOMAIN-SUFFIX,ele.me,DIRECT + - DOMAIN-SUFFIX,ctrip.com,DIRECT + - DOMAIN-SUFFIX,suning.com,DIRECT + - DOMAIN-SUFFIX,dianping.com,DIRECT + - DOMAIN-SUFFIX,amap.com,DIRECT + - DOMAIN-SUFFIX,autonavi.com,DIRECT + - DOMAIN-SUFFIX,mi.com,DIRECT + - DOMAIN-SUFFIX,miui.com,DIRECT + - DOMAIN-SUFFIX,ifeng.com,DIRECT + - DOMAIN-SUFFIX,youdao.com,DIRECT + - DOMAIN-SUFFIX,iciba.com,DIRECT + - DOMAIN-SUFFIX,xunlei.com,DIRECT + - DOMAIN-SUFFIX,smzdm.com,DIRECT + - DOMAIN-SUFFIX,sspai.com,DIRECT + - DOMAIN-SUFFIX,36kr.com,DIRECT + - DOMAIN-SUFFIX,speedtest.net,DIRECT + - DOMAIN-SUFFIX,microsoft.com,DIRECT + - DOMAIN-SUFFIX,microsoftonline.com,DIRECT + - DOMAIN-SUFFIX,office.com,DIRECT + - DOMAIN-SUFFIX,office365.com,DIRECT + - DOMAIN-SUFFIX,windows.com,DIRECT + - DOMAIN-SUFFIX,windowsupdate.com,DIRECT + - DOMAIN-SUFFIX,live.com,DIRECT + - DOMAIN-SUFFIX,msn.com,DIRECT + - DOMAIN-SUFFIX,cn,DIRECT + - DOMAIN-KEYWORD,-cn,DIRECT + # Blocked services (KEYWORD) + - DOMAIN-KEYWORD,google,$app_name + - DOMAIN-KEYWORD,gmail,$app_name + - DOMAIN-KEYWORD,youtube,$app_name + - DOMAIN-KEYWORD,facebook,$app_name + - DOMAIN-KEYWORD,twitter,$app_name + - DOMAIN-KEYWORD,instagram,$app_name + - DOMAIN-KEYWORD,whatsapp,$app_name + - DOMAIN-KEYWORD,telegram,$app_name + - DOMAIN-KEYWORD,github,$app_name + - DOMAIN-KEYWORD,blogspot,$app_name + - DOMAIN-KEYWORD,dropbox,$app_name + - DOMAIN-KEYWORD,wikipedia,$app_name + - DOMAIN-KEYWORD,pinterest,$app_name + - DOMAIN-KEYWORD,discord,$app_name + - DOMAIN-KEYWORD,openai,$app_name + - DOMAIN-KEYWORD,anthropic,$app_name + - DOMAIN-KEYWORD,netflix,$app_name + - DOMAIN-KEYWORD,spotify,$app_name + - DOMAIN-KEYWORD,amazon,$app_name + # Blocked services (SUFFIX) + - DOMAIN-SUFFIX,t.co,$app_name + - DOMAIN-SUFFIX,x.com,$app_name + - DOMAIN-SUFFIX,twimg.com,$app_name + - DOMAIN-SUFFIX,fb.me,$app_name + - DOMAIN-SUFFIX,fbcdn.net,$app_name + - DOMAIN-SUFFIX,youtu.be,$app_name + - DOMAIN-SUFFIX,ytimg.com,$app_name + - DOMAIN-SUFFIX,gstatic.com,$app_name + - DOMAIN-SUFFIX,ggpht.com,$app_name + - DOMAIN-SUFFIX,googlevideo.com,$app_name + - DOMAIN-SUFFIX,v2ex.com,$app_name + - DOMAIN-SUFFIX,medium.com,$app_name + - DOMAIN-SUFFIX,reddit.com,$app_name + - DOMAIN-SUFFIX,redd.it,$app_name + - DOMAIN-SUFFIX,imgur.com,$app_name + - DOMAIN-SUFFIX,pixiv.net,$app_name + - DOMAIN-SUFFIX,nytimes.com,$app_name + - DOMAIN-SUFFIX,nyt.com,$app_name + - DOMAIN-SUFFIX,bbc.com,$app_name + - DOMAIN-SUFFIX,bbc.co.uk,$app_name + - DOMAIN-SUFFIX,steamcommunity.com,$app_name + - DOMAIN-SUFFIX,twitch.tv,$app_name + - DOMAIN-SUFFIX,vimeo.com,$app_name + - DOMAIN-SUFFIX,tumblr.com,$app_name + - DOMAIN-SUFFIX,linkedin.com,$app_name + - DOMAIN-SUFFIX,licdn.com,$app_name + - DOMAIN-SUFFIX,mega.nz,$app_name + - DOMAIN-SUFFIX,archive.org,$app_name + - DOMAIN-SUFFIX,wikimedia.org,$app_name + - DOMAIN-SUFFIX,soundcloud.com,$app_name + # Telegram IP + - IP-CIDR,91.108.4.0/22,$app_name,no-resolve + - IP-CIDR,91.108.8.0/21,$app_name,no-resolve + - IP-CIDR,91.108.12.0/22,$app_name,no-resolve + - IP-CIDR,91.108.16.0/22,$app_name,no-resolve + - IP-CIDR,91.108.56.0/22,$app_name,no-resolve + - IP-CIDR,149.154.160.0/20,$app_name,no-resolve + - IP-CIDR6,2001:67c:4e8::/48,$app_name,no-resolve + - IP-CIDR6,2001:b28:f23d::/48,$app_name,no-resolve + - IP-CIDR6,2001:b28:f23f::/48,$app_name,no-resolve + # Fallback + - GEOIP,CN,DIRECT + - MATCH,$app_name diff --git a/Xboard/resources/rules/default.sing-box.json b/Xboard/resources/rules/default.sing-box.json new file mode 100644 index 0000000..8d4fd5a --- /dev/null +++ b/Xboard/resources/rules/default.sing-box.json @@ -0,0 +1,138 @@ +{ + "dns": { + "rules": [ + { + "outbound": ["any"], + "server": "local" + }, + { + "clash_mode": "global", + "server": "remote" + }, + { + "clash_mode": "direct", + "server": "local" + }, + { + "rule_set": ["geosite-cn"], + "server": "local" + } + ], + "servers": [ + { + "address": "https://1.1.1.1/dns-query", + "detour": "节点选择", + "tag": "remote" + }, + { + "address": "https://223.5.5.5/dns-query", + "detour": "direct", + "tag": "local" + }, + { + "address": "rcode://success", + "tag": "block" + } + ], + "strategy": "prefer_ipv4" + }, + "experimental": { + "cache_file": { + "enabled": true, + "path": "cache.db", + "cache_id": "cache_db", + "store_fakeip": true + } + }, + "inbounds": [ + { + "auto_route": true, + "domain_strategy": "prefer_ipv4", + "endpoint_independent_nat": true, + "address": ["172.19.0.1/30", "2001:0470:f9da:fdfa::1/64"], + "mtu": 9000, + "sniff": true, + "sniff_override_destination": true, + "stack": "system", + "strict_route": true, + "type": "tun" + }, + { + "domain_strategy": "prefer_ipv4", + "listen": "127.0.0.1", + "listen_port": 2333, + "sniff": true, + "sniff_override_destination": true, + "tag": "socks-in", + "type": "socks", + "users": [] + }, + { + "domain_strategy": "prefer_ipv4", + "listen": "127.0.0.1", + "listen_port": 2334, + "sniff": true, + "sniff_override_destination": true, + "tag": "mixed-in", + "type": "mixed", + "users": [] + } + ], + "outbounds": [ + { + "tag": "节点选择", + "type": "selector", + "default": "自动选择", + "outbounds": ["自动选择"] + }, + { "tag": "direct", "type": "direct" }, + { "tag": "block", "type": "block" }, + { "tag": "dns-out", "type": "dns" }, + { + "tag": "自动选择", + "type": "urltest", + "outbounds": [] + } + ], + "route": { + "auto_detect_interface": true, + "rules": [ + { + "outbound": "dns-out", + "protocol": "dns" + }, + { + "clash_mode": "direct", + "outbound": "direct" + }, + { + "clash_mode": "global", + "outbound": "节点选择" + }, + { + "ip_is_private": true, + "outbound": "direct" + }, + { + "rule_set": ["geosite-cn", "geoip-cn"], + "outbound": "direct" + } + ], + "rule_set": [ + { + "tag": "geosite-cn", + "type": "remote", + "format": "binary", + "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs", + "download_detour": "自动选择" + }, + { + "tag": "geoip-cn", + "type": "remote", + "format": "binary", + "url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs", + "download_detour": "自动选择" + } + ] + } +} diff --git a/Xboard/resources/rules/default.surfboard.conf b/Xboard/resources/rules/default.surfboard.conf new file mode 100644 index 0000000..e0d1bc1 --- /dev/null +++ b/Xboard/resources/rules/default.surfboard.conf @@ -0,0 +1,576 @@ +#!MANAGED-CONFIG $subs_link interval=43200 strict=true +# Thanks @Hackl0us SS-Rule-Snippet + +[General] +loglevel = notify +ipv6 = false +skip-proxy = localhost, *.local, injections.adguard.org, local.adguard.org, 0.0.0.0/8, 10.0.0.0/8, 17.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 192.88.99.0/24, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4, 255.255.255.255/32 +tls-provider = default +show-error-page-for-reject = true +dns-server = 223.6.6.6, 119.29.29.29, 119.28.28.28 +test-timeout = 5 +internet-test-url = http://bing.com +proxy-test-url = http://bing.com + +[Panel] +SubscribeInfo = $subscribe_info, style=info + +# Surfboard 的服务器和策略组配置方式与 Surge 类似, 可以参考 Surge 的规则配置手册: https://manual.nssurge.com/ + +[Proxy] +$proxies + +[Proxy Group] +Proxy = select, auto, fallback, $proxy_group +auto = url-test, $proxy_group, url=http://www.gstatic.com/generate_204, interval=43200 +fallback = fallback, $proxy_group, url=http://www.gstatic.com/generate_204, interval=43200 + +[Rule] +# 自定义规则 +## 您可以在此处插入自定义规则 +DOMAIN-SUFFIX,tophub.today,DIRECT +DOMAIN-SUFFIX,netmarble.com,Proxy +DOMAIN-SUFFIX,worldflipper.jp,Proxy +DOMAIN-SUFFIX,naver.com,Proxy +DOMAIN-SUFFIX,smartmediarep.com,Proxy +DOMAIN-SUFFIX,technews.tw,Proxy + +# 强制订阅域名直连 +DOMAIN,$subs_domain,DIRECT + +# Google 中国服务 +DOMAIN-SUFFIX,services.googleapis.cn,Proxy +DOMAIN-SUFFIX,xn--ngstr-lra8j.com,Proxy + +# Apple +DOMAIN,developer.apple.com,Proxy +DOMAIN-SUFFIX,digicert.com,Proxy +USER-AGENT,com.apple.trustd*,Proxy +DOMAIN-SUFFIX,apple-dns.net,Proxy +DOMAIN,testflight.apple.com,Proxy +DOMAIN,sandbox.itunes.apple.com,Proxy +DOMAIN,itunes.apple.com,Proxy +DOMAIN-SUFFIX,apps.apple.com,Proxy +DOMAIN-SUFFIX,blobstore.apple.com,Proxy +DOMAIN,cvws.icloud-content.com,Proxy +DOMAIN,safebrowsing.urlsec.qq.com,DIRECT +DOMAIN,safebrowsing.googleapis.com,DIRECT +USER-AGENT,com.apple.appstored*,DIRECT +USER-AGENT,AppStore*,DIRECT +DOMAIN-SUFFIX,mzstatic.com,DIRECT +DOMAIN-SUFFIX,itunes.apple.com,DIRECT +DOMAIN-SUFFIX,icloud.com,DIRECT +DOMAIN-SUFFIX,icloud-content.com,DIRECT +USER-AGENT,cloudd*,DIRECT +USER-AGENT,*com.apple.WebKit*,DIRECT +USER-AGENT,*com.apple.*,DIRECT +DOMAIN-SUFFIX,me.com,DIRECT +DOMAIN-SUFFIX,aaplimg.com,DIRECT +DOMAIN-SUFFIX,cdn20.com,DIRECT +DOMAIN-SUFFIX,cdn-apple.com,DIRECT +DOMAIN-SUFFIX,akadns.net,DIRECT +DOMAIN-SUFFIX,akamaiedge.net,DIRECT +DOMAIN-SUFFIX,edgekey.net,DIRECT +DOMAIN-SUFFIX,mwcloudcdn.com,DIRECT +DOMAIN-SUFFIX,mwcname.com,DIRECT +DOMAIN-SUFFIX,apple.com,DIRECT +DOMAIN-SUFFIX,apple-cloudkit.com,DIRECT +DOMAIN-SUFFIX,apple-mapkit.com,DIRECT + +# 国内网站 +USER-AGENT,MicroMessenger Client*,DIRECT +USER-AGENT,WeChat*,DIRECT + +DOMAIN-SUFFIX,126.com,DIRECT +DOMAIN-SUFFIX,126.net,DIRECT +DOMAIN-SUFFIX,127.net,DIRECT +DOMAIN-SUFFIX,163.com,DIRECT +DOMAIN-SUFFIX,360buyimg.com,DIRECT +DOMAIN-SUFFIX,36kr.com,DIRECT +DOMAIN-SUFFIX,acfun.tv,DIRECT +DOMAIN-SUFFIX,air-matters.com,DIRECT +DOMAIN-SUFFIX,aixifan.com,DIRECT +DOMAIN-KEYWORD,alicdn,DIRECT +DOMAIN-KEYWORD,alipay,DIRECT +DOMAIN-KEYWORD,aliyun,DIRECT +DOMAIN-KEYWORD,taobao,DIRECT +DOMAIN-SUFFIX,amap.com,DIRECT +DOMAIN-SUFFIX,autonavi.com,DIRECT +DOMAIN-KEYWORD,baidu,DIRECT +DOMAIN-SUFFIX,bdimg.com,DIRECT +DOMAIN-SUFFIX,bdstatic.com,DIRECT +DOMAIN-SUFFIX,bilibili.com,DIRECT +DOMAIN-SUFFIX,bilivideo.com,DIRECT +DOMAIN-SUFFIX,caiyunapp.com,DIRECT +DOMAIN-SUFFIX,clouddn.com,DIRECT +DOMAIN-SUFFIX,cnbeta.com,DIRECT +DOMAIN-SUFFIX,cnbetacdn.com,DIRECT +DOMAIN-SUFFIX,cootekservice.com,DIRECT +DOMAIN-SUFFIX,csdn.net,DIRECT +DOMAIN-SUFFIX,ctrip.com,DIRECT +DOMAIN-SUFFIX,dgtle.com,DIRECT +DOMAIN-SUFFIX,dianping.com,DIRECT +DOMAIN-SUFFIX,douban.com,DIRECT +DOMAIN-SUFFIX,doubanio.com,DIRECT +DOMAIN-SUFFIX,duokan.com,DIRECT +DOMAIN-SUFFIX,easou.com,DIRECT +DOMAIN-SUFFIX,ele.me,DIRECT +DOMAIN-SUFFIX,feng.com,DIRECT +DOMAIN-SUFFIX,fir.im,DIRECT +DOMAIN-SUFFIX,frdic.com,DIRECT +DOMAIN-SUFFIX,g-cores.com,DIRECT +DOMAIN-SUFFIX,godic.net,DIRECT +DOMAIN-SUFFIX,gtimg.com,DIRECT +DOMAIN-SUFFIX,hongxiu.com,DIRECT +DOMAIN-SUFFIX,hxcdn.net,DIRECT +DOMAIN-SUFFIX,iciba.com,DIRECT +DOMAIN-SUFFIX,ifeng.com,DIRECT +DOMAIN-SUFFIX,ifengimg.com,DIRECT +DOMAIN-SUFFIX,ipip.net,DIRECT +DOMAIN-SUFFIX,iqiyi.com,DIRECT +DOMAIN-SUFFIX,jd.com,DIRECT +DOMAIN-SUFFIX,jianshu.com,DIRECT +DOMAIN-SUFFIX,knewone.com,DIRECT +DOMAIN-SUFFIX,le.com,DIRECT +DOMAIN-SUFFIX,lecloud.com,DIRECT +DOMAIN-SUFFIX,lemicp.com,DIRECT +DOMAIN-SUFFIX,licdn.com,DIRECT +DOMAIN-SUFFIX,luoo.net,DIRECT +DOMAIN-SUFFIX,meituan.com,DIRECT +DOMAIN-SUFFIX,meituan.net,DIRECT +DOMAIN-SUFFIX,mi.com,DIRECT +DOMAIN-SUFFIX,miaopai.com,DIRECT +DOMAIN-SUFFIX,microsoft.com,DIRECT +DOMAIN-SUFFIX,microsoftonline.com,DIRECT +DOMAIN-SUFFIX,miui.com,DIRECT +DOMAIN-SUFFIX,miwifi.com,DIRECT +DOMAIN-SUFFIX,mob.com,DIRECT +DOMAIN-SUFFIX,netease.com,DIRECT +DOMAIN-SUFFIX,office.com,DIRECT +DOMAIN-KEYWORD,officecdn,DIRECT +DOMAIN-SUFFIX,office365.com,DIRECT +DOMAIN-SUFFIX,oschina.net,DIRECT +DOMAIN-SUFFIX,ppsimg.com,DIRECT +DOMAIN-SUFFIX,pstatp.com,DIRECT +DOMAIN-SUFFIX,qcloud.com,DIRECT +DOMAIN-SUFFIX,qdaily.com,DIRECT +DOMAIN-SUFFIX,qdmm.com,DIRECT +DOMAIN-SUFFIX,qhimg.com,DIRECT +DOMAIN-SUFFIX,qhres.com,DIRECT +DOMAIN-SUFFIX,qidian.com,DIRECT +DOMAIN-SUFFIX,qihucdn.com,DIRECT +DOMAIN-SUFFIX,qiniu.com,DIRECT +DOMAIN-SUFFIX,qiniucdn.com,DIRECT +DOMAIN-SUFFIX,qiyipic.com,DIRECT +DOMAIN-SUFFIX,qq.com,DIRECT +DOMAIN-SUFFIX,qqurl.com,DIRECT +DOMAIN-SUFFIX,rarbg.to,DIRECT +DOMAIN-SUFFIX,ruguoapp.com,DIRECT +DOMAIN-SUFFIX,segmentfault.com,DIRECT +DOMAIN-SUFFIX,sinaapp.com,DIRECT +DOMAIN-SUFFIX,smzdm.com,DIRECT +DOMAIN-SUFFIX,snapdrop.net,DIRECT +DOMAIN-SUFFIX,sogou.com,DIRECT +DOMAIN-SUFFIX,sogoucdn.com,DIRECT +DOMAIN-SUFFIX,sohu.com,DIRECT +DOMAIN-SUFFIX,soku.com,DIRECT +DOMAIN-SUFFIX,speedtest.net,DIRECT +DOMAIN-SUFFIX,sspai.com,DIRECT +DOMAIN-SUFFIX,suning.com,DIRECT +DOMAIN-SUFFIX,taobao.com,DIRECT +DOMAIN-SUFFIX,tencent.com,DIRECT +DOMAIN-SUFFIX,tenpay.com,DIRECT +DOMAIN-SUFFIX,tianyancha.com,DIRECT +DOMAIN-KEYWORD,.tmall.com,DIRECT +DOMAIN-SUFFIX,tudou.com,DIRECT +DOMAIN-SUFFIX,umetrip.com,DIRECT +DOMAIN-SUFFIX,upaiyun.com,DIRECT +DOMAIN-SUFFIX,upyun.com,DIRECT +DOMAIN-SUFFIX,veryzhun.com,DIRECT +DOMAIN-SUFFIX,weather.com,DIRECT +DOMAIN-SUFFIX,weibo.com,DIRECT +DOMAIN-SUFFIX,xiami.com,DIRECT +DOMAIN-SUFFIX,xiami.net,DIRECT +DOMAIN-SUFFIX,xiaomicp.com,DIRECT +DOMAIN-SUFFIX,ximalaya.com,DIRECT +DOMAIN-SUFFIX,xmcdn.com,DIRECT +DOMAIN-SUFFIX,xunlei.com,DIRECT +DOMAIN-SUFFIX,yhd.com,DIRECT +DOMAIN-SUFFIX,yihaodianimg.com,DIRECT +DOMAIN-SUFFIX,yinxiang.com,DIRECT +DOMAIN-SUFFIX,ykimg.com,DIRECT +DOMAIN-SUFFIX,youdao.com,DIRECT +DOMAIN-SUFFIX,youku.com,DIRECT +DOMAIN-SUFFIX,zealer.com,DIRECT +DOMAIN-SUFFIX,zhihu.com,DIRECT +DOMAIN-SUFFIX,zhimg.com,DIRECT +DOMAIN-SUFFIX,zimuzu.tv,DIRECT +DOMAIN-SUFFIX,zoho.com,DIRECT + +# 常见广告 +DOMAIN-KEYWORD,admarvel,REJECT-TINYGIF +DOMAIN-KEYWORD,admaster,REJECT-TINYGIF +DOMAIN-KEYWORD,adsage,REJECT-TINYGIF +DOMAIN-KEYWORD,adsmogo,REJECT-TINYGIF +DOMAIN-KEYWORD,adsrvmedia,REJECT-TINYGIF +DOMAIN-KEYWORD,adwords,REJECT-TINYGIF +DOMAIN-KEYWORD,adservice,REJECT-TINYGIF +DOMAIN-SUFFIX,appsflyer.com,REJECT-TINYGIF +DOMAIN-KEYWORD,domob,REJECT-TINYGIF +DOMAIN-SUFFIX,doubleclick.net,REJECT-TINYGIF +DOMAIN-KEYWORD,duomeng,REJECT-TINYGIF +DOMAIN-KEYWORD,dwtrack,REJECT-TINYGIF +DOMAIN-KEYWORD,guanggao,REJECT-TINYGIF +DOMAIN-KEYWORD,lianmeng,REJECT-TINYGIF +DOMAIN-SUFFIX,mmstat.com,REJECT-TINYGIF +DOMAIN-KEYWORD,mopub,REJECT-TINYGIF +DOMAIN-KEYWORD,omgmta,REJECT-TINYGIF +DOMAIN-KEYWORD,openx,REJECT-TINYGIF +DOMAIN-KEYWORD,partnerad,REJECT-TINYGIF +DOMAIN-KEYWORD,pingfore,REJECT-TINYGIF +DOMAIN-KEYWORD,supersonicads,REJECT-TINYGIF +DOMAIN-KEYWORD,uedas,REJECT-TINYGIF +DOMAIN-KEYWORD,umeng,REJECT-TINYGIF +DOMAIN-KEYWORD,usage,REJECT-TINYGIF +DOMAIN-SUFFIX,vungle.com,REJECT-TINYGIF +DOMAIN-KEYWORD,wlmonitor,REJECT-TINYGIF +DOMAIN-KEYWORD,zjtoolbar,REJECT-TINYGIF + +## 抗 DNS 污染 +DOMAIN-KEYWORD,amazon,Proxy +DOMAIN-KEYWORD,google,Proxy +DOMAIN-KEYWORD,gmail,Proxy +DOMAIN-KEYWORD,youtube,Proxy +DOMAIN-KEYWORD,facebook,Proxy +DOMAIN-SUFFIX,fb.me,Proxy +DOMAIN-SUFFIX,fbcdn.net,Proxy +DOMAIN-KEYWORD,twitter,Proxy +DOMAIN-KEYWORD,instagram,Proxy +DOMAIN-KEYWORD,dropbox,Proxy +DOMAIN-SUFFIX,twimg.com,Proxy +DOMAIN-KEYWORD,blogspot,Proxy +DOMAIN-SUFFIX,youtu.be,Proxy + +## 常见国外域名列表 +DOMAIN-SUFFIX,9to5mac.com,Proxy +DOMAIN-SUFFIX,abpchina.org,Proxy +DOMAIN-SUFFIX,adblockplus.org,Proxy +DOMAIN-SUFFIX,adobe.com,Proxy +DOMAIN-SUFFIX,akamaized.net,Proxy +DOMAIN-SUFFIX,alfredapp.com,Proxy +DOMAIN-SUFFIX,amplitude.com,Proxy +DOMAIN-SUFFIX,ampproject.org,Proxy +DOMAIN-SUFFIX,android.com,Proxy +DOMAIN-SUFFIX,angularjs.org,Proxy +DOMAIN-SUFFIX,aolcdn.com,Proxy +DOMAIN-SUFFIX,apkpure.com,Proxy +DOMAIN-SUFFIX,appledaily.com,Proxy +DOMAIN-SUFFIX,appshopper.com,Proxy +DOMAIN-SUFFIX,appspot.com,Proxy +DOMAIN-SUFFIX,arcgis.com,Proxy +DOMAIN-SUFFIX,archive.org,Proxy +DOMAIN-SUFFIX,armorgames.com,Proxy +DOMAIN-SUFFIX,aspnetcdn.com,Proxy +DOMAIN-SUFFIX,att.com,Proxy +DOMAIN-SUFFIX,awsstatic.com,Proxy +DOMAIN-SUFFIX,azureedge.net,Proxy +DOMAIN-SUFFIX,azurewebsites.net,Proxy +DOMAIN-SUFFIX,bing.com,Proxy +DOMAIN-SUFFIX,bintray.com,Proxy +DOMAIN-SUFFIX,bit.com,Proxy +DOMAIN-SUFFIX,bit.ly,Proxy +DOMAIN-SUFFIX,bitbucket.org,Proxy +DOMAIN-SUFFIX,bjango.com,Proxy +DOMAIN-SUFFIX,bkrtx.com,Proxy +DOMAIN-SUFFIX,blog.com,Proxy +DOMAIN-SUFFIX,blogcdn.com,Proxy +DOMAIN-SUFFIX,blogger.com,Proxy +DOMAIN-SUFFIX,blogsmithmedia.com,Proxy +DOMAIN-SUFFIX,blogspot.com,Proxy +DOMAIN-SUFFIX,blogspot.hk,Proxy +DOMAIN-SUFFIX,bloomberg.com,Proxy +DOMAIN-SUFFIX,box.com,Proxy +DOMAIN-SUFFIX,box.net,Proxy +DOMAIN-SUFFIX,cachefly.net,Proxy +DOMAIN-SUFFIX,chromium.org,Proxy +DOMAIN-SUFFIX,cl.ly,Proxy +DOMAIN-SUFFIX,cloudflare.com,Proxy +DOMAIN-SUFFIX,cloudfront.net,Proxy +DOMAIN-SUFFIX,cloudmagic.com,Proxy +DOMAIN-SUFFIX,cmail19.com,Proxy +DOMAIN-SUFFIX,cnet.com,Proxy +DOMAIN-SUFFIX,cocoapods.org,Proxy +DOMAIN-SUFFIX,comodoca.com,Proxy +DOMAIN-SUFFIX,crashlytics.com,Proxy +DOMAIN-SUFFIX,culturedcode.com,Proxy +DOMAIN-SUFFIX,d.pr,Proxy +DOMAIN-SUFFIX,danilo.to,Proxy +DOMAIN-SUFFIX,dayone.me,Proxy +DOMAIN-SUFFIX,db.tt,Proxy +DOMAIN-SUFFIX,deskconnect.com,Proxy +DOMAIN-SUFFIX,disq.us,Proxy +DOMAIN-SUFFIX,disqus.com,Proxy +DOMAIN-SUFFIX,disquscdn.com,Proxy +DOMAIN-SUFFIX,dnsimple.com,Proxy +DOMAIN-SUFFIX,docker.com,Proxy +DOMAIN-SUFFIX,dribbble.com,Proxy +DOMAIN-SUFFIX,droplr.com,Proxy +DOMAIN-SUFFIX,duckduckgo.com,Proxy +DOMAIN-SUFFIX,dueapp.com,Proxy +DOMAIN-SUFFIX,dytt8.net,Proxy +DOMAIN-SUFFIX,edgecastcdn.net,Proxy +DOMAIN-SUFFIX,edgekey.net,Proxy +DOMAIN-SUFFIX,edgesuite.net,Proxy +DOMAIN-SUFFIX,engadget.com,Proxy +DOMAIN-SUFFIX,entrust.net,Proxy +DOMAIN-SUFFIX,eurekavpt.com,Proxy +DOMAIN-SUFFIX,evernote.com,Proxy +DOMAIN-SUFFIX,fabric.io,Proxy +DOMAIN-SUFFIX,fast.com,Proxy +DOMAIN-SUFFIX,fastly.net,Proxy +DOMAIN-SUFFIX,fc2.com,Proxy +DOMAIN-SUFFIX,feedburner.com,Proxy +DOMAIN-SUFFIX,feedly.com,Proxy +DOMAIN-SUFFIX,feedsportal.com,Proxy +DOMAIN-SUFFIX,fiftythree.com,Proxy +DOMAIN-SUFFIX,firebaseio.com,Proxy +DOMAIN-SUFFIX,flexibits.com,Proxy +DOMAIN-SUFFIX,flickr.com,Proxy +DOMAIN-SUFFIX,flipboard.com,Proxy +DOMAIN-SUFFIX,g.co,Proxy +DOMAIN-SUFFIX,gabia.net,Proxy +DOMAIN-SUFFIX,geni.us,Proxy +DOMAIN-SUFFIX,gfx.ms,Proxy +DOMAIN-SUFFIX,ggpht.com,Proxy +DOMAIN-SUFFIX,ghostnoteapp.com,Proxy +DOMAIN-SUFFIX,git.io,Proxy +DOMAIN-KEYWORD,github,Proxy +DOMAIN-SUFFIX,globalsign.com,Proxy +DOMAIN-SUFFIX,gmodules.com,Proxy +DOMAIN-SUFFIX,godaddy.com,Proxy +DOMAIN-SUFFIX,golang.org,Proxy +DOMAIN-SUFFIX,gongm.in,Proxy +DOMAIN-SUFFIX,goo.gl,Proxy +DOMAIN-SUFFIX,goodreaders.com,Proxy +DOMAIN-SUFFIX,goodreads.com,Proxy +DOMAIN-SUFFIX,gravatar.com,Proxy +DOMAIN-SUFFIX,gstatic.com,Proxy +DOMAIN-SUFFIX,gvt0.com,Proxy +DOMAIN-SUFFIX,hockeyapp.net,Proxy +DOMAIN-SUFFIX,hotmail.com,Proxy +DOMAIN-SUFFIX,icons8.com,Proxy +DOMAIN-SUFFIX,ifixit.com,Proxy +DOMAIN-SUFFIX,ift.tt,Proxy +DOMAIN-SUFFIX,ifttt.com,Proxy +DOMAIN-SUFFIX,iherb.com,Proxy +DOMAIN-SUFFIX,imageshack.us,Proxy +DOMAIN-SUFFIX,img.ly,Proxy +DOMAIN-SUFFIX,imgur.com,Proxy +DOMAIN-SUFFIX,imore.com,Proxy +DOMAIN-SUFFIX,instapaper.com,Proxy +DOMAIN-SUFFIX,ipn.li,Proxy +DOMAIN-SUFFIX,is.gd,Proxy +DOMAIN-SUFFIX,issuu.com,Proxy +DOMAIN-SUFFIX,itgonglun.com,Proxy +DOMAIN-SUFFIX,itun.es,Proxy +DOMAIN-SUFFIX,ixquick.com,Proxy +DOMAIN-SUFFIX,j.mp,Proxy +DOMAIN-SUFFIX,js.revsci.net,Proxy +DOMAIN-SUFFIX,jshint.com,Proxy +DOMAIN-SUFFIX,jtvnw.net,Proxy +DOMAIN-SUFFIX,justgetflux.com,Proxy +DOMAIN-SUFFIX,kat.cr,Proxy +DOMAIN-SUFFIX,klip.me,Proxy +DOMAIN-SUFFIX,libsyn.com,Proxy +DOMAIN-SUFFIX,linkedin.com,Proxy +DOMAIN-SUFFIX,line-apps.com,Proxy +DOMAIN-SUFFIX,linode.com,Proxy +DOMAIN-SUFFIX,lithium.com,Proxy +DOMAIN-SUFFIX,littlehj.com,Proxy +DOMAIN-SUFFIX,live.com,Proxy +DOMAIN-SUFFIX,live.net,Proxy +DOMAIN-SUFFIX,livefilestore.com,Proxy +DOMAIN-SUFFIX,llnwd.net,Proxy +DOMAIN-SUFFIX,macid.co,Proxy +DOMAIN-SUFFIX,macromedia.com,Proxy +DOMAIN-SUFFIX,macrumors.com,Proxy +DOMAIN-SUFFIX,mashable.com,Proxy +DOMAIN-SUFFIX,mathjax.org,Proxy +DOMAIN-SUFFIX,medium.com,Proxy +DOMAIN-SUFFIX,mega.co.nz,Proxy +DOMAIN-SUFFIX,mega.nz,Proxy +DOMAIN-SUFFIX,megaupload.com,Proxy +DOMAIN-SUFFIX,microsofttranslator.com,Proxy +DOMAIN-SUFFIX,mindnode.com,Proxy +DOMAIN-SUFFIX,mobile01.com,Proxy +DOMAIN-SUFFIX,modmyi.com,Proxy +DOMAIN-SUFFIX,msedge.net,Proxy +DOMAIN-SUFFIX,myfontastic.com,Proxy +DOMAIN-SUFFIX,name.com,Proxy +DOMAIN-SUFFIX,nextmedia.com,Proxy +DOMAIN-SUFFIX,nsstatic.net,Proxy +DOMAIN-SUFFIX,nssurge.com,Proxy +DOMAIN-SUFFIX,nyt.com,Proxy +DOMAIN-SUFFIX,nytimes.com,Proxy +DOMAIN-SUFFIX,omnigroup.com,Proxy +DOMAIN-SUFFIX,onedrive.com,Proxy +DOMAIN-SUFFIX,onenote.com,Proxy +DOMAIN-SUFFIX,ooyala.com,Proxy +DOMAIN-SUFFIX,openvpn.net,Proxy +DOMAIN-SUFFIX,openwrt.org,Proxy +DOMAIN-SUFFIX,orkut.com,Proxy +DOMAIN-SUFFIX,osxdaily.com,Proxy +DOMAIN-SUFFIX,outlook.com,Proxy +DOMAIN-SUFFIX,ow.ly,Proxy +DOMAIN-SUFFIX,paddleapi.com,Proxy +DOMAIN-SUFFIX,parallels.com,Proxy +DOMAIN-SUFFIX,parse.com,Proxy +DOMAIN-SUFFIX,pdfexpert.com,Proxy +DOMAIN-SUFFIX,periscope.tv,Proxy +DOMAIN-SUFFIX,pinboard.in,Proxy +DOMAIN-SUFFIX,pinterest.com,Proxy +DOMAIN-SUFFIX,pixelmator.com,Proxy +DOMAIN-SUFFIX,pixiv.net,Proxy +DOMAIN-SUFFIX,playpcesor.com,Proxy +DOMAIN-SUFFIX,playstation.com,Proxy +DOMAIN-SUFFIX,playstation.com.hk,Proxy +DOMAIN-SUFFIX,playstation.net,Proxy +DOMAIN-SUFFIX,playstationnetwork.com,Proxy +DOMAIN-SUFFIX,pushwoosh.com,Proxy +DOMAIN-SUFFIX,rime.im,Proxy +DOMAIN-SUFFIX,servebom.com,Proxy +DOMAIN-SUFFIX,sfx.ms,Proxy +DOMAIN-SUFFIX,shadowsocks.org,Proxy +DOMAIN-SUFFIX,sharethis.com,Proxy +DOMAIN-SUFFIX,shazam.com,Proxy +DOMAIN-SUFFIX,skype.com,Proxy +DOMAIN-SUFFIX,smartdnsProxy.com,Proxy +DOMAIN-SUFFIX,smartmailcloud.com,Proxy +DOMAIN-SUFFIX,sndcdn.com,Proxy +DOMAIN-SUFFIX,sony.com,Proxy +DOMAIN-SUFFIX,soundcloud.com,Proxy +DOMAIN-SUFFIX,sourceforge.net,Proxy +DOMAIN-SUFFIX,spotify.com,Proxy +DOMAIN-SUFFIX,squarespace.com,Proxy +DOMAIN-SUFFIX,sstatic.net,Proxy +DOMAIN-SUFFIX,st.luluku.pw,Proxy +DOMAIN-SUFFIX,stackoverflow.com,Proxy +DOMAIN-SUFFIX,startpage.com,Proxy +DOMAIN-SUFFIX,staticflickr.com,Proxy +DOMAIN-SUFFIX,steamcommunity.com,Proxy +DOMAIN-SUFFIX,symauth.com,Proxy +DOMAIN-SUFFIX,symcb.com,Proxy +DOMAIN-SUFFIX,symcd.com,Proxy +DOMAIN-SUFFIX,tapbots.com,Proxy +DOMAIN-SUFFIX,tapbots.net,Proxy +DOMAIN-SUFFIX,tdesktop.com,Proxy +DOMAIN-SUFFIX,techcrunch.com,Proxy +DOMAIN-SUFFIX,techsmith.com,Proxy +DOMAIN-SUFFIX,thepiratebay.org,Proxy +DOMAIN-SUFFIX,theverge.com,Proxy +DOMAIN-SUFFIX,time.com,Proxy +DOMAIN-SUFFIX,timeinc.net,Proxy +DOMAIN-SUFFIX,tiny.cc,Proxy +DOMAIN-SUFFIX,tinypic.com,Proxy +DOMAIN-SUFFIX,tmblr.co,Proxy +DOMAIN-SUFFIX,todoist.com,Proxy +DOMAIN-SUFFIX,trello.com,Proxy +DOMAIN-SUFFIX,trustasiassl.com,Proxy +DOMAIN-SUFFIX,tumblr.co,Proxy +DOMAIN-SUFFIX,tumblr.com,Proxy +DOMAIN-SUFFIX,tweetdeck.com,Proxy +DOMAIN-SUFFIX,tweetmarker.net,Proxy +DOMAIN-SUFFIX,twitch.tv,Proxy +DOMAIN-SUFFIX,txmblr.com,Proxy +DOMAIN-SUFFIX,typekit.net,Proxy +DOMAIN-SUFFIX,ubertags.com,Proxy +DOMAIN-SUFFIX,ublock.org,Proxy +DOMAIN-SUFFIX,ubnt.com,Proxy +DOMAIN-SUFFIX,ulyssesapp.com,Proxy +DOMAIN-SUFFIX,urchin.com,Proxy +DOMAIN-SUFFIX,usertrust.com,Proxy +DOMAIN-SUFFIX,v.gd,Proxy +DOMAIN-SUFFIX,v2ex.com,Proxy +DOMAIN-SUFFIX,vimeo.com,Proxy +DOMAIN-SUFFIX,vimeocdn.com,Proxy +DOMAIN-SUFFIX,vine.co,Proxy +DOMAIN-SUFFIX,vivaldi.com,Proxy +DOMAIN-SUFFIX,vox-cdn.com,Proxy +DOMAIN-SUFFIX,vsco.co,Proxy +DOMAIN-SUFFIX,vultr.com,Proxy +DOMAIN-SUFFIX,w.org,Proxy +DOMAIN-SUFFIX,w3schools.com,Proxy +DOMAIN-SUFFIX,webtype.com,Proxy +DOMAIN-SUFFIX,wikiwand.com,Proxy +DOMAIN-SUFFIX,wikileaks.org,Proxy +DOMAIN-SUFFIX,wikimedia.org,Proxy +DOMAIN-SUFFIX,wikipedia.com,Proxy +DOMAIN-SUFFIX,wikipedia.org,Proxy +DOMAIN-SUFFIX,windows.com,Proxy +DOMAIN-SUFFIX,windows.net,Proxy +DOMAIN-SUFFIX,wire.com,Proxy +DOMAIN-SUFFIX,wordpress.com,Proxy +DOMAIN-SUFFIX,workflowy.com,Proxy +DOMAIN-SUFFIX,wp.com,Proxy +DOMAIN-SUFFIX,wsj.com,Proxy +DOMAIN-SUFFIX,wsj.net,Proxy +DOMAIN-SUFFIX,xda-developers.com,Proxy +DOMAIN-SUFFIX,xeeno.com,Proxy +DOMAIN-SUFFIX,xiti.com,Proxy +DOMAIN-SUFFIX,yahoo.com,Proxy +DOMAIN-SUFFIX,yimg.com,Proxy +DOMAIN-SUFFIX,ying.com,Proxy +DOMAIN-SUFFIX,yoyo.org,Proxy +DOMAIN-SUFFIX,ytimg.com,Proxy + +# Telegram +DOMAIN-SUFFIX,telegra.ph,Proxy +DOMAIN-SUFFIX,telegram.org,Proxy + +IP-CIDR,91.108.4.0/22,Proxy,no-resolve +IP-CIDR,91.108.8.0/21,Proxy,no-resolve +IP-CIDR,91.108.16.0/22,Proxy,no-resolve +IP-CIDR,91.108.56.0/22,Proxy,no-resolve +IP-CIDR,149.154.160.0/20,Proxy,no-resolve +IP-CIDR6,2001:67c:4e8::/48,Proxy,no-resolve +IP-CIDR6,2001:b28:f23d::/48,Proxy,no-resolve +IP-CIDR6,2001:b28:f23f::/48,Proxy,no-resolve + +# Google 中国服务 services.googleapis.cn +IP-CIDR,120.232.181.162/32,Proxy,no-resolve +IP-CIDR,120.241.147.226/32,Proxy,no-resolve +IP-CIDR,120.253.253.226/32,Proxy,no-resolve +IP-CIDR,120.253.255.162/32,Proxy,no-resolve +IP-CIDR,120.253.255.34/32,Proxy,no-resolve +IP-CIDR,120.253.255.98/32,Proxy,no-resolve +IP-CIDR,180.163.150.162/32,Proxy,no-resolve +IP-CIDR,180.163.150.34/32,Proxy,no-resolve +IP-CIDR,180.163.151.162/32,Proxy,no-resolve +IP-CIDR,180.163.151.34/32,Proxy,no-resolve +IP-CIDR,203.208.39.0/24,Proxy,no-resolve +IP-CIDR,203.208.40.0/24,Proxy,no-resolve +IP-CIDR,203.208.41.0/24,Proxy,no-resolve +IP-CIDR,203.208.43.0/24,Proxy,no-resolve +IP-CIDR,203.208.50.0/24,Proxy,no-resolve +IP-CIDR,220.181.174.162/32,Proxy,no-resolve +IP-CIDR,220.181.174.226/32,Proxy,no-resolve +IP-CIDR,220.181.174.34/32,Proxy,no-resolve + +# LAN +DOMAIN-SUFFIX,local,DIRECT +IP-CIDR,127.0.0.0/8,DIRECT +IP-CIDR,172.16.0.0/12,DIRECT +IP-CIDR,192.168.0.0/16,DIRECT +IP-CIDR,10.0.0.0/8,DIRECT +IP-CIDR,17.0.0.0/8,DIRECT +IP-CIDR,100.64.0.0/10,DIRECT +IP-CIDR,224.0.0.0/4,DIRECT +IP-CIDR6,fe80::/10,DIRECT + +# 剩余未匹配的国内网站 +DOMAIN-SUFFIX,cn,DIRECT +DOMAIN-KEYWORD,-cn,DIRECT + +# 最终规则 +GEOIP,CN,DIRECT +FINAL,Proxy \ No newline at end of file diff --git a/Xboard/resources/rules/default.surge.conf b/Xboard/resources/rules/default.surge.conf new file mode 100644 index 0000000..1560bbe --- /dev/null +++ b/Xboard/resources/rules/default.surge.conf @@ -0,0 +1,595 @@ +#!MANAGED-CONFIG $subs_link interval=43200 strict=true +# Surge 的规则配置手册: https://manual.nssurge.com/ +# Thanks @Hackl0us SS-Rule-Snippet + +[General] +loglevel = notify +# 从 Surge iOS 4 / Surge Mac 3.3.0 起,工具开始支持 DoH +doh-server = https://doh.pub/dns-query +# https://dns.alidns.com/dns-query, https://13800000000.rubyfish.cn/, https://dns.google/dns-query +dns-server = 223.5.5.5, 114.114.114.114 +tun-excluded-routes = 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 192.88.99.0/24, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 255.255.255.255/32 +skip-proxy = localhost, *.local, injections.adguard.org, local.adguard.org, captive.apple.com, guzzoni.apple.com, 0.0.0.0/8, 10.0.0.0/8, 17.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 192.88.99.0/24, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4, 255.255.255.255/32 + +wifi-assist = true +allow-wifi-access = true +wifi-access-http-port = 6152 +wifi-access-socks5-port = 6153 +http-listen = 0.0.0.0:6152 +socks5-listen = 0.0.0.0:6153 + +external-controller-access = surgepasswd@0.0.0.0:6170 +replica = false + +tls-provider = openssl +network-framework = false +exclude-simple-hostnames = true +ipv6 = true + +test-timeout = 4 +proxy-test-url = http://www.gstatic.com/generate_204 +geoip-maxmind-url = https://unpkg.zhimg.com/rulestatic@1.0.1/Country.mmdb + +[Replica] +hide-apple-request = true +hide-crashlytics-request = true +use-keyword-filter = false +hide-udp = false + +[Panel] +SubscribeInfo = $subscribe_info, style=info + +# ----------------------------- +# Surge 的几种策略配置规范,请参考 https://manual.nssurge.com/policy/proxy.html +# 不同的代理策略有*很多*可选参数,请参考上方连接的 Parameters 一段,根据需求自行添加参数。 +# +# Surge 现已支持 UDP 转发功能,请参考: https://trello.com/c/ugOMxD3u/53-udp-%E8%BD%AC%E5%8F%91 +# Surge 现已支持 TCP-Fast-Open 技术,请参考: https://trello.com/c/ij65BU6Q/48-tcp-fast-open-troubleshooting-guide +# Surge 现已支持 ss-libev 的全部加密方式和混淆,请参考: https://trello.com/c/BTr0vG1O/47-ss-libev-%E7%9A%84%E6%94%AF%E6%8C%81%E6%83%85%E5%86%B5 +# ----------------------------- + +[Proxy] +$proxies + +[Proxy Group] +Proxy = select, auto, fallback, $proxy_group +auto = url-test, $proxy_group, url=http://www.gstatic.com/generate_204, interval=43200 +fallback = fallback, $proxy_group, url=http://www.gstatic.com/generate_204, interval=43200 + +[Rule] +# 自定义规则 +## 您可以在此处插入自定义规则 + +# 强制订阅域名直连 +DOMAIN,$subs_domain,DIRECT + +# Google 中国服务 +DOMAIN-SUFFIX,services.googleapis.cn,Proxy +DOMAIN-SUFFIX,xn--ngstr-lra8j.com,Proxy + +# Apple +DOMAIN,developer.apple.com,Proxy +DOMAIN-SUFFIX,digicert.com,Proxy +USER-AGENT,com.apple.trustd*,Proxy +DOMAIN-SUFFIX,apple-dns.net,Proxy +DOMAIN,testflight.apple.com,Proxy +DOMAIN,sandbox.itunes.apple.com,Proxy +DOMAIN,itunes.apple.com,Proxy +DOMAIN-SUFFIX,apps.apple.com,Proxy +DOMAIN-SUFFIX,blobstore.apple.com,Proxy +DOMAIN,cvws.icloud-content.com,Proxy +DOMAIN,safebrowsing.urlsec.qq.com,DIRECT +DOMAIN,safebrowsing.googleapis.com,DIRECT +USER-AGENT,com.apple.appstored*,DIRECT +USER-AGENT,AppStore*,DIRECT +DOMAIN-SUFFIX,mzstatic.com,DIRECT +DOMAIN-SUFFIX,itunes.apple.com,DIRECT +DOMAIN-SUFFIX,icloud.com,DIRECT +DOMAIN-SUFFIX,icloud-content.com,DIRECT +USER-AGENT,cloudd*,DIRECT +USER-AGENT,*com.apple.WebKit*,DIRECT +USER-AGENT,*com.apple.*,DIRECT +DOMAIN-SUFFIX,me.com,DIRECT +DOMAIN-SUFFIX,aaplimg.com,DIRECT +DOMAIN-SUFFIX,cdn20.com,DIRECT +DOMAIN-SUFFIX,cdn-apple.com,DIRECT +DOMAIN-SUFFIX,akadns.net,DIRECT +DOMAIN-SUFFIX,akamaiedge.net,DIRECT +DOMAIN-SUFFIX,edgekey.net,DIRECT +DOMAIN-SUFFIX,mwcloudcdn.com,DIRECT +DOMAIN-SUFFIX,mwcname.com,DIRECT +DOMAIN-SUFFIX,apple.com,DIRECT +DOMAIN-SUFFIX,apple-cloudkit.com,DIRECT +DOMAIN-SUFFIX,apple-mapkit.com,DIRECT + +# 国内网站 +USER-AGENT,MicroMessenger Client*,DIRECT +USER-AGENT,WeChat*,DIRECT + +DOMAIN-SUFFIX,126.com,DIRECT +DOMAIN-SUFFIX,126.net,DIRECT +DOMAIN-SUFFIX,127.net,DIRECT +DOMAIN-SUFFIX,163.com,DIRECT +DOMAIN-SUFFIX,360buyimg.com,DIRECT +DOMAIN-SUFFIX,36kr.com,DIRECT +DOMAIN-SUFFIX,acfun.tv,DIRECT +DOMAIN-SUFFIX,air-matters.com,DIRECT +DOMAIN-SUFFIX,aixifan.com,DIRECT +DOMAIN-KEYWORD,alicdn,DIRECT +DOMAIN-KEYWORD,alipay,DIRECT +DOMAIN-KEYWORD,aliyun,DIRECT +DOMAIN-KEYWORD,taobao,DIRECT +DOMAIN-SUFFIX,amap.com,DIRECT +DOMAIN-SUFFIX,autonavi.com,DIRECT +DOMAIN-KEYWORD,baidu,DIRECT +DOMAIN-SUFFIX,bdimg.com,DIRECT +DOMAIN-SUFFIX,bdstatic.com,DIRECT +DOMAIN-SUFFIX,bilibili.com,DIRECT +DOMAIN-SUFFIX,bilivideo.com,DIRECT +DOMAIN-SUFFIX,caiyunapp.com,DIRECT +DOMAIN-SUFFIX,clouddn.com,DIRECT +DOMAIN-SUFFIX,cnbeta.com,DIRECT +DOMAIN-SUFFIX,cnbetacdn.com,DIRECT +DOMAIN-SUFFIX,cootekservice.com,DIRECT +DOMAIN-SUFFIX,csdn.net,DIRECT +DOMAIN-SUFFIX,ctrip.com,DIRECT +DOMAIN-SUFFIX,dgtle.com,DIRECT +DOMAIN-SUFFIX,dianping.com,DIRECT +DOMAIN-SUFFIX,douban.com,DIRECT +DOMAIN-SUFFIX,doubanio.com,DIRECT +DOMAIN-SUFFIX,duokan.com,DIRECT +DOMAIN-SUFFIX,easou.com,DIRECT +DOMAIN-SUFFIX,ele.me,DIRECT +DOMAIN-SUFFIX,feng.com,DIRECT +DOMAIN-SUFFIX,fir.im,DIRECT +DOMAIN-SUFFIX,frdic.com,DIRECT +DOMAIN-SUFFIX,g-cores.com,DIRECT +DOMAIN-SUFFIX,godic.net,DIRECT +DOMAIN-SUFFIX,gtimg.com,DIRECT +DOMAIN-SUFFIX,hongxiu.com,DIRECT +DOMAIN-SUFFIX,hxcdn.net,DIRECT +DOMAIN-SUFFIX,iciba.com,DIRECT +DOMAIN-SUFFIX,ifeng.com,DIRECT +DOMAIN-SUFFIX,ifengimg.com,DIRECT +DOMAIN-SUFFIX,ipip.net,DIRECT +DOMAIN-SUFFIX,iqiyi.com,DIRECT +DOMAIN-SUFFIX,jd.com,DIRECT +DOMAIN-SUFFIX,jianshu.com,DIRECT +DOMAIN-SUFFIX,knewone.com,DIRECT +DOMAIN-SUFFIX,le.com,DIRECT +DOMAIN-SUFFIX,lecloud.com,DIRECT +DOMAIN-SUFFIX,lemicp.com,DIRECT +DOMAIN-SUFFIX,licdn.com,DIRECT +DOMAIN-SUFFIX,luoo.net,DIRECT +DOMAIN-SUFFIX,meituan.com,DIRECT +DOMAIN-SUFFIX,meituan.net,DIRECT +DOMAIN-SUFFIX,mi.com,DIRECT +DOMAIN-SUFFIX,miaopai.com,DIRECT +DOMAIN-SUFFIX,microsoft.com,DIRECT +DOMAIN-SUFFIX,microsoftonline.com,DIRECT +DOMAIN-SUFFIX,miui.com,DIRECT +DOMAIN-SUFFIX,miwifi.com,DIRECT +DOMAIN-SUFFIX,mob.com,DIRECT +DOMAIN-SUFFIX,netease.com,DIRECT +DOMAIN-SUFFIX,office.com,DIRECT +DOMAIN-KEYWORD,officecdn,DIRECT +DOMAIN-SUFFIX,office365.com,DIRECT +DOMAIN-SUFFIX,oschina.net,DIRECT +DOMAIN-SUFFIX,ppsimg.com,DIRECT +DOMAIN-SUFFIX,pstatp.com,DIRECT +DOMAIN-SUFFIX,qcloud.com,DIRECT +DOMAIN-SUFFIX,qdaily.com,DIRECT +DOMAIN-SUFFIX,qdmm.com,DIRECT +DOMAIN-SUFFIX,qhimg.com,DIRECT +DOMAIN-SUFFIX,qhres.com,DIRECT +DOMAIN-SUFFIX,qidian.com,DIRECT +DOMAIN-SUFFIX,qihucdn.com,DIRECT +DOMAIN-SUFFIX,qiniu.com,DIRECT +DOMAIN-SUFFIX,qiniucdn.com,DIRECT +DOMAIN-SUFFIX,qiyipic.com,DIRECT +DOMAIN-SUFFIX,qq.com,DIRECT +DOMAIN-SUFFIX,qqurl.com,DIRECT +DOMAIN-SUFFIX,rarbg.to,DIRECT +DOMAIN-SUFFIX,ruguoapp.com,DIRECT +DOMAIN-SUFFIX,segmentfault.com,DIRECT +DOMAIN-SUFFIX,sinaapp.com,DIRECT +DOMAIN-SUFFIX,smzdm.com,DIRECT +DOMAIN-SUFFIX,snapdrop.net,DIRECT +DOMAIN-SUFFIX,sogou.com,DIRECT +DOMAIN-SUFFIX,sogoucdn.com,DIRECT +DOMAIN-SUFFIX,sohu.com,DIRECT +DOMAIN-SUFFIX,soku.com,DIRECT +DOMAIN-SUFFIX,speedtest.net,DIRECT +DOMAIN-SUFFIX,sspai.com,DIRECT +DOMAIN-SUFFIX,suning.com,DIRECT +DOMAIN-SUFFIX,taobao.com,DIRECT +DOMAIN-SUFFIX,tencent.com,DIRECT +DOMAIN-SUFFIX,tenpay.com,DIRECT +DOMAIN-SUFFIX,tianyancha.com,DIRECT +DOMAIN-KEYWORD,.tmall.com,DIRECT +DOMAIN-SUFFIX,tudou.com,DIRECT +DOMAIN-SUFFIX,umetrip.com,DIRECT +DOMAIN-SUFFIX,upaiyun.com,DIRECT +DOMAIN-SUFFIX,upyun.com,DIRECT +DOMAIN-SUFFIX,veryzhun.com,DIRECT +DOMAIN-SUFFIX,weather.com,DIRECT +DOMAIN-SUFFIX,weibo.com,DIRECT +DOMAIN-SUFFIX,xiami.com,DIRECT +DOMAIN-SUFFIX,xiami.net,DIRECT +DOMAIN-SUFFIX,xiaomicp.com,DIRECT +DOMAIN-SUFFIX,ximalaya.com,DIRECT +DOMAIN-SUFFIX,xmcdn.com,DIRECT +DOMAIN-SUFFIX,xunlei.com,DIRECT +DOMAIN-SUFFIX,yhd.com,DIRECT +DOMAIN-SUFFIX,yihaodianimg.com,DIRECT +DOMAIN-SUFFIX,yinxiang.com,DIRECT +DOMAIN-SUFFIX,ykimg.com,DIRECT +DOMAIN-SUFFIX,youdao.com,DIRECT +DOMAIN-SUFFIX,youku.com,DIRECT +DOMAIN-SUFFIX,zealer.com,DIRECT +DOMAIN-SUFFIX,zhihu.com,DIRECT +DOMAIN-SUFFIX,zhimg.com,DIRECT +DOMAIN-SUFFIX,zimuzu.tv,DIRECT +DOMAIN-SUFFIX,zoho.com,DIRECT + +# 常见广告 +DOMAIN-KEYWORD,admarvel,REJECT-TINYGIF +DOMAIN-KEYWORD,admaster,REJECT-TINYGIF +DOMAIN-KEYWORD,adsage,REJECT-TINYGIF +DOMAIN-KEYWORD,adsmogo,REJECT-TINYGIF +DOMAIN-KEYWORD,adsrvmedia,REJECT-TINYGIF +DOMAIN-KEYWORD,adwords,REJECT-TINYGIF +DOMAIN-KEYWORD,adservice,REJECT-TINYGIF +DOMAIN-SUFFIX,appsflyer.com,REJECT-TINYGIF +DOMAIN-KEYWORD,domob,REJECT-TINYGIF +DOMAIN-SUFFIX,doubleclick.net,REJECT-TINYGIF +DOMAIN-KEYWORD,duomeng,REJECT-TINYGIF +DOMAIN-KEYWORD,dwtrack,REJECT-TINYGIF +DOMAIN-KEYWORD,guanggao,REJECT-TINYGIF +DOMAIN-KEYWORD,lianmeng,REJECT-TINYGIF +DOMAIN-SUFFIX,mmstat.com,REJECT-TINYGIF +DOMAIN-KEYWORD,mopub,REJECT-TINYGIF +DOMAIN-KEYWORD,omgmta,REJECT-TINYGIF +DOMAIN-KEYWORD,openx,REJECT-TINYGIF +DOMAIN-KEYWORD,partnerad,REJECT-TINYGIF +DOMAIN-KEYWORD,pingfore,REJECT-TINYGIF +DOMAIN-KEYWORD,supersonicads,REJECT-TINYGIF +DOMAIN-KEYWORD,uedas,REJECT-TINYGIF +DOMAIN-KEYWORD,umeng,REJECT-TINYGIF +DOMAIN-KEYWORD,usage,REJECT-TINYGIF +DOMAIN-SUFFIX,vungle.com,REJECT-TINYGIF +DOMAIN-KEYWORD,wlmonitor,REJECT-TINYGIF +DOMAIN-KEYWORD,zjtoolbar,REJECT-TINYGIF + +## 抗 DNS 污染 +DOMAIN-KEYWORD,amazon,Proxy +DOMAIN-KEYWORD,google,Proxy +DOMAIN-KEYWORD,gmail,Proxy +DOMAIN-KEYWORD,youtube,Proxy +DOMAIN-KEYWORD,facebook,Proxy +DOMAIN-SUFFIX,fb.me,Proxy +DOMAIN-SUFFIX,fbcdn.net,Proxy +DOMAIN-KEYWORD,twitter,Proxy +DOMAIN-KEYWORD,instagram,Proxy +DOMAIN-KEYWORD,dropbox,Proxy +DOMAIN-SUFFIX,twimg.com,Proxy +DOMAIN-KEYWORD,blogspot,Proxy +DOMAIN-SUFFIX,youtu.be,Proxy + +## 常见国外域名列表 +DOMAIN-SUFFIX,9to5mac.com,Proxy +DOMAIN-SUFFIX,abpchina.org,Proxy +DOMAIN-SUFFIX,adblockplus.org,Proxy +DOMAIN-SUFFIX,adobe.com,Proxy +DOMAIN-SUFFIX,akamaized.net,Proxy +DOMAIN-SUFFIX,alfredapp.com,Proxy +DOMAIN-SUFFIX,amplitude.com,Proxy +DOMAIN-SUFFIX,ampproject.org,Proxy +DOMAIN-SUFFIX,android.com,Proxy +DOMAIN-SUFFIX,angularjs.org,Proxy +DOMAIN-SUFFIX,aolcdn.com,Proxy +DOMAIN-SUFFIX,apkpure.com,Proxy +DOMAIN-SUFFIX,appledaily.com,Proxy +DOMAIN-SUFFIX,appshopper.com,Proxy +DOMAIN-SUFFIX,appspot.com,Proxy +DOMAIN-SUFFIX,arcgis.com,Proxy +DOMAIN-SUFFIX,archive.org,Proxy +DOMAIN-SUFFIX,armorgames.com,Proxy +DOMAIN-SUFFIX,aspnetcdn.com,Proxy +DOMAIN-SUFFIX,att.com,Proxy +DOMAIN-SUFFIX,awsstatic.com,Proxy +DOMAIN-SUFFIX,azureedge.net,Proxy +DOMAIN-SUFFIX,azurewebsites.net,Proxy +DOMAIN-SUFFIX,bing.com,Proxy +DOMAIN-SUFFIX,bintray.com,Proxy +DOMAIN-SUFFIX,bit.com,Proxy +DOMAIN-SUFFIX,bit.ly,Proxy +DOMAIN-SUFFIX,bitbucket.org,Proxy +DOMAIN-SUFFIX,bjango.com,Proxy +DOMAIN-SUFFIX,bkrtx.com,Proxy +DOMAIN-SUFFIX,blog.com,Proxy +DOMAIN-SUFFIX,blogcdn.com,Proxy +DOMAIN-SUFFIX,blogger.com,Proxy +DOMAIN-SUFFIX,blogsmithmedia.com,Proxy +DOMAIN-SUFFIX,blogspot.com,Proxy +DOMAIN-SUFFIX,blogspot.hk,Proxy +DOMAIN-SUFFIX,bloomberg.com,Proxy +DOMAIN-SUFFIX,box.com,Proxy +DOMAIN-SUFFIX,box.net,Proxy +DOMAIN-SUFFIX,cachefly.net,Proxy +DOMAIN-SUFFIX,chromium.org,Proxy +DOMAIN-SUFFIX,cl.ly,Proxy +DOMAIN-SUFFIX,cloudflare.com,Proxy +DOMAIN-SUFFIX,cloudfront.net,Proxy +DOMAIN-SUFFIX,cloudmagic.com,Proxy +DOMAIN-SUFFIX,cmail19.com,Proxy +DOMAIN-SUFFIX,cnet.com,Proxy +DOMAIN-SUFFIX,cocoapods.org,Proxy +DOMAIN-SUFFIX,comodoca.com,Proxy +DOMAIN-SUFFIX,crashlytics.com,Proxy +DOMAIN-SUFFIX,culturedcode.com,Proxy +DOMAIN-SUFFIX,d.pr,Proxy +DOMAIN-SUFFIX,danilo.to,Proxy +DOMAIN-SUFFIX,dayone.me,Proxy +DOMAIN-SUFFIX,db.tt,Proxy +DOMAIN-SUFFIX,deskconnect.com,Proxy +DOMAIN-SUFFIX,disq.us,Proxy +DOMAIN-SUFFIX,disqus.com,Proxy +DOMAIN-SUFFIX,disquscdn.com,Proxy +DOMAIN-SUFFIX,dnsimple.com,Proxy +DOMAIN-SUFFIX,docker.com,Proxy +DOMAIN-SUFFIX,dribbble.com,Proxy +DOMAIN-SUFFIX,droplr.com,Proxy +DOMAIN-SUFFIX,duckduckgo.com,Proxy +DOMAIN-SUFFIX,dueapp.com,Proxy +DOMAIN-SUFFIX,dytt8.net,Proxy +DOMAIN-SUFFIX,edgecastcdn.net,Proxy +DOMAIN-SUFFIX,edgekey.net,Proxy +DOMAIN-SUFFIX,edgesuite.net,Proxy +DOMAIN-SUFFIX,engadget.com,Proxy +DOMAIN-SUFFIX,entrust.net,Proxy +DOMAIN-SUFFIX,eurekavpt.com,Proxy +DOMAIN-SUFFIX,evernote.com,Proxy +DOMAIN-SUFFIX,fabric.io,Proxy +DOMAIN-SUFFIX,fast.com,Proxy +DOMAIN-SUFFIX,fastly.net,Proxy +DOMAIN-SUFFIX,fc2.com,Proxy +DOMAIN-SUFFIX,feedburner.com,Proxy +DOMAIN-SUFFIX,feedly.com,Proxy +DOMAIN-SUFFIX,feedsportal.com,Proxy +DOMAIN-SUFFIX,fiftythree.com,Proxy +DOMAIN-SUFFIX,firebaseio.com,Proxy +DOMAIN-SUFFIX,flexibits.com,Proxy +DOMAIN-SUFFIX,flickr.com,Proxy +DOMAIN-SUFFIX,flipboard.com,Proxy +DOMAIN-SUFFIX,g.co,Proxy +DOMAIN-SUFFIX,gabia.net,Proxy +DOMAIN-SUFFIX,geni.us,Proxy +DOMAIN-SUFFIX,gfx.ms,Proxy +DOMAIN-SUFFIX,ggpht.com,Proxy +DOMAIN-SUFFIX,ghostnoteapp.com,Proxy +DOMAIN-SUFFIX,git.io,Proxy +DOMAIN-KEYWORD,github,Proxy +DOMAIN-SUFFIX,globalsign.com,Proxy +DOMAIN-SUFFIX,gmodules.com,Proxy +DOMAIN-SUFFIX,godaddy.com,Proxy +DOMAIN-SUFFIX,golang.org,Proxy +DOMAIN-SUFFIX,gongm.in,Proxy +DOMAIN-SUFFIX,goo.gl,Proxy +DOMAIN-SUFFIX,goodreaders.com,Proxy +DOMAIN-SUFFIX,goodreads.com,Proxy +DOMAIN-SUFFIX,gravatar.com,Proxy +DOMAIN-SUFFIX,gstatic.com,Proxy +DOMAIN-SUFFIX,gvt0.com,Proxy +DOMAIN-SUFFIX,hockeyapp.net,Proxy +DOMAIN-SUFFIX,hotmail.com,Proxy +DOMAIN-SUFFIX,icons8.com,Proxy +DOMAIN-SUFFIX,ifixit.com,Proxy +DOMAIN-SUFFIX,ift.tt,Proxy +DOMAIN-SUFFIX,ifttt.com,Proxy +DOMAIN-SUFFIX,iherb.com,Proxy +DOMAIN-SUFFIX,imageshack.us,Proxy +DOMAIN-SUFFIX,img.ly,Proxy +DOMAIN-SUFFIX,imgur.com,Proxy +DOMAIN-SUFFIX,imore.com,Proxy +DOMAIN-SUFFIX,instapaper.com,Proxy +DOMAIN-SUFFIX,ipn.li,Proxy +DOMAIN-SUFFIX,is.gd,Proxy +DOMAIN-SUFFIX,issuu.com,Proxy +DOMAIN-SUFFIX,itgonglun.com,Proxy +DOMAIN-SUFFIX,itun.es,Proxy +DOMAIN-SUFFIX,ixquick.com,Proxy +DOMAIN-SUFFIX,j.mp,Proxy +DOMAIN-SUFFIX,js.revsci.net,Proxy +DOMAIN-SUFFIX,jshint.com,Proxy +DOMAIN-SUFFIX,jtvnw.net,Proxy +DOMAIN-SUFFIX,justgetflux.com,Proxy +DOMAIN-SUFFIX,kat.cr,Proxy +DOMAIN-SUFFIX,klip.me,Proxy +DOMAIN-SUFFIX,libsyn.com,Proxy +DOMAIN-SUFFIX,linkedin.com,Proxy +DOMAIN-SUFFIX,line-apps.com,Proxy +DOMAIN-SUFFIX,linode.com,Proxy +DOMAIN-SUFFIX,lithium.com,Proxy +DOMAIN-SUFFIX,littlehj.com,Proxy +DOMAIN-SUFFIX,live.com,Proxy +DOMAIN-SUFFIX,live.net,Proxy +DOMAIN-SUFFIX,livefilestore.com,Proxy +DOMAIN-SUFFIX,llnwd.net,Proxy +DOMAIN-SUFFIX,macid.co,Proxy +DOMAIN-SUFFIX,macromedia.com,Proxy +DOMAIN-SUFFIX,macrumors.com,Proxy +DOMAIN-SUFFIX,mashable.com,Proxy +DOMAIN-SUFFIX,mathjax.org,Proxy +DOMAIN-SUFFIX,medium.com,Proxy +DOMAIN-SUFFIX,mega.co.nz,Proxy +DOMAIN-SUFFIX,mega.nz,Proxy +DOMAIN-SUFFIX,megaupload.com,Proxy +DOMAIN-SUFFIX,microsofttranslator.com,Proxy +DOMAIN-SUFFIX,mindnode.com,Proxy +DOMAIN-SUFFIX,mobile01.com,Proxy +DOMAIN-SUFFIX,modmyi.com,Proxy +DOMAIN-SUFFIX,msedge.net,Proxy +DOMAIN-SUFFIX,myfontastic.com,Proxy +DOMAIN-SUFFIX,name.com,Proxy +DOMAIN-SUFFIX,nextmedia.com,Proxy +DOMAIN-SUFFIX,nsstatic.net,Proxy +DOMAIN-SUFFIX,nssurge.com,Proxy +DOMAIN-SUFFIX,nyt.com,Proxy +DOMAIN-SUFFIX,nytimes.com,Proxy +DOMAIN-SUFFIX,omnigroup.com,Proxy +DOMAIN-SUFFIX,onedrive.com,Proxy +DOMAIN-SUFFIX,onenote.com,Proxy +DOMAIN-SUFFIX,ooyala.com,Proxy +DOMAIN-SUFFIX,openvpn.net,Proxy +DOMAIN-SUFFIX,openwrt.org,Proxy +DOMAIN-SUFFIX,orkut.com,Proxy +DOMAIN-SUFFIX,osxdaily.com,Proxy +DOMAIN-SUFFIX,outlook.com,Proxy +DOMAIN-SUFFIX,ow.ly,Proxy +DOMAIN-SUFFIX,paddleapi.com,Proxy +DOMAIN-SUFFIX,parallels.com,Proxy +DOMAIN-SUFFIX,parse.com,Proxy +DOMAIN-SUFFIX,pdfexpert.com,Proxy +DOMAIN-SUFFIX,periscope.tv,Proxy +DOMAIN-SUFFIX,pinboard.in,Proxy +DOMAIN-SUFFIX,pinterest.com,Proxy +DOMAIN-SUFFIX,pixelmator.com,Proxy +DOMAIN-SUFFIX,pixiv.net,Proxy +DOMAIN-SUFFIX,playpcesor.com,Proxy +DOMAIN-SUFFIX,playstation.com,Proxy +DOMAIN-SUFFIX,playstation.com.hk,Proxy +DOMAIN-SUFFIX,playstation.net,Proxy +DOMAIN-SUFFIX,playstationnetwork.com,Proxy +DOMAIN-SUFFIX,pushwoosh.com,Proxy +DOMAIN-SUFFIX,rime.im,Proxy +DOMAIN-SUFFIX,servebom.com,Proxy +DOMAIN-SUFFIX,sfx.ms,Proxy +DOMAIN-SUFFIX,shadowsocks.org,Proxy +DOMAIN-SUFFIX,sharethis.com,Proxy +DOMAIN-SUFFIX,shazam.com,Proxy +DOMAIN-SUFFIX,skype.com,Proxy +DOMAIN-SUFFIX,smartdnsProxy.com,Proxy +DOMAIN-SUFFIX,smartmailcloud.com,Proxy +DOMAIN-SUFFIX,sndcdn.com,Proxy +DOMAIN-SUFFIX,sony.com,Proxy +DOMAIN-SUFFIX,soundcloud.com,Proxy +DOMAIN-SUFFIX,sourceforge.net,Proxy +DOMAIN-SUFFIX,spotify.com,Proxy +DOMAIN-SUFFIX,squarespace.com,Proxy +DOMAIN-SUFFIX,sstatic.net,Proxy +DOMAIN-SUFFIX,st.luluku.pw,Proxy +DOMAIN-SUFFIX,stackoverflow.com,Proxy +DOMAIN-SUFFIX,startpage.com,Proxy +DOMAIN-SUFFIX,staticflickr.com,Proxy +DOMAIN-SUFFIX,steamcommunity.com,Proxy +DOMAIN-SUFFIX,symauth.com,Proxy +DOMAIN-SUFFIX,symcb.com,Proxy +DOMAIN-SUFFIX,symcd.com,Proxy +DOMAIN-SUFFIX,tapbots.com,Proxy +DOMAIN-SUFFIX,tapbots.net,Proxy +DOMAIN-SUFFIX,tdesktop.com,Proxy +DOMAIN-SUFFIX,techcrunch.com,Proxy +DOMAIN-SUFFIX,techsmith.com,Proxy +DOMAIN-SUFFIX,thepiratebay.org,Proxy +DOMAIN-SUFFIX,theverge.com,Proxy +DOMAIN-SUFFIX,time.com,Proxy +DOMAIN-SUFFIX,timeinc.net,Proxy +DOMAIN-SUFFIX,tiny.cc,Proxy +DOMAIN-SUFFIX,tinypic.com,Proxy +DOMAIN-SUFFIX,tmblr.co,Proxy +DOMAIN-SUFFIX,todoist.com,Proxy +DOMAIN-SUFFIX,trello.com,Proxy +DOMAIN-SUFFIX,trustasiassl.com,Proxy +DOMAIN-SUFFIX,tumblr.co,Proxy +DOMAIN-SUFFIX,tumblr.com,Proxy +DOMAIN-SUFFIX,tweetdeck.com,Proxy +DOMAIN-SUFFIX,tweetmarker.net,Proxy +DOMAIN-SUFFIX,twitch.tv,Proxy +DOMAIN-SUFFIX,txmblr.com,Proxy +DOMAIN-SUFFIX,typekit.net,Proxy +DOMAIN-SUFFIX,ubertags.com,Proxy +DOMAIN-SUFFIX,ublock.org,Proxy +DOMAIN-SUFFIX,ubnt.com,Proxy +DOMAIN-SUFFIX,ulyssesapp.com,Proxy +DOMAIN-SUFFIX,urchin.com,Proxy +DOMAIN-SUFFIX,usertrust.com,Proxy +DOMAIN-SUFFIX,v.gd,Proxy +DOMAIN-SUFFIX,v2ex.com,Proxy +DOMAIN-SUFFIX,vimeo.com,Proxy +DOMAIN-SUFFIX,vimeocdn.com,Proxy +DOMAIN-SUFFIX,vine.co,Proxy +DOMAIN-SUFFIX,vivaldi.com,Proxy +DOMAIN-SUFFIX,vox-cdn.com,Proxy +DOMAIN-SUFFIX,vsco.co,Proxy +DOMAIN-SUFFIX,vultr.com,Proxy +DOMAIN-SUFFIX,w.org,Proxy +DOMAIN-SUFFIX,w3schools.com,Proxy +DOMAIN-SUFFIX,webtype.com,Proxy +DOMAIN-SUFFIX,wikiwand.com,Proxy +DOMAIN-SUFFIX,wikileaks.org,Proxy +DOMAIN-SUFFIX,wikimedia.org,Proxy +DOMAIN-SUFFIX,wikipedia.com,Proxy +DOMAIN-SUFFIX,wikipedia.org,Proxy +DOMAIN-SUFFIX,windows.com,Proxy +DOMAIN-SUFFIX,windows.net,Proxy +DOMAIN-SUFFIX,wire.com,Proxy +DOMAIN-SUFFIX,wordpress.com,Proxy +DOMAIN-SUFFIX,workflowy.com,Proxy +DOMAIN-SUFFIX,wp.com,Proxy +DOMAIN-SUFFIX,wsj.com,Proxy +DOMAIN-SUFFIX,wsj.net,Proxy +DOMAIN-SUFFIX,xda-developers.com,Proxy +DOMAIN-SUFFIX,xeeno.com,Proxy +DOMAIN-SUFFIX,xiti.com,Proxy +DOMAIN-SUFFIX,yahoo.com,Proxy +DOMAIN-SUFFIX,yimg.com,Proxy +DOMAIN-SUFFIX,ying.com,Proxy +DOMAIN-SUFFIX,yoyo.org,Proxy +DOMAIN-SUFFIX,ytimg.com,Proxy + +# Telegram +DOMAIN-SUFFIX,telegra.ph,Proxy +DOMAIN-SUFFIX,telegram.org,Proxy + +IP-CIDR,91.108.4.0/22,Proxy,no-resolve +IP-CIDR,91.108.8.0/21,Proxy,no-resolve +IP-CIDR,91.108.16.0/22,Proxy,no-resolve +IP-CIDR,91.108.56.0/22,Proxy,no-resolve +IP-CIDR,149.154.160.0/20,Proxy,no-resolve +IP-CIDR6,2001:67c:4e8::/48,Proxy,no-resolve +IP-CIDR6,2001:b28:f23d::/48,Proxy,no-resolve +IP-CIDR6,2001:b28:f23f::/48,Proxy,no-resolve + +# Google 中国服务 services.googleapis.cn +IP-CIDR,120.232.181.162/32,Proxy,no-resolve +IP-CIDR,120.241.147.226/32,Proxy,no-resolve +IP-CIDR,120.253.253.226/32,Proxy,no-resolve +IP-CIDR,120.253.255.162/32,Proxy,no-resolve +IP-CIDR,120.253.255.34/32,Proxy,no-resolve +IP-CIDR,120.253.255.98/32,Proxy,no-resolve +IP-CIDR,180.163.150.162/32,Proxy,no-resolve +IP-CIDR,180.163.150.34/32,Proxy,no-resolve +IP-CIDR,180.163.151.162/32,Proxy,no-resolve +IP-CIDR,180.163.151.34/32,Proxy,no-resolve +IP-CIDR,203.208.39.0/24,Proxy,no-resolve +IP-CIDR,203.208.40.0/24,Proxy,no-resolve +IP-CIDR,203.208.41.0/24,Proxy,no-resolve +IP-CIDR,203.208.43.0/24,Proxy,no-resolve +IP-CIDR,203.208.50.0/24,Proxy,no-resolve +IP-CIDR,220.181.174.162/32,Proxy,no-resolve +IP-CIDR,220.181.174.226/32,Proxy,no-resolve +IP-CIDR,220.181.174.34/32,Proxy,no-resolve + +RULE-SET,LAN,DIRECT + +# 剩余未匹配的国内网站 +DOMAIN-SUFFIX,cn,DIRECT +DOMAIN-KEYWORD,-cn,DIRECT + +# 最终规则 +GEOIP,CN,DIRECT +FINAL,Proxy,dns-failed + +[URL Rewrite] +^https?://(www.)?(g|google).cn https://www.google.com 302 diff --git a/Xboard/resources/sass/app.scss b/Xboard/resources/sass/app.scss new file mode 100644 index 0000000..8337712 --- /dev/null +++ b/Xboard/resources/sass/app.scss @@ -0,0 +1 @@ +// diff --git a/Xboard/resources/views/admin.blade.php b/Xboard/resources/views/admin.blade.php new file mode 100644 index 0000000..8733d36 --- /dev/null +++ b/Xboard/resources/views/admin.blade.php @@ -0,0 +1,86 @@ + + + + + + + {{ $title }} + + @php + $manifestPath = public_path('assets/admin/manifest.json'); + $manifest = file_exists($manifestPath) ? json_decode(file_get_contents($manifestPath), true) : null; + $entry = is_array($manifest) ? ($manifest['index.html'] ?? null) : null; + $scripts = []; + $styles = []; + $locales = []; + + if (is_array($entry)) { + $visited = []; + $collectAssets = function ($chunkName) use (&$collectAssets, &$manifest, &$visited, &$scripts, &$styles) { + if (isset($visited[$chunkName]) || !isset($manifest[$chunkName]) || !is_array($manifest[$chunkName])) { + return; + } + + $visited[$chunkName] = true; + $chunk = $manifest[$chunkName]; + + if (!empty($chunk['css']) && is_array($chunk['css'])) { + foreach ($chunk['css'] as $cssFile) { + $styles[$cssFile] = $cssFile; + } + } + + if (!empty($chunk['imports']) && is_array($chunk['imports'])) { + foreach ($chunk['imports'] as $import) { + $collectAssets($import); + } + } + + if (!empty($chunk['isEntry']) && !empty($chunk['file'])) { + $scripts[$chunk['file']] = $chunk['file']; + } + }; + + $collectAssets('index.html'); + } + + foreach (glob(public_path('assets/admin/locales/*.js')) ?: [] as $localeFile) { + $locales[] = 'locales/' . basename($localeFile); + } + sort($locales); + @endphp + + @if($entry && count($scripts) > 0) + @foreach($styles as $css) + + @endforeach + @foreach($locales as $locale) + + @endforeach + @foreach($scripts as $js) + + @endforeach + @else + {{-- Fallback: hardcoded paths for backward compatibility --}} + + + + + + + @endif + + + +
+ + + diff --git a/Xboard/resources/views/client/subscribe.blade.php b/Xboard/resources/views/client/subscribe.blade.php new file mode 100644 index 0000000..9895b77 --- /dev/null +++ b/Xboard/resources/views/client/subscribe.blade.php @@ -0,0 +1,296 @@ + + + + + + + {{ __('Subscribe') }} + + + + +
+

{{ __('User Information') }}

+ +
+
+
{{ __('Username') }}
+
{{ $username }}
+
+ +
+
{{ __('Status') }}
+
+ + {{ $status === 'active' ? __('Active') : __('Inactive') }} + +
+
+ +
+
{{ __('Data Used') }}
+
{{ $data_used }}
+
+ +
+
{{ __('Data Limit') }}
+
{{ $data_limit }}
+
+ +
+
{{ __('Expiration Date') }}
+
{{ $expired_date }}
+
+ + @if (isset($device_limit)) +
+
{{ __('Device Limit') }}
+
{{ $device_limit }} {{ __('Devices') }}
+
+ @endif + + @if ($reset_day) +
+
{{ __('Reset In') }}
+
{{ $reset_day }} {{ __('Days') }}
+
+ @endif +
+ + +
+ + + + + diff --git a/Xboard/resources/views/errors/500.blade.php b/Xboard/resources/views/errors/500.blade.php new file mode 100644 index 0000000..0cb0cd5 --- /dev/null +++ b/Xboard/resources/views/errors/500.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Server Error')) +@section('code', '500') +@section('message', __($exception->getMessage() ?: 'Server Error')) diff --git a/Xboard/resources/views/mail/classic/mailLogin.blade.php b/Xboard/resources/views/mail/classic/mailLogin.blade.php new file mode 100644 index 0000000..91f916b --- /dev/null +++ b/Xboard/resources/views/mail/classic/mailLogin.blade.php @@ -0,0 +1,195 @@ + + + + + + 邮箱验证码 + + + + + + + + + + +
+ +
+ + + + + + + +
+ {{$name}} +
+ + + + + + + + + + + + + + + + +
+ Dear Customer +
+ 您正在登入到{{$name}}, 请在 5 分钟内点击下方链接进行登入。如果您未授权该登入请求,请无视。 +
+ {{$link}} +
+ (本邮件由系统自动发出,请勿直接回复) +
+ 登录 {{$name}} +
+
+ +
+
+
+ + + diff --git a/Xboard/resources/views/mail/classic/notify.blade.php b/Xboard/resources/views/mail/classic/notify.blade.php new file mode 100644 index 0000000..61cba96 --- /dev/null +++ b/Xboard/resources/views/mail/classic/notify.blade.php @@ -0,0 +1,187 @@ + + + + + + 网站通知 + + + + + + + + + + +
+ +
+ + + + + + + +
+ 网站通知 +
+ + + + + + + + + + + + + +
+ Dear Customer +
+ {!! nl2br($content) !!} +
+ (本邮件由系统自动发出,请勿直接回复) +
+ 登录 {{$name}} +
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/Xboard/resources/views/mail/classic/remindExpire.blade.php b/Xboard/resources/views/mail/classic/remindExpire.blade.php new file mode 100644 index 0000000..0c8dfec --- /dev/null +++ b/Xboard/resources/views/mail/classic/remindExpire.blade.php @@ -0,0 +1,187 @@ + + + + + + 到期提示 + + + + + + + + + + +
+ +
+ + + + + + + +
+ 到期提示 +
+ + + + + + + + + + + + + +
+ Dear Customer +
+ 您的订阅套餐将于 24 小时后到期,请及时续费 +
+ (本邮件由系统自动发出,请勿直接回复) +
+ 登录 {{$name}} +
+
+ +
+
+
+ + + diff --git a/Xboard/resources/views/mail/classic/remindTraffic.blade.php b/Xboard/resources/views/mail/classic/remindTraffic.blade.php new file mode 100644 index 0000000..786f206 --- /dev/null +++ b/Xboard/resources/views/mail/classic/remindTraffic.blade.php @@ -0,0 +1,187 @@ + + + + + + 流量提示 + + + + + + + + + + +
+ +
+ + + + + + + +
+ 流量提示 +
+ + + + + + + + + + + + + +
+ Dear Customer +
+ 您本月的套餐流量已使用 80%,请合理安排使用,避免提前耗尽 +
+ (本邮件由系统自动发出,请勿直接回复) +
+ 登录 {{$name}} +
+
+ +
+
+
+ + + diff --git a/Xboard/resources/views/mail/classic/verify.blade.php b/Xboard/resources/views/mail/classic/verify.blade.php new file mode 100644 index 0000000..ac1bd6e --- /dev/null +++ b/Xboard/resources/views/mail/classic/verify.blade.php @@ -0,0 +1,195 @@ + + + + + + 邮箱验证码 + + + + + + + + + + +
+ +
+ + + + + + + +
+ 邮箱验证码 +
+ + + + + + + + + + + + + + + + +
+ Dear Customer +
+ 请填写以下验证码完成邮箱验证 (5分钟内有效) +
+ {{$code}} +
+ (本邮件由系统自动发出,请勿直接回复) +
+ 登录 {{$name}} +
+
+ +
+
+
+ + + diff --git a/Xboard/resources/views/mail/default/mailLogin.blade.php b/Xboard/resources/views/mail/default/mailLogin.blade.php new file mode 100644 index 0000000..5047acd --- /dev/null +++ b/Xboard/resources/views/mail/default/mailLogin.blade.php @@ -0,0 +1,43 @@ +
+ + + + + + +
+
+ + + + + + + + + + + + + + + + +
{{$name}}
登入到{{$name}}
+ 尊敬的用户您好! +
+
+ 您正在登入到{{$name}}, 请在 5 分钟内点击下方链接进行登入。如果您未授权该登入请求,请无视。 + {{$link}} +
+
+
+ + + + + + +
返回{{$name}}
+
+
diff --git a/Xboard/resources/views/mail/default/notify.blade.php b/Xboard/resources/views/mail/default/notify.blade.php new file mode 100644 index 0000000..0b38ee5 --- /dev/null +++ b/Xboard/resources/views/mail/default/notify.blade.php @@ -0,0 +1,42 @@ +
+ + + + + + +
+
+ + + + + + + + + + + + + + + + +
{{$name}}
网站通知
+ 尊敬的用户您好! +
+
+ {!! nl2br($content) !!} +
+
+
+ + + + + + +
返回{{$name}}
+
+
diff --git a/Xboard/resources/views/mail/default/remindExpire.blade.php b/Xboard/resources/views/mail/default/remindExpire.blade.php new file mode 100644 index 0000000..e858e28 --- /dev/null +++ b/Xboard/resources/views/mail/default/remindExpire.blade.php @@ -0,0 +1,42 @@ +
+ + + + + + +
+
+ + + + + + + + + + + + + + + + +
{{$name}}
到期通知
+ 尊敬的用户您好! +
+
+ 你的服务将在24小时内到期。为了不造成使用上的影响请尽快续费。如果你已续费请忽略此邮件。 +
+
+
+ + + + + + +
返回{{$name}}
+
+
diff --git a/Xboard/resources/views/mail/default/remindTraffic.blade.php b/Xboard/resources/views/mail/default/remindTraffic.blade.php new file mode 100644 index 0000000..022f4e9 --- /dev/null +++ b/Xboard/resources/views/mail/default/remindTraffic.blade.php @@ -0,0 +1,42 @@ +
+ + + + + + +
+
+ + + + + + + + + + + + + + + + +
{{$name}}
流量通知
+ 尊敬的用户您好! +
+
+ 你的流量已经使用80%。为了不造成使用上的影响请合理安排流量的使用。 +
+
+
+ + + + + + +
返回{{$name}}
+
+
diff --git a/Xboard/resources/views/mail/default/verify.blade.php b/Xboard/resources/views/mail/default/verify.blade.php new file mode 100644 index 0000000..5c5b396 --- /dev/null +++ b/Xboard/resources/views/mail/default/verify.blade.php @@ -0,0 +1,42 @@ +
+ + + + + + +
+
+ + + + + + + + + + + + + + + + +
{{$name}}
邮箱验证码
+ 尊敬的用户您好! +
+
+ 您的验证码是:{{$code}},请在 5 分钟内进行验证。如果该验证码不为您本人申请,请无视。 +
+
+
+ + + + + + +
返回{{$name}}
+
+
diff --git a/Xboard/routes/channels.php b/Xboard/routes/channels.php new file mode 100644 index 0000000..12c1b91 --- /dev/null +++ b/Xboard/routes/channels.php @@ -0,0 +1,19 @@ +id === (int)$id; +}); diff --git a/Xboard/routes/console.php b/Xboard/routes/console.php new file mode 100644 index 0000000..da55196 --- /dev/null +++ b/Xboard/routes/console.php @@ -0,0 +1,19 @@ +comment(Inspiring::quote()); +})->describe('Display an inspiring quote'); diff --git a/Xboard/routes/web.php b/Xboard/routes/web.php new file mode 100644 index 0000000..abdc1e9 --- /dev/null +++ b/Xboard/routes/web.php @@ -0,0 +1,92 @@ +getHost(); + $configHost = parse_url(admin_setting('app_url'), PHP_URL_HOST); + + if ($requestHost !== $configHost) { + abort(403); + } + } + + $theme = admin_setting('frontend_theme', 'Xboard'); + $themeService = new ThemeService(); + + try { + if (!$themeService->exists($theme)) { + if ($theme !== 'Xboard') { + Log::warning('Theme not found, switching to default theme', ['theme' => $theme]); + $theme = 'Xboard'; + admin_setting(['frontend_theme' => $theme]); + } + $themeService->switch($theme); + } + + if (!$themeService->getThemeViewPath($theme)) { + throw new Exception('主题视图文件不存在'); + } + + $publicThemePath = public_path('theme/' . $theme); + if (!File::exists($publicThemePath)) { + $themePath = $themeService->getThemePath($theme); + if (!$themePath || !File::copyDirectory($themePath, $publicThemePath)) { + throw new Exception('主题初始化失败'); + } + Log::info('Theme initialized in public directory', ['theme' => $theme]); + } + + $renderParams = [ + 'title' => admin_setting('app_name', 'Xboard'), + 'theme' => $theme, + 'version' => app(UpdateService::class)->getCurrentVersion(), + 'description' => admin_setting('app_description', 'Xboard is best'), + 'logo' => admin_setting('logo'), + 'theme_config' => $themeService->getConfig($theme) + ]; + return view('theme::' . $theme . '.dashboard', $renderParams); + } catch (Exception $e) { + Log::error('Theme rendering failed', [ + 'theme' => $theme, + 'error' => $e->getMessage() + ]); + abort(500, '主题加载失败'); + } +}); + +//TODO:: 兼容 +Route::get('/' . admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))), function () { + return view('admin', [ + 'title' => admin_setting('app_name', 'XBoard'), + 'theme_sidebar' => admin_setting('frontend_theme_sidebar', 'light'), + 'theme_header' => admin_setting('frontend_theme_header', 'dark'), + 'theme_color' => admin_setting('frontend_theme_color', 'default'), + 'background_url' => admin_setting('frontend_background_url'), + 'version' => app(UpdateService::class)->getCurrentVersion(), + 'logo' => admin_setting('logo'), + 'secure_path' => admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))) + ]); +}); + +Route::get('/' . (admin_setting('subscribe_path', 's')) . '/{token}', [\App\Http\Controllers\V1\Client\ClientController::class, 'subscribe']) + ->middleware('client') + ->name('client.subscribe'); \ No newline at end of file diff --git a/Xboard/storage/backup/.gitignore b/Xboard/storage/backup/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/Xboard/storage/backup/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/Xboard/storage/debugbar/.gitignore b/Xboard/storage/debugbar/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/Xboard/storage/debugbar/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Xboard/storage/framework/cache/.gitignore b/Xboard/storage/framework/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/Xboard/storage/framework/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Xboard/storage/framework/sessions/.gitignore b/Xboard/storage/framework/sessions/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/Xboard/storage/framework/sessions/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Xboard/storage/framework/views/.gitignore b/Xboard/storage/framework/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/Xboard/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Xboard/storage/logs/.gitignore b/Xboard/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/Xboard/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Xboard/storage/theme/.gitignore b/Xboard/storage/theme/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/Xboard/storage/theme/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/Xboard/storage/tmp/.gitignore b/Xboard/storage/tmp/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/Xboard/storage/tmp/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/Xboard/storage/views/.gitignore b/Xboard/storage/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/Xboard/storage/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Xboard/theme/.gitignore b/Xboard/theme/.gitignore new file mode 100644 index 0000000..17697d3 --- /dev/null +++ b/Xboard/theme/.gitignore @@ -0,0 +1,4 @@ +/* +!v2board +!Xboard +!.gitignore diff --git a/Xboard/theme/Xboard/assets/images/background.svg b/Xboard/theme/Xboard/assets/images/background.svg new file mode 100644 index 0000000..e466997 --- /dev/null +++ b/Xboard/theme/Xboard/assets/images/background.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Xboard/theme/Xboard/assets/umi.js b/Xboard/theme/Xboard/assets/umi.js new file mode 100644 index 0000000..391d5f0 --- /dev/null +++ b/Xboard/theme/Xboard/assets/umi.js @@ -0,0 +1,2 @@ +!function(){"use strict";try{if("undefined"!=typeof document){var o=document.createElement("style");o.appendChild(document.createTextNode('@charset "UTF-8";html.dark .markdown-body{color-scheme:dark;--color-prettylights-syntax-comment: #8b949e;--color-prettylights-syntax-constant: #79c0ff;--color-prettylights-syntax-entity: #d2a8ff;--color-prettylights-syntax-storage-modifier-import: #c9d1d9;--color-prettylights-syntax-entity-tag: #7ee787;--color-prettylights-syntax-keyword: #ff7b72;--color-prettylights-syntax-string: #a5d6ff;--color-prettylights-syntax-variable: #ffa657;--color-prettylights-syntax-brackethighlighter-unmatched: #f85149;--color-prettylights-syntax-invalid-illegal-text: #f0f6fc;--color-prettylights-syntax-invalid-illegal-bg: #8e1519;--color-prettylights-syntax-carriage-return-text: #f0f6fc;--color-prettylights-syntax-carriage-return-bg: #b62324;--color-prettylights-syntax-string-regexp: #7ee787;--color-prettylights-syntax-markup-list: #f2cc60;--color-prettylights-syntax-markup-heading: #1f6feb;--color-prettylights-syntax-markup-italic: #c9d1d9;--color-prettylights-syntax-markup-bold: #c9d1d9;--color-prettylights-syntax-markup-deleted-text: #ffdcd7;--color-prettylights-syntax-markup-deleted-bg: #67060c;--color-prettylights-syntax-markup-inserted-text: #aff5b4;--color-prettylights-syntax-markup-inserted-bg: #033a16;--color-prettylights-syntax-markup-changed-text: #ffdfb6;--color-prettylights-syntax-markup-changed-bg: #5a1e02;--color-prettylights-syntax-markup-ignored-text: #c9d1d9;--color-prettylights-syntax-markup-ignored-bg: #1158c7;--color-prettylights-syntax-meta-diff-range: #d2a8ff;--color-prettylights-syntax-brackethighlighter-angle: #8b949e;--color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;--color-prettylights-syntax-constant-other-reference-link: #a5d6ff;--color-fg-default: #e6edf3;--color-fg-muted: #7d8590;--color-fg-subtle: #6e7681;--color-canvas-default: #0d1117;--color-canvas-subtle: #161b22;--color-border-default: #30363d;--color-border-muted: #21262d;--color-neutral-muted: rgba(110,118,129,.4);--color-accent-fg: #2f81f7;--color-accent-emphasis: #1f6feb;--color-attention-fg: #d29922;--color-attention-subtle: rgba(187,128,9,.15);--color-danger-fg: #f85149;--color-done-fg: #a371f7}html:not(.dark) .markdown-body{color-scheme:light;--color-prettylights-syntax-comment: #6e7781;--color-prettylights-syntax-constant: #0550ae;--color-prettylights-syntax-entity: #6639ba;--color-prettylights-syntax-storage-modifier-import: #24292f;--color-prettylights-syntax-entity-tag: #116329;--color-prettylights-syntax-keyword: #cf222e;--color-prettylights-syntax-string: #0a3069;--color-prettylights-syntax-variable: #953800;--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;--color-prettylights-syntax-invalid-illegal-bg: #82071e;--color-prettylights-syntax-carriage-return-text: #f6f8fa;--color-prettylights-syntax-carriage-return-bg: #cf222e;--color-prettylights-syntax-string-regexp: #116329;--color-prettylights-syntax-markup-list: #3b2300;--color-prettylights-syntax-markup-heading: #0550ae;--color-prettylights-syntax-markup-italic: #24292f;--color-prettylights-syntax-markup-bold: #24292f;--color-prettylights-syntax-markup-deleted-text: #82071e;--color-prettylights-syntax-markup-deleted-bg: #ffebe9;--color-prettylights-syntax-markup-inserted-text: #116329;--color-prettylights-syntax-markup-inserted-bg: #dafbe1;--color-prettylights-syntax-markup-changed-text: #953800;--color-prettylights-syntax-markup-changed-bg: #ffd8b5;--color-prettylights-syntax-markup-ignored-text: #eaeef2;--color-prettylights-syntax-markup-ignored-bg: #0550ae;--color-prettylights-syntax-meta-diff-range: #8250df;--color-prettylights-syntax-brackethighlighter-angle: #57606a;--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;--color-prettylights-syntax-constant-other-reference-link: #0a3069;--color-fg-default: #1F2328;--color-fg-muted: #656d76;--color-fg-subtle: #6e7781;--color-canvas-default: #ffffff;--color-canvas-subtle: #f6f8fa;--color-border-default: #d0d7de;--color-border-muted: hsla(210,18%,87%,1);--color-neutral-muted: rgba(175,184,193,.2);--color-accent-fg: #0969da;--color-accent-emphasis: #0969da;--color-attention-fg: #9a6700;--color-attention-subtle: #fff8c5;--color-danger-fg: #d1242f;--color-done-fg: #8250df}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;margin:0;color:var(--color-fg-default);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom}.markdown-body h1:hover .anchor .octicon-link:before,.markdown-body h2:hover .anchor .octicon-link:before,.markdown-body h3:hover .anchor .octicon-link:before,.markdown-body h4:hover .anchor .octicon-link:before,.markdown-body h5:hover .anchor .octicon-link:before,.markdown-body h6:hover .anchor .octicon-link:before{width:16px;height:16px;content:" ";display:inline-block;background-color:currentColor;-webkit-mask-image:url("data:image/svg+xml,");mask-image:url("data:image/svg+xml,")}.markdown-body details,.markdown-body figcaption,.markdown-body figure{display:block}.markdown-body summary{display:list-item}.markdown-body [hidden]{display:none!important}.markdown-body a{background-color:transparent;color:var(--color-accent-fg);text-decoration:none}.markdown-body abbr[title]{border-bottom:none;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-body b,.markdown-body strong{font-weight:var(--base-text-weight-semibold, 600)}.markdown-body dfn{font-style:italic}.markdown-body h1{margin:.67em 0;font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:2em;border-bottom:1px solid var(--color-border-muted)}.markdown-body mark{background-color:var(--color-attention-subtle);color:var(--color-fg-default)}.markdown-body small{font-size:90%}.markdown-body sub,.markdown-body sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}.markdown-body sub{bottom:-.25em}.markdown-body sup{top:-.5em}.markdown-body img{border-style:none;max-width:100%;box-sizing:content-box;background-color:var(--color-canvas-default)}.markdown-body code,.markdown-body kbd,.markdown-body pre,.markdown-body samp{font-family:monospace;font-size:1em}.markdown-body figure{margin:1em 40px}.markdown-body hr{box-sizing:content-box;overflow:hidden;background:transparent;border-bottom:1px solid var(--color-border-muted);height:.25em;padding:0;margin:24px 0;background-color:var(--color-border-default);border:0}.markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit}.markdown-body [type=button],.markdown-body [type=reset],.markdown-body [type=submit]{-webkit-appearance:button}.markdown-body [type=checkbox],.markdown-body [type=radio]{box-sizing:border-box;padding:0}.markdown-body [type=number]::-webkit-inner-spin-button,.markdown-body [type=number]::-webkit-outer-spin-button{height:auto}.markdown-body [type=search]::-webkit-search-cancel-button,.markdown-body [type=search]::-webkit-search-decoration{-webkit-appearance:none}.markdown-body ::-webkit-input-placeholder{color:inherit;opacity:.54}.markdown-body ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.markdown-body a:hover{text-decoration:underline}.markdown-body ::placeholder{color:var(--color-fg-subtle);opacity:1}.markdown-body hr:before{display:table;content:""}.markdown-body hr:after{display:table;clear:both;content:""}.markdown-body table{border-spacing:0;border-collapse:collapse;display:block;width:max-content;max-width:100%;overflow:auto}.markdown-body td,.markdown-body th{padding:0}.markdown-body details summary{cursor:pointer}.markdown-body details:not([open])>*:not(summary){display:none!important}.markdown-body a:focus,.markdown-body [role=button]:focus,.markdown-body input[type=radio]:focus,.markdown-body input[type=checkbox]:focus{outline:2px solid var(--color-accent-fg);outline-offset:-2px;box-shadow:none}.markdown-body a:focus:not(:focus-visible),.markdown-body [role=button]:focus:not(:focus-visible),.markdown-body input[type=radio]:focus:not(:focus-visible),.markdown-body input[type=checkbox]:focus:not(:focus-visible){outline:solid 1px transparent}.markdown-body a:focus-visible,.markdown-body [role=button]:focus-visible,.markdown-body input[type=radio]:focus-visible,.markdown-body input[type=checkbox]:focus-visible{outline:2px solid var(--color-accent-fg);outline-offset:-2px;box-shadow:none}.markdown-body a:not([class]):focus,.markdown-body a:not([class]):focus-visible,.markdown-body input[type=radio]:focus,.markdown-body input[type=radio]:focus-visible,.markdown-body input[type=checkbox]:focus,.markdown-body input[type=checkbox]:focus-visible{outline-offset:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font:11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;line-height:10px;color:var(--color-fg-default);vertical-align:middle;background-color:var(--color-canvas-subtle);border:solid 1px var(--color-neutral-muted);border-bottom-color:var(--color-neutral-muted);border-radius:6px;box-shadow:inset 0 -1px 0 var(--color-neutral-muted)}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:var(--base-text-weight-semibold, 600);line-height:1.25}.markdown-body h2{font-weight:var(--base-text-weight-semibold, 600);padding-bottom:.3em;font-size:1.5em;border-bottom:1px solid var(--color-border-muted)}.markdown-body h3{font-weight:var(--base-text-weight-semibold, 600);font-size:1.25em}.markdown-body h4{font-weight:var(--base-text-weight-semibold, 600);font-size:1em}.markdown-body h5{font-weight:var(--base-text-weight-semibold, 600);font-size:.875em}.markdown-body h6{font-weight:var(--base-text-weight-semibold, 600);font-size:.85em;color:var(--color-fg-muted)}.markdown-body p{margin-top:0;margin-bottom:10px}.markdown-body blockquote{margin:0;padding:0 1em;color:var(--color-fg-muted);border-left:.25em solid var(--color-border-default)}.markdown-body ul,.markdown-body ol{margin-top:0;margin-bottom:0;padding-left:2em}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman}.markdown-body ul ul ol,.markdown-body ul ol ol,.markdown-body ol ul ol,.markdown-body ol ol ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body tt,.markdown-body code,.markdown-body samp{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px}.markdown-body pre{margin-top:0;margin-bottom:0;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:12px;word-wrap:normal}.markdown-body .octicon{display:inline-block;overflow:visible!important;vertical-align:text-bottom;fill:currentColor}.markdown-body input::-webkit-outer-spin-button,.markdown-body input::-webkit-inner-spin-button{margin:0;-webkit-appearance:none;appearance:none}.markdown-body .color-fg-accent{color:var(--color-accent-fg)!important}.markdown-body .color-fg-attention{color:var(--color-attention-fg)!important}.markdown-body .color-fg-done{color:var(--color-done-fg)!important}.markdown-body .flex-items-center{align-items:center!important}.markdown-body .mb-1{margin-bottom:var(--base-size-4, 4px)!important}.markdown-body .text-semibold{font-weight:var(--base-text-weight-medium, 500)!important}.markdown-body .d-inline-flex{display:inline-flex!important}.markdown-body:before{display:table;content:""}.markdown-body:after{display:table;clear:both;content:""}.markdown-body>*:first-child{margin-top:0!important}.markdown-body>*:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:var(--color-danger-fg)}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:none}.markdown-body p,.markdown-body blockquote,.markdown-body ul,.markdown-body ol,.markdown-body dl,.markdown-body table,.markdown-body pre,.markdown-body details{margin-top:0;margin-bottom:16px}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:var(--color-fg-default);vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 tt,.markdown-body h1 code,.markdown-body h2 tt,.markdown-body h2 code,.markdown-body h3 tt,.markdown-body h3 code,.markdown-body h4 tt,.markdown-body h4 code,.markdown-body h5 tt,.markdown-body h5 code,.markdown-body h6 tt,.markdown-body h6 code{padding:0 .2em;font-size:inherit}.markdown-body summary h1,.markdown-body summary h2,.markdown-body summary h3,.markdown-body summary h4,.markdown-body summary h5,.markdown-body summary h6{display:inline-block}.markdown-body summary h1 .anchor,.markdown-body summary h2 .anchor,.markdown-body summary h3 .anchor,.markdown-body summary h4 .anchor,.markdown-body summary h5 .anchor,.markdown-body summary h6 .anchor{margin-left:-40px}.markdown-body summary h1,.markdown-body summary h2{padding-bottom:0;border-bottom:0}.markdown-body ul.no-list,.markdown-body ol.no-list{padding:0;list-style-type:none}.markdown-body ol[type="a s"]{list-style-type:lower-alpha}.markdown-body ol[type="A s"]{list-style-type:upper-alpha}.markdown-body ol[type="i s"]{list-style-type:lower-roman}.markdown-body ol[type="I s"]{list-style-type:upper-roman}.markdown-body ol[type="1"]{list-style-type:decimal}.markdown-body div>ol:not([type]){list-style-type:decimal}.markdown-body ul ul,.markdown-body ul ol,.markdown-body ol ol,.markdown-body ol ul{margin-top:0;margin-bottom:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:var(--base-text-weight-semibold, 600)}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table th{font-weight:var(--base-text-weight-semibold, 600)}.markdown-body table th,.markdown-body table td{padding:6px 13px;border:1px solid var(--color-border-default)}.markdown-body table td>:last-child{margin-bottom:0}.markdown-body table tr{background-color:var(--color-canvas-default);border-top:1px solid var(--color-border-muted)}.markdown-body table tr:nth-child(2n){background-color:var(--color-canvas-subtle)}.markdown-body table img{background-color:transparent}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid var(--color-border-default)}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:var(--color-fg-default)}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:.2em .4em;margin:0;font-size:85%;white-space:break-spaces;background-color:var(--color-neutral-muted);border-radius:6px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body samp{font-size:85%}.markdown-body pre code{font-size:100%}.markdown-body pre>code{padding:0;margin:0;word-break:normal;white-space:pre;background:transparent;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;color:var(--color-fg-default);background-color:var(--color-canvas-subtle);border-radius:6px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:var(--color-canvas-default);border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:var(--base-text-weight-semibold, 600);background:var(--color-canvas-subtle);border-top:0}.markdown-body [data-footnote-ref]:before{content:"["}.markdown-body [data-footnote-ref]:after{content:"]"}.markdown-body .footnotes{font-size:12px;color:var(--color-fg-muted);border-top:1px solid var(--color-border-default)}.markdown-body .footnotes ol{padding-left:16px}.markdown-body .footnotes ol ul{display:inline-block;padding-left:16px;margin-top:16px}.markdown-body .footnotes li{position:relative}.markdown-body .footnotes li:target:before{position:absolute;inset:-8px -8px -8px -24px;pointer-events:none;content:"";border:2px solid var(--color-accent-emphasis);border-radius:6px}.markdown-body .footnotes li:target{color:var(--color-fg-default)}.markdown-body .footnotes .data-footnote-backref g-emoji{font-family:monospace}.markdown-body .pl-c{color:var(--color-prettylights-syntax-comment)}.markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:var(--color-prettylights-syntax-constant)}.markdown-body .pl-e,.markdown-body .pl-en{color:var(--color-prettylights-syntax-entity)}.markdown-body .pl-smi,.markdown-body .pl-s .pl-s1{color:var(--color-prettylights-syntax-storage-modifier-import)}.markdown-body .pl-ent{color:var(--color-prettylights-syntax-entity-tag)}.markdown-body .pl-k{color:var(--color-prettylights-syntax-keyword)}.markdown-body .pl-s,.markdown-body .pl-pds,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sre,.markdown-body .pl-sr .pl-sra{color:var(--color-prettylights-syntax-string)}.markdown-body .pl-v,.markdown-body .pl-smw{color:var(--color-prettylights-syntax-variable)}.markdown-body .pl-bu{color:var(--color-prettylights-syntax-brackethighlighter-unmatched)}.markdown-body .pl-ii{color:var(--color-prettylights-syntax-invalid-illegal-text);background-color:var(--color-prettylights-syntax-invalid-illegal-bg)}.markdown-body .pl-c2{color:var(--color-prettylights-syntax-carriage-return-text);background-color:var(--color-prettylights-syntax-carriage-return-bg)}.markdown-body .pl-sr .pl-cce{font-weight:700;color:var(--color-prettylights-syntax-string-regexp)}.markdown-body .pl-ml{color:var(--color-prettylights-syntax-markup-list)}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body .pl-ms{font-weight:700;color:var(--color-prettylights-syntax-markup-heading)}.markdown-body .pl-mi{font-style:italic;color:var(--color-prettylights-syntax-markup-italic)}.markdown-body .pl-mb{font-weight:700;color:var(--color-prettylights-syntax-markup-bold)}.markdown-body .pl-md{color:var(--color-prettylights-syntax-markup-deleted-text);background-color:var(--color-prettylights-syntax-markup-deleted-bg)}.markdown-body .pl-mi1{color:var(--color-prettylights-syntax-markup-inserted-text);background-color:var(--color-prettylights-syntax-markup-inserted-bg)}.markdown-body .pl-mc{color:var(--color-prettylights-syntax-markup-changed-text);background-color:var(--color-prettylights-syntax-markup-changed-bg)}.markdown-body .pl-mi2{color:var(--color-prettylights-syntax-markup-ignored-text);background-color:var(--color-prettylights-syntax-markup-ignored-bg)}.markdown-body .pl-mdr{font-weight:700;color:var(--color-prettylights-syntax-meta-diff-range)}.markdown-body .pl-ba{color:var(--color-prettylights-syntax-brackethighlighter-angle)}.markdown-body .pl-sg{color:var(--color-prettylights-syntax-sublimelinter-gutter-mark)}.markdown-body .pl-corl{text-decoration:underline;color:var(--color-prettylights-syntax-constant-other-reference-link)}.markdown-body g-emoji{display:inline-block;min-width:1ch;font-family:"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol;font-size:1em;font-style:normal!important;font-weight:var(--base-text-weight-normal, 400);line-height:1;vertical-align:-.075em}.markdown-body g-emoji img{width:1em;height:1em}.markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item label{font-weight:var(--base-text-weight-normal, 400)}.markdown-body .task-list-item.enabled label{cursor:pointer}.markdown-body .task-list-item+.task-list-item{margin-top:4px}.markdown-body .task-list-item .handle{display:none}.markdown-body .task-list-item-checkbox{margin:0 .2em .25em -1.4em;vertical-align:middle}.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox{margin:0 -1.6em .25em .2em}.markdown-body .contains-task-list{position:relative}.markdown-body .contains-task-list:hover .task-list-item-convert-container,.markdown-body .contains-task-list:focus-within .task-list-item-convert-container{display:block;width:auto;height:24px;overflow:visible;clip:auto}.markdown-body .QueryBuilder .qb-entity{color:var(--color-prettylights-syntax-entity)}.markdown-body .QueryBuilder .qb-constant{color:var(--color-prettylights-syntax-constant)}.markdown-body ::-webkit-calendar-picker-indicator{filter:invert(50%)}.markdown-body .markdown-alert{padding:0 1em;margin-bottom:16px;color:inherit;border-left:.25em solid var(--color-border-default)}.markdown-body .markdown-alert>:first-child{margin-top:0}.markdown-body .markdown-alert>:last-child{margin-bottom:0}.markdown-body .markdown-alert.markdown-alert-note{border-left-color:var(--color-accent-fg)}.markdown-body .markdown-alert.markdown-alert-important{border-left-color:var(--color-done-fg)}.markdown-body .markdown-alert.markdown-alert-warning{border-left-color:var(--color-attention-fg)}*,:before,:after{box-sizing:border-box;background-repeat:no-repeat}:before,:after{text-decoration:inherit;vertical-align:inherit}:where(:root){cursor:default;line-height:1.5;overflow-wrap:break-word;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%}:where(body){margin:0}:where(h1){font-size:2em;margin:.67em 0}:where(dl,ol,ul) :where(dl,ol,ul){margin:0}:where(hr){color:inherit;height:0}:where(nav) :where(ol,ul){list-style-type:none;padding:0}:where(nav li):before{content:"​";float:left}:where(pre){font-family:monospace,monospace;font-size:1em;overflow:auto}:where(abbr[title]){text-decoration:underline;text-decoration:underline dotted}:where(b,strong){font-weight:bolder}:where(code,kbd,samp){font-family:monospace,monospace;font-size:1em}:where(small){font-size:80%}:where(audio,canvas,iframe,img,svg,video){vertical-align:baseline}:where(iframe){border-style:none}:where(svg:not([fill])){fill:currentColor}:where(table){border-collapse:collapse;border-color:inherit;text-indent:0}:where(button,input,select){margin:0}:where(button,[type=button i],[type=reset i],[type=submit i]){-webkit-appearance:button}:where(fieldset){border:1px solid #a0a0a0}:where(progress){vertical-align:baseline}:where(textarea){margin:0;resize:vertical}:where([type=search i]){-webkit-appearance:textfield;outline-offset:-2px}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}:where(dialog){background-color:#fff;border:solid;color:#000;height:-moz-fit-content;height:fit-content;left:0;margin:auto;padding:1em;position:absolute;right:0;width:-moz-fit-content;width:fit-content}:where(dialog:not([open])){display:none}:where(details>summary:first-of-type){display:list-item}:where([aria-busy=true i]){cursor:progress}:where([aria-controls]){cursor:pointer}:where([aria-disabled=true i],[disabled]){cursor:not-allowed}:where([aria-hidden=false i][hidden]){display:initial}:where([aria-hidden=false i][hidden]:not(:focus)){clip:rect(0,0,0,0);position:absolute}html{font-size:16px}html,body{width:100%;height:100%;overflow:hidden;background-color:#f2f2f2;font-family:Encode Sans Condensed,sans-serif}html.dark body{background-color:#292b2b}::-webkit-scrollbar{width:8px;background-color:#eee}::-webkit-scrollbar-thumb{background-color:#c1c1c1}::-webkit-scrollbar-thumb:hover{background-color:#a8a8a8}html,body{width:100%;height:100%;overflow:hidden;font-size:16px}#app{width:100%;height:100%}.fade-slide-leave-active,.fade-slide-enter-active{transition:all .3s}.fade-slide-enter-from{opacity:0;transform:translate(-30px)}.fade-slide-leave-to{opacity:0;transform:translate(30px)}.cus-scroll{overflow:auto}.cus-scroll::-webkit-scrollbar{width:8px;height:8px}.cus-scroll-x{overflow-x:auto}.cus-scroll-x::-webkit-scrollbar{width:0;height:8px}.cus-scroll-y{overflow-y:auto}.cus-scroll-y::-webkit-scrollbar{width:8px;height:0}.cus-scroll::-webkit-scrollbar-thumb,.cus-scroll-x::-webkit-scrollbar-thumb,.cus-scroll-y::-webkit-scrollbar-thumb{background-color:transparent;border-radius:4px}.cus-scroll:hover::-webkit-scrollbar-thumb,.cus-scroll-x:hover::-webkit-scrollbar-thumb,.cus-scroll-y:hover::-webkit-scrollbar-thumb{background:#bfbfbf}.cus-scroll:hover::-webkit-scrollbar-thumb:hover,.cus-scroll-x:hover::-webkit-scrollbar-thumb:hover,.cus-scroll-y:hover::-webkit-scrollbar-thumb:hover{background:var(--primary-color)}#--unocss--{layer:__ALL__}#app{height:100%}#app .n-config-provider{height:inherit}@media (max-width: 768px){.title-text[data-v-143fb1b0]{max-width:100px}}.side-menu:not(.n-menu--collapsed) .n-menu-item-content:before{left:5px;right:5px}.side-menu:not(.n-menu--collapsed) .n-menu-item-content.n-menu-item-content--selected:before,.side-menu:not(.n-menu--collapsed) .n-menu-item-content:hover:before{border-left:4px solid var(--primary-color)}.carousel-img[data-v-2492d831]{width:100%;height:240px;object-fit:cover}.pay-qrcode{width:100%;height:100%}.pay-qrcode>canvas{width:100%!important;height:100%!important}.card-container[data-v-16d7c058]{display:grid;justify-content:space-between;grid-template-columns:repeat(auto-fit,minmax(calc(100% - 1rem),1fr));row-gap:20px;min-width:100%}.card-item[data-v-16d7c058]{max-width:100%}@media screen and (min-width: 768px){.card-container[data-v-16d7c058]{grid-template-columns:repeat(auto-fit,minmax(calc(50% - 1rem),1fr));column-gap:20px;min-width:375px}}@media screen and (min-width: 1200px){.card-container[data-v-16d7c058]{grid-template-columns:repeat(auto-fit,minmax(calc(33.33% - 1rem),1fr));padding:0 10px;column-gap:20px;min-width:375px}}#--unocss-layer-start--__ALL__--{start:__ALL__}*,:before,:after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / .5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / .5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }.container{width:100%}.wh-full,[wh-full=""]{width:100%;height:100%}.f-c-c,[f-c-c=""]{display:flex;align-items:center;justify-content:center}.flex-col,[flex-col=""]{display:flex;flex-direction:column}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.right-0{right:0}.right-4{right:16px}[bottom~="20"]{bottom:80px}.z-99999{z-index:99999}.grid{display:grid}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.m-0{margin:0}.m-0\\!{margin:0!important}.m-3{margin:12px}.m-auto,[m-auto=""]{margin:auto}.mx-2{margin-left:8px;margin-right:8px}.mx-2\\.5{margin-left:10px;margin-right:10px}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:4px}.mb-1em{margin-bottom:1em}.mb-2{margin-bottom:8px}.mb-2\\.5{margin-bottom:10px}.mb-3{margin-bottom:12px}.mb-4{margin-bottom:16px}.mb-5{margin-bottom:20px}.ml-1{margin-left:4px}.ml-2\\.5{margin-left:10px}.ml-5{margin-left:20px}.ml-auto,[ml-auto=""]{margin-left:auto}.mr-0{margin-right:0}.mr-1\\.2{margin-right:4.8px}.mr-1\\.3{margin-right:5.2px}.mr-5{margin-right:20px}.mr-auto{margin-right:auto}.mr10,[mr10=""]{margin-right:40px}.mt-1{margin-top:4px}.mt-15,[mt-15=""]{margin-top:60px}.mt-2{margin-top:8px}.mt-2\\.5{margin-top:10px}.mt-4{margin-top:16px}.mt-5,[mt-5=""]{margin-top:20px}.mt-8{margin-top:32px}.inline-block{display:inline-block}.hidden{display:none}.h-1\\.5{height:6px}.h-10{height:40px}.h-15{height:60px}.h-35,[h-35=""]{height:140px}.h-5,.h5{height:20px}.h-60,[h-60=""]{height:240px}.h-8{height:32px}.h-9{height:36px}.h-auto{height:auto}.h-full,[h-full=""]{height:100%}.h-full\\!{height:100%!important}.h-px{height:1px}.h1{height:4px}.h2{height:8px}.h3{height:12px}.max-h-8{max-height:32px}.max-w-1200{max-width:4800px}.max-w-125{max-width:500px}.max-w-full{max-width:100%}.max-w-md{max-width:448px}.min-w-75{min-width:300px}.w-1\\.5{width:6px}.w-150{width:600px}.w-16{width:64px}.w-35,[w-35=""]{width:140px}.w-375{width:1500px}.w-5{width:20px}.w-75{width:300px}.w-8{width:32px}.w-auto{width:auto}.w-full{width:100%}.w-full\\!{width:100%!important}.flex,[flex=""]{display:flex}.flex-\\[1\\]{flex:1}.flex-\\[2\\]{flex:2}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-wrap{flex-wrap:wrap}[transform-origin~=center]{transform-origin:center}.transform{transform:translate(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotate(var(--un-rotate-z)) skew(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z))}.cursor-pointer,[cursor-pointer=""]{cursor:pointer}.resize{resize:both}.items-center,[items-center=""]{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-3{gap:12px}.gap-5{gap:20px}.space-x-4>:not([hidden])~:not([hidden]){--un-space-x-reverse:0;margin-left:calc(16px * calc(1 - var(--un-space-x-reverse)));margin-right:calc(16px * var(--un-space-x-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--un-space-y-reverse:0;margin-top:calc(16px * calc(1 - var(--un-space-y-reverse)));margin-bottom:calc(16px * var(--un-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--un-space-y-reverse:0;margin-top:calc(20px * calc(1 - var(--un-space-y-reverse)));margin-bottom:calc(20px * var(--un-space-y-reverse))}.overflow-hidden{overflow:hidden}.whitespace-nowrap{white-space:nowrap}.break-anywhere{overflow-wrap:anywhere}.b{border-width:1px}.border-0,.dark [dark~=border-0]{border-width:0px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-\\[\\#646669\\]{--un-border-opacity:1;border-color:rgb(100 102 105 / var(--un-border-opacity))}.border-gray-600{--un-border-opacity:1;border-color:rgb(75 85 99 / var(--un-border-opacity))}.border-primary{border-color:var(--primary-color)}.border-transparent{border-color:transparent}.rounded-full,[rounded-full=""]{border-radius:9999px}.rounded-lg{border-radius:8px}.rounded-md{border-radius:6px}.border-none{border-style:none}.border-solid{border-style:solid}.bg-\\[--n-color-embedded\\]{background-color:var(--n-color-embedded)}.bg-\\[--n-color\\]{background-color:var(--n-color)}.bg-\\[\\#f5f6fb\\],.bg-hex-f5f6fb{--un-bg-opacity:1;background-color:rgb(245 246 251 / var(--un-bg-opacity))}.bg-blue-500{--un-bg-opacity:1;background-color:rgb(59 130 246 / var(--un-bg-opacity))}.bg-dark,.dark [dark~=bg-dark]{--un-bg-opacity:1;background-color:rgb(24 24 28 / var(--un-bg-opacity))}.bg-gray-300{--un-bg-opacity:1;background-color:rgb(209 213 219 / var(--un-bg-opacity))}.bg-gray-50{--un-bg-opacity:1;background-color:rgb(249 250 251 / var(--un-bg-opacity))}.bg-gray-800{--un-bg-opacity:1;background-color:rgb(31 41 55 / var(--un-bg-opacity))}.bg-green-500{--un-bg-opacity:1;background-color:rgb(34 197 94 / var(--un-bg-opacity))}.bg-orange-600{--un-bg-opacity:1;background-color:rgb(234 88 12 / var(--un-bg-opacity))}.bg-red-500{--un-bg-opacity:1;background-color:rgb(239 68 68 / var(--un-bg-opacity))}.bg-transparent{background-color:transparent}.bg-white{--un-bg-opacity:1;background-color:rgb(255 255 255 / var(--un-bg-opacity))}.dark .dark\\:bg-hex-101014{--un-bg-opacity:1;background-color:rgb(16 16 20 / var(--un-bg-opacity))}.dark .dark\\:bg-hex-121212{--un-bg-opacity:1;background-color:rgb(18 18 18 / var(--un-bg-opacity))}.dark .dark\\:bg-primary\\/20,.dark .dark\\:hover\\:bg-primary\\/20:hover{background-color:var(--primary-color)}.hover\\:bg-gray-100:hover{--un-bg-opacity:1;background-color:rgb(243 244 246 / var(--un-bg-opacity))}.p-0{padding:0}.p-0\\!{padding:0!important}.p-0\\.5{padding:2px}.p-1{padding:4px}.p-2\\.5{padding:10px}.p-5{padding:20px}.p-6{padding:24px}.px,.px-4{padding-left:16px;padding-right:16px}.px-3{padding-left:12px;padding-right:12px}.px-6{padding-left:24px;padding-right:24px}.py-2{padding-top:8px;padding-bottom:8px}.py-3{padding-top:12px;padding-bottom:12px}.py-4{padding-top:16px;padding-bottom:16px}.pb-1{padding-bottom:4px}.pb-2\\.5{padding-bottom:10px}.pb-4{padding-bottom:16px}.pb-8{padding-bottom:32px}.pl-1{padding-left:4px}.pl-4{padding-left:16px}.pr-4{padding-right:16px}.pt-1{padding-top:4px}.pt-2{padding-top:8px}.pt-2\\.5{padding-top:10px}.pt-4{padding-top:16px}.pt-5{padding-top:20px}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.indent{text-indent:24px}[indent~="0"]{text-indent:0}.root-indent:root{text-indent:24px}[root-indent~="18"]:root{text-indent:72px}.vertical-bottom{vertical-align:bottom}.text-14,[text-14=""]{font-size:56px}.text-3xl{font-size:30px;line-height:36px}.text-4xl{font-size:36px;line-height:40px}.text-5xl{font-size:48px;line-height:1}.text-9xl{font-size:128px;line-height:1}.text-base{font-size:16px;line-height:24px}.text-lg{font-size:18px;line-height:28px}.text-sm{font-size:14px;line-height:20px}.text-xl{font-size:20px;line-height:28px}.text-xs{font-size:12px;line-height:16px}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.color-\\[hsla\\(0\\,0\\%\\,100\\%\\,\\.75\\)\\]{--un-text-opacity:.75;color:hsla(0,0%,100%,var(--un-text-opacity))}.color-\\#48bc19{--un-text-opacity:1;color:rgb(72 188 25 / var(--un-text-opacity))}.color-\\#f8f9fa{--un-text-opacity:1;color:rgb(248 249 250 / var(--un-text-opacity))}.color-\\#f8f9fa41{--un-text-opacity:.25;color:rgb(248 249 250 / var(--un-text-opacity))}.color-\\#f9a314{--un-text-opacity:1;color:rgb(249 163 20 / var(--un-text-opacity))}.color-primary,.text-\\[--primary-color\\]{color:var(--primary-color)}[color~="#343a40"]{--un-text-opacity:1;color:rgb(52 58 64 / var(--un-text-opacity))}[color~="#6a6a6a"]{--un-text-opacity:1;color:rgb(106 106 106 / var(--un-text-opacity))}[color~="#6c757d"]{--un-text-opacity:1;color:rgb(108 117 125 / var(--un-text-opacity))}[color~="#db4619"]{--un-text-opacity:1;color:rgb(219 70 25 / var(--un-text-opacity))}[hover~=color-primary]:hover{color:var(--primary-color)}.dark .dark\\:text-gray-100{--un-text-opacity:1;color:rgb(243 244 246 / var(--un-text-opacity))}.dark .dark\\:text-gray-300,.text-gray-300{--un-text-opacity:1;color:rgb(209 213 219 / var(--un-text-opacity))}.text-\\[rgba\\(0\\,0\\,0\\,0\\.45\\)\\]{--un-text-opacity:.45;color:rgba(0,0,0,var(--un-text-opacity))}.text-gray-400{--un-text-opacity:1;color:rgb(156 163 175 / var(--un-text-opacity))}.text-gray-500{--un-text-opacity:1;color:rgb(107 114 128 / var(--un-text-opacity))}.text-gray-600{--un-text-opacity:1;color:rgb(75 85 99 / var(--un-text-opacity))}.text-gray-700{--un-text-opacity:1;color:rgb(55 65 81 / var(--un-text-opacity))}.text-gray-900{--un-text-opacity:1;color:rgb(17 24 39 / var(--un-text-opacity))}.text-green-400{--un-text-opacity:1;color:rgb(74 222 128 / var(--un-text-opacity))}.text-green-500{--un-text-opacity:1;color:rgb(34 197 94 / var(--un-text-opacity))}.text-red-500{--un-text-opacity:1;color:rgb(239 68 68 / var(--un-text-opacity))}.text-white{--un-text-opacity:1;color:rgb(255 255 255 / var(--un-text-opacity))}.decoration-underline,[hover~=decoration-underline]:hover{text-decoration-line:underline}.tab{-moz-tab-size:4;-o-tab-size:4;tab-size:4}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-85{opacity:.85}.hover\\:opacity-75:hover{opacity:.75}.shadow-black{--un-shadow-opacity:1;--un-shadow-color:rgb(0 0 0 / var(--un-shadow-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}[duration~="500"]{transition-duration:.5s}[content~="$t("]{content:var(--t\\()}.placeholder-gray-400::placeholder{--un-placeholder-opacity:1;color:rgb(156 163 175 / var(--un-placeholder-opacity))}[placeholder~="$t("]::placeholder{color:var(--t\\()}@media (min-width: 768px){.md\\:mx-auto{margin-left:auto;margin-right:auto}.md\\:mb-10{margin-bottom:40px}.md\\:ml-5{margin-left:20px}.md\\:mr-2\\.5{margin-right:10px}.md\\:mt-10{margin-top:40px}.md\\:mt-5{margin-top:20px}.md\\:block{display:block}.md\\:hidden{display:none}.md\\:h-8{height:32px}.md\\:w-8{width:32px}.md\\:flex-\\[1\\]{flex:1}.md\\:flex-\\[2\\]{flex:2}.md\\:p-4{padding:16px}.md\\:pl-5{padding-left:20px}}@media (min-width: 1024px){.lg\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}#--unocss-layer-end--__ALL__--{end:__ALL__}')),document.head.appendChild(o)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}}(); +var e,t,n=Object.defineProperty,o=Object.defineProperties,r=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertyNames,a=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,c=(e,t)=>{if(t=Symbol[e])return t;throw Error("Symbol."+e+" is not defined")},u=(e,t,o)=>t in e?n(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o,d=(e,t)=>{for(var n in t||(t={}))l.call(t,n)&&u(e,n,t[n]);if(a)for(var n of a(t))s.call(t,n)&&u(e,n,t[n]);return e},p=(e,t)=>o(e,r(t)),h=(e,t)=>{var n={};for(var o in e)l.call(e,o)&&t.indexOf(o)<0&&(n[o]=e[o]);if(null!=e&&a)for(var o of a(e))t.indexOf(o)<0&&s.call(e,o)&&(n[o]=e[o]);return n},f=(e,t,n)=>(u(e,"symbol"!=typeof t?t+"":t,n),n),v=(e,t,n)=>new Promise(((o,r)=>{var i=e=>{try{l(n.next(e))}catch(t){r(t)}},a=e=>{try{l(n.throw(e))}catch(t){r(t)}},l=e=>e.done?o(e.value):Promise.resolve(e.value).then(i,a);l((n=n.apply(e,t)).next())})),m=function(e,t){this[0]=e,this[1]=t},g=e=>{var t,n=e[c("asyncIterator")],o=!1,r={};return null==n?(n=e[c("iterator")](),t=e=>r[e]=t=>n[e](t)):(n=n.call(e),t=e=>r[e]=t=>{if(o){if(o=!1,"throw"===e)throw t;return t}return o=!0,{done:!1,value:new m(new Promise((o=>{var r=n[e](t);if(!(r instanceof Object))throw TypeError("Object expected");o(r)})),1)}}),r[c("iterator")]=()=>r,t("next"),"throw"in n?t("throw"):r.throw=e=>{throw e},"return"in n&&t("return"),r},b=(e={"assets/umi.js"(e,t){var n;function o(e,t){const n=new Set(e.split(","));return t?e=>n.has(e.toLowerCase()):e=>n.has(e)}!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))t(e);new MutationObserver((e=>{for(const n of e)if("childList"===n.type)for(const e of n.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&t(e)})).observe(document,{childList:!0,subtree:!0})}function t(e){if(e.ep)return;e.ep=!0;const t=function(e){const t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?t.credentials="include":"anonymous"===e.crossOrigin?t.credentials="omit":t.credentials="same-origin",t}(e);fetch(e.href,t)}}();const r={},i=[],a=()=>{},l=()=>!1,s=e=>111===e.charCodeAt(0)&&110===e.charCodeAt(1)&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),u=e=>e.startsWith("onUpdate:"),b=Object.assign,y=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},x=Object.prototype.hasOwnProperty,C=(e,t)=>x.call(e,t),w=Array.isArray,k=e=>"[object Map]"===E(e),S=e=>"[object Set]"===E(e),_=e=>"function"==typeof e,P=e=>"string"==typeof e,T=e=>"symbol"==typeof e,A=e=>null!==e&&"object"==typeof e,z=e=>(A(e)||_(e))&&_(e.then)&&_(e.catch),R=Object.prototype.toString,E=e=>R.call(e),O=e=>"[object Object]"===E(e),M=e=>P(e)&&"NaN"!==e&&"-"!==e[0]&&""+parseInt(e,10)===e,F=o(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),I=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},L=/-(\w)/g,B=I((e=>e.replace(L,((e,t)=>t?t.toUpperCase():"")))),D=/\B([A-Z])/g,$=I((e=>e.replace(D,"-$1").toLowerCase())),N=I((e=>e.charAt(0).toUpperCase()+e.slice(1))),j=I((e=>e?`on${N(e)}`:"")),H=(e,t)=>!Object.is(e,t),W=(e,...t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:o,value:n})},V=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let q;const K=()=>q||(q="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{});function G(e){if(w(e)){const t={};for(let n=0;n{if(e){const n=e.split(Y);n.length>1&&(t[n[0].trim()]=n[1].trim())}})),t}function J(e){let t="";if(P(e))t=e;else if(w(e))for(let n=0;n!(!e||!0!==e.__v_isRef),oe=e=>P(e)?e:null==e?"":w(e)||A(e)&&(e.toString===R||!_(e.toString))?ne(e)?oe(e.value):JSON.stringify(e,re,2):String(e),re=(e,t)=>ne(t)?re(e,t.value):k(t)?{[`Map(${t.size})`]:[...t.entries()].reduce(((e,[t,n],o)=>(e[ie(t,o)+" =>"]=n,e)),{})}:S(t)?{[`Set(${t.size})`]:[...t.values()].map((e=>ie(e)))}:T(t)?ie(t):!A(t)||w(t)||O(t)?t:String(t),ie=(e,t="")=>{var n;return T(e)?`Symbol(${null!=(n=e.description)?n:t})`:e};let ae,le;class se{constructor(e=!1){this.detached=e,this._active=!0,this.effects=[],this.cleanups=[],this.parent=ae,!e&&ae&&(this.index=(ae.scopes||(ae.scopes=[])).push(this)-1)}get active(){return this._active}run(e){if(this._active){const t=ae;try{return ae=this,e()}finally{ae=t}}}on(){ae=this}off(){ae=this.parent}stop(e){if(this._active){let t,n;for(t=0,n=this.effects.length;t=4))break}1===this._dirtyLevel&&(this._dirtyLevel=0),xe()}return this._dirtyLevel>=4}set dirty(e){this._dirtyLevel=e?4:0}run(){if(this._dirtyLevel=0,!this.active)return this.fn();let e=me,t=le;try{return me=!0,le=this,this._runnings++,he(this),this.fn()}finally{fe(this),this._runnings--,le=t,me=e}}stop(){this.active&&(he(this),fe(this),this.onStop&&this.onStop(),this.active=!1)}}function he(e){e._trackId++,e._depsLength=0}function fe(e){if(e.deps.length>e._depsLength){for(let t=e._depsLength;t{const n=new Map;return n.cleanup=e,n.computed=t,n},Te=new WeakMap,Ae=Symbol(""),ze=Symbol("");function Re(e,t,n){if(me&&le){let t=Te.get(e);t||Te.set(e,t=new Map);let o=t.get(n);o||t.set(n,o=Pe((()=>t.delete(n)))),ke(le,o)}}function Ee(e,t,n,o,r,i){const a=Te.get(e);if(!a)return;let l=[];if("clear"===t)l=[...a.values()];else if("length"===n&&w(e)){const e=Number(o);a.forEach(((t,n)=>{("length"===n||!T(n)&&n>=e)&&l.push(t)}))}else switch(void 0!==n&&l.push(a.get(n)),t){case"add":w(e)?M(n)&&l.push(a.get("length")):(l.push(a.get(Ae)),k(e)&&l.push(a.get(ze)));break;case"delete":w(e)||(l.push(a.get(Ae)),k(e)&&l.push(a.get(ze)));break;case"set":k(e)&&l.push(a.get(Ae))}Ce();for(const s of l)s&&_e(s,4);we()}const Oe=o("__proto__,__v_isRef,__isVue"),Me=new Set(Object.getOwnPropertyNames(Symbol).filter((e=>"arguments"!==e&&"caller"!==e)).map((e=>Symbol[e])).filter(T)),Fe=Ie();function Ie(){const e={};return["includes","indexOf","lastIndexOf"].forEach((t=>{e[t]=function(...e){const n=kt(this);for(let t=0,r=this.length;t{e[t]=function(...e){ye(),Ce();const n=kt(this)[t].apply(this,e);return we(),xe(),n}})),e}function Le(e){T(e)||(e=String(e));const t=kt(this);return Re(t,0,e),t.hasOwnProperty(e)}class Be{constructor(e=!1,t=!1){this._isReadonly=e,this._isShallow=t}get(e,t,n){const o=this._isReadonly,r=this._isShallow;if("__v_isReactive"===t)return!o;if("__v_isReadonly"===t)return o;if("__v_isShallow"===t)return r;if("__v_raw"===t)return n===(o?r?ht:pt:r?dt:ut).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(n)?e:void 0;const i=w(e);if(!o){if(i&&C(Fe,t))return Reflect.get(Fe,t,n);if("hasOwnProperty"===t)return Le}const a=Reflect.get(e,t,n);return(T(t)?Me.has(t):Oe(t))?a:(o||Re(e,0,t),r?a:Rt(a)?i&&M(t)?a:a.value:A(a)?o?gt(a):vt(a):a)}}class De extends Be{constructor(e=!1){super(!1,e)}set(e,t,n,o){let r=e[t];if(!this._isShallow){const t=xt(r);if(Ct(n)||xt(n)||(r=kt(r),n=kt(n)),!w(e)&&Rt(r)&&!Rt(n))return!t&&(r.value=n,!0)}const i=w(e)&&M(t)?Number(t)e,Ue=e=>Reflect.getPrototypeOf(e);function Ve(e,t,n=!1,o=!1){const r=kt(e=e.__v_raw),i=kt(t);n||(H(t,i)&&Re(r,0,t),Re(r,0,i));const{has:a}=Ue(r),l=o?We:n?Pt:_t;return a.call(r,t)?l(e.get(t)):a.call(r,i)?l(e.get(i)):void(e!==r&&e.get(t))}function qe(e,t=!1){const n=this.__v_raw,o=kt(n),r=kt(e);return t||(H(e,r)&&Re(o,0,e),Re(o,0,r)),e===r?n.has(e):n.has(e)||n.has(r)}function Ke(e,t=!1){return e=e.__v_raw,!t&&Re(kt(e),0,Ae),Reflect.get(e,"size",e)}function Ge(e,t=!1){t||Ct(e)||xt(e)||(e=kt(e));const n=kt(this);return Ue(n).has.call(n,e)||(n.add(e),Ee(n,"add",e,e)),this}function Xe(e,t,n=!1){n||Ct(t)||xt(t)||(t=kt(t));const o=kt(this),{has:r,get:i}=Ue(o);let a=r.call(o,e);a||(e=kt(e),a=r.call(o,e));const l=i.call(o,e);return o.set(e,t),a?H(t,l)&&Ee(o,"set",e,t):Ee(o,"add",e,t),this}function Ye(e){const t=kt(this),{has:n,get:o}=Ue(t);let r=n.call(t,e);r||(e=kt(e),r=n.call(t,e)),o&&o.call(t,e);const i=t.delete(e);return r&&Ee(t,"delete",e,void 0),i}function Qe(){const e=kt(this),t=0!==e.size,n=e.clear();return t&&Ee(e,"clear",void 0,void 0),n}function Ze(e,t){return function(n,o){const r=this,i=r.__v_raw,a=kt(i),l=t?We:e?Pt:_t;return!e&&Re(a,0,Ae),i.forEach(((e,t)=>n.call(o,l(e),l(t),r)))}}function Je(e,t,n){return function(...o){const r=this.__v_raw,i=kt(r),a=k(i),l="entries"===e||e===Symbol.iterator&&a,s="keys"===e&&a,c=r[e](...o),u=n?We:t?Pt:_t;return!t&&Re(i,0,s?ze:Ae),{next(){const{value:e,done:t}=c.next();return t?{value:e,done:t}:{value:l?[u(e[0]),u(e[1])]:u(e),done:t}},[Symbol.iterator](){return this}}}}function et(e){return function(...t){return"delete"!==e&&("clear"===e?void 0:this)}}function tt(){const e={get(e){return Ve(this,e)},get size(){return Ke(this)},has:qe,add:Ge,set:Xe,delete:Ye,clear:Qe,forEach:Ze(!1,!1)},t={get(e){return Ve(this,e,!1,!0)},get size(){return Ke(this)},has:qe,add(e){return Ge.call(this,e,!0)},set(e,t){return Xe.call(this,e,t,!0)},delete:Ye,clear:Qe,forEach:Ze(!1,!0)},n={get(e){return Ve(this,e,!0)},get size(){return Ke(this,!0)},has(e){return qe.call(this,e,!0)},add:et("add"),set:et("set"),delete:et("delete"),clear:et("clear"),forEach:Ze(!0,!1)},o={get(e){return Ve(this,e,!0,!0)},get size(){return Ke(this,!0)},has(e){return qe.call(this,e,!0)},add:et("add"),set:et("set"),delete:et("delete"),clear:et("clear"),forEach:Ze(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach((r=>{e[r]=Je(r,!1,!1),n[r]=Je(r,!0,!1),t[r]=Je(r,!1,!0),o[r]=Je(r,!0,!0)})),[e,n,t,o]}const[nt,ot,rt,it]=tt();function at(e,t){const n=t?e?it:rt:e?ot:nt;return(t,o,r)=>"__v_isReactive"===o?!e:"__v_isReadonly"===o?e:"__v_raw"===o?t:Reflect.get(C(n,o)&&o in t?n:t,o,r)}const lt={get:at(!1,!1)},st={get:at(!1,!0)},ct={get:at(!0,!1)},ut=new WeakMap,dt=new WeakMap,pt=new WeakMap,ht=new WeakMap;function ft(e){return e.__v_skip||!Object.isExtensible(e)?0:function(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}((e=>E(e).slice(8,-1))(e))}function vt(e){return xt(e)?e:bt(e,!1,Ne,lt,ut)}function mt(e){return bt(e,!1,He,st,dt)}function gt(e){return bt(e,!0,je,ct,pt)}function bt(e,t,n,o,r){if(!A(e))return e;if(e.__v_raw&&(!t||!e.__v_isReactive))return e;const i=r.get(e);if(i)return i;const a=ft(e);if(0===a)return e;const l=new Proxy(e,2===a?o:n);return r.set(e,l),l}function yt(e){return xt(e)?yt(e.__v_raw):!(!e||!e.__v_isReactive)}function xt(e){return!(!e||!e.__v_isReadonly)}function Ct(e){return!(!e||!e.__v_isShallow)}function wt(e){return!!e&&!!e.__v_raw}function kt(e){const t=e&&e.__v_raw;return t?kt(t):e}function St(e){return Object.isExtensible(e)&&U(e,"__v_skip",!0),e}const _t=e=>A(e)?vt(e):e,Pt=e=>A(e)?gt(e):e;class Tt{constructor(e,t,n,o){this.getter=e,this._setter=t,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this.effect=new pe((()=>e(this._value)),(()=>zt(this,2===this.effect._dirtyLevel?2:3))),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=n}get value(){const e=kt(this);return e._cacheable&&!e.effect.dirty||!H(e._value,e._value=e.effect.run())||zt(e,4),At(e),e.effect._dirtyLevel>=2&&zt(e,2),e._value}set value(e){this._setter(e)}get _dirty(){return this.effect.dirty}set _dirty(e){this.effect.dirty=e}}function At(e){var t;me&&le&&(e=kt(e),ke(le,null!=(t=e.dep)?t:e.dep=Pe((()=>e.dep=void 0),e instanceof Tt?e:void 0)))}function zt(e,t=4,n,o){const r=(e=kt(e)).dep;r&&_e(r,t)}function Rt(e){return!(!e||!0!==e.__v_isRef)}function Et(e){return Mt(e,!1)}function Ot(e){return Mt(e,!0)}function Mt(e,t){return Rt(e)?e:new Ft(e,t)}class Ft{constructor(e,t){this.__v_isShallow=t,this.dep=void 0,this.__v_isRef=!0,this._rawValue=t?e:kt(e),this._value=t?e:_t(e)}get value(){return At(this),this._value}set value(e){const t=this.__v_isShallow||Ct(e)||xt(e);e=t?e:kt(e),H(e,this._rawValue)&&(this._rawValue,this._rawValue=e,this._value=t?e:_t(e),zt(this,4))}}function It(e){return Rt(e)?e.value:e}const Lt={get:(e,t,n)=>It(Reflect.get(e,t,n)),set:(e,t,n,o)=>{const r=e[t];return Rt(r)&&!Rt(n)?(r.value=n,!0):Reflect.set(e,t,n,o)}};function Bt(e){return yt(e)?e:new Proxy(e,Lt)}class Dt{constructor(e){this.dep=void 0,this.__v_isRef=!0;const{get:t,set:n}=e((()=>At(this)),(()=>zt(this)));this._get=t,this._set=n}get value(){return this._get()}set value(e){this._set(e)}}class $t{constructor(e,t,n){this._object=e,this._key=t,this._defaultValue=n,this.__v_isRef=!0}get value(){const e=this._object[this._key];return void 0===e?this._defaultValue:e}set value(e){this._object[this._key]=e}get dep(){return function(e,t){const n=Te.get(e);return n&&n.get(t)}(kt(this._object),this._key)}}class Nt{constructor(e){this._getter=e,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function jt(e,t,n){return Rt(e)?e:_(e)?new Nt(e):A(e)&&arguments.length>1?Ht(e,t,n):Et(e)}function Ht(e,t,n){const o=e[t];return Rt(o)?o:new $t(e,t,n)}function Wt(e,t,n,o){try{return o?e(...o):e()}catch(r){Vt(r,t,n)}}function Ut(e,t,n,o){if(_(e)){const r=Wt(e,t,n,o);return r&&z(r)&&r.catch((e=>{Vt(e,t,n)})),r}if(w(e)){const r=[];for(let i=0;i>>1,r=Gt[o],i=ln(r);iln(e)-ln(t)));if(Yt.length=0,Qt)return void Qt.push(...e);for(Qt=e,Zt=0;Ztnull==e.id?1/0:e.id,sn=(e,t)=>{const n=ln(e)-ln(t);if(0===n){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function cn(e){Kt=!1,qt=!0,Gt.sort(sn);try{for(Xt=0;Xt{o._d&&Tr(-1);const r=pn(t);let i;try{i=e(...n)}finally{pn(r),o._d&&Tr(1)}return i};return o._n=!0,o._c=!0,o._d=!0,o}function fn(e,t){if(null===un)return e;const n=ri(un),o=e.dirs||(e.dirs=[]);for(let i=0;i{e.isMounted=!0})),Hn((()=>{e.isUnmounting=!0})),e}const yn=[Function,Array],xn={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:yn,onEnter:yn,onAfterEnter:yn,onEnterCancelled:yn,onBeforeLeave:yn,onLeave:yn,onAfterLeave:yn,onLeaveCancelled:yn,onBeforeAppear:yn,onAppear:yn,onAfterAppear:yn,onAppearCancelled:yn},Cn=e=>{const t=e.subTree;return t.component?Cn(t.component):t},wn={name:"BaseTransition",props:xn,setup(e,{slots:t}){const n=Gr(),o=bn();return()=>{const r=t.default&&An(t.default(),!0);if(!r||!r.length)return;let i=r[0];if(r.length>1)for(const e of r)if(e.type!==Cr){i=e;break}const a=kt(e),{mode:l}=a;if(o.isLeaving)return _n(i);const s=Pn(i);if(!s)return _n(i);let c=Sn(s,a,o,n,(e=>c=e));Tn(s,c);const u=n.subTree,d=u&&Pn(u);if(d&&d.type!==Cr&&!Or(s,d)&&Cn(n).type!==Cr){const e=Sn(d,a,o,n);if(Tn(d,e),"out-in"===l&&s.type!==Cr)return o.isLeaving=!0,e.afterLeave=()=>{o.isLeaving=!1,!1!==n.update.active&&(n.effect.dirty=!0,n.update())},_n(i);"in-out"===l&&s.type!==Cr&&(e.delayLeave=(e,t,n)=>{kn(o,d)[String(d.key)]=d,e[mn]=()=>{t(),e[mn]=void 0,delete c.delayedLeave},c.delayedLeave=n})}return i}}};function kn(e,t){const{leavingVNodes:n}=e;let o=n.get(t.type);return o||(o=Object.create(null),n.set(t.type,o)),o}function Sn(e,t,n,o,r){const{appear:i,mode:a,persisted:l=!1,onBeforeEnter:s,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:h,onAfterLeave:f,onLeaveCancelled:v,onBeforeAppear:m,onAppear:g,onAfterAppear:b,onAppearCancelled:y}=t,x=String(e.key),C=kn(n,e),k=(e,t)=>{e&&Ut(e,o,9,t)},S=(e,t)=>{const n=t[1];k(e,t),w(e)?e.every((e=>e.length<=1))&&n():e.length<=1&&n()},_={mode:a,persisted:l,beforeEnter(t){let o=s;if(!n.isMounted){if(!i)return;o=m||s}t[mn]&&t[mn](!0);const r=C[x];r&&Or(e,r)&&r.el[mn]&&r.el[mn](),k(o,[t])},enter(e){let t=c,o=u,r=d;if(!n.isMounted){if(!i)return;t=g||c,o=b||u,r=y||d}let a=!1;const l=e[gn]=t=>{a||(a=!0,k(t?r:o,[e]),_.delayedLeave&&_.delayedLeave(),e[gn]=void 0)};t?S(t,[e,l]):l()},leave(t,o){const r=String(e.key);if(t[gn]&&t[gn](!0),n.isUnmounting)return o();k(p,[t]);let i=!1;const a=t[mn]=n=>{i||(i=!0,o(),k(n?v:f,[t]),t[mn]=void 0,C[r]===e&&delete C[r])};C[r]=e,h?S(h,[t,a]):a()},clone(e){const i=Sn(e,t,n,o,r);return r&&r(i),i}};return _}function _n(e){if(En(e))return(e=Br(e)).children=null,e}function Pn(e){if(!En(e))return e;const{shapeFlag:t,children:n}=e;if(n){if(16&t)return n[0];if(32&t&&_(n.default))return n.default()}}function Tn(e,t){6&e.shapeFlag&&e.component?Tn(e.component.subTree,t):128&e.shapeFlag?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function An(e,t=!1,n){let o=[],r=0;for(let i=0;i1)for(let i=0;ib({name:e.name},t,{setup:e}))():e}const Rn=e=>!!e.type.__asyncLoader,En=e=>e.type.__isKeepAlive;function On(e,t){Fn(e,"a",t)}function Mn(e,t){Fn(e,"da",t)}function Fn(e,t,n=Kr){const o=e.__wdc||(e.__wdc=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}return e()});if(Ln(t,o,n),n){let e=n.parent;for(;e&&e.parent;)En(e.parent.vnode)&&In(o,t,n,e),e=e.parent}}function In(e,t,n,o){const r=Ln(t,e,o,!0);Wn((()=>{y(o[t],r)}),n)}function Ln(e,t,n=Kr,o=!1){if(n){const r=n[e]||(n[e]=[]),i=t.__weh||(t.__weh=(...o)=>{ye();const r=Qr(n),i=Ut(t,n,e,o);return r(),xe(),i});return o?r.unshift(i):r.push(i),i}}const Bn=e=>(t,n=Kr)=>{ei&&"sp"!==e||Ln(e,((...e)=>t(...e)),n)},Dn=Bn("bm"),$n=Bn("m"),Nn=Bn("bu"),jn=Bn("u"),Hn=Bn("bum"),Wn=Bn("um"),Un=Bn("sp"),Vn=Bn("rtg"),qn=Bn("rtc");function Kn(e,t=Kr){Ln("ec",e,t)}const Gn="components";function Xn(e,t){return Zn(Gn,e,!0,t)||e}const Yn=Symbol.for("v-ndc");function Qn(e){return P(e)?Zn(Gn,e,!1)||e:e||Yn}function Zn(e,t,n=!0,o=!1){const r=un||Kr;if(r){const n=r.type;if(e===Gn){const e=ii(n,!1);if(e&&(e===t||e===B(t)||e===N(B(t))))return n}const i=Jn(r[e]||n[e],t)||Jn(r.appContext[e],t);return!i&&o?n:i}}function Jn(e,t){return e&&(e[t]||e[B(t)]||e[N(B(t))])}function eo(e,t,n,o){let r;const i=n&&n[o];if(w(e)||P(e)){r=new Array(e.length);for(let n=0,o=e.length;nt(e,n,void 0,i&&i[n])));else{const n=Object.keys(e);r=new Array(n.length);for(let o=0,a=n.length;o!Er(e)||e.type!==Cr&&!(e.type===yr&&!no(e.children))))?e:null}const oo=e=>e?Jr(e)?ri(e):oo(e.parent):null,ro=b(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>oo(e.parent),$root:e=>oo(e.root),$emit:e=>e.emit,$options:e=>ho(e),$forceUpdate:e=>e.f||(e.f=()=>{e.effect.dirty=!0,nn(e.update)}),$nextTick:e=>e.n||(e.n=tn.bind(e.proxy)),$watch:e=>cr.bind(e)}),io=(e,t)=>e!==r&&!e.__isScriptSetup&&C(e,t),ao={get({_:e},t){if("__v_skip"===t)return!0;const{ctx:n,setupState:o,data:i,props:a,accessCache:l,type:s,appContext:c}=e;let u;if("$"!==t[0]){const s=l[t];if(void 0!==s)switch(s){case 1:return o[t];case 2:return i[t];case 4:return n[t];case 3:return a[t]}else{if(io(o,t))return l[t]=1,o[t];if(i!==r&&C(i,t))return l[t]=2,i[t];if((u=e.propsOptions[0])&&C(u,t))return l[t]=3,a[t];if(n!==r&&C(n,t))return l[t]=4,n[t];so&&(l[t]=0)}}const d=ro[t];let p,h;return d?("$attrs"===t&&Re(e.attrs,0,""),d(e)):(p=s.__cssModules)&&(p=p[t])?p:n!==r&&C(n,t)?(l[t]=4,n[t]):(h=c.config.globalProperties,C(h,t)?h[t]:void 0)},set({_:e},t,n){const{data:o,setupState:i,ctx:a}=e;return io(i,t)?(i[t]=n,!0):o!==r&&C(o,t)?(o[t]=n,!0):!(C(e.props,t)||"$"===t[0]&&t.slice(1)in e||(a[t]=n,0))},has({_:{data:e,setupState:t,accessCache:n,ctx:o,appContext:i,propsOptions:a}},l){let s;return!!n[l]||e!==r&&C(e,l)||io(t,l)||(s=a[0])&&C(s,l)||C(o,l)||C(ro,l)||C(i.config.globalProperties,l)},defineProperty(e,t,n){return null!=n.get?e._.accessCache[t]=0:C(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function lo(e){return w(e)?e.reduce(((e,t)=>(e[t]=null,e)),{}):e}let so=!0;function co(e){const t=ho(e),n=e.proxy,o=e.ctx;so=!1,t.beforeCreate&&uo(t.beforeCreate,e,"bc");const{data:r,computed:i,methods:l,watch:s,provide:c,inject:u,created:d,beforeMount:p,mounted:h,beforeUpdate:f,updated:v,activated:m,deactivated:g,beforeDestroy:b,beforeUnmount:y,destroyed:x,unmounted:C,render:k,renderTracked:S,renderTriggered:P,errorCaptured:T,serverPrefetch:z,expose:R,inheritAttrs:E,components:O,directives:M,filters:F}=t;if(u&&function(e,t,n=a){w(e)&&(e=go(e));for(const o in e){const n=e[o];let r;r=A(n)?"default"in n?Po(n.from||o,n.default,!0):Po(n.from||o):Po(n),Rt(r)?Object.defineProperty(t,o,{enumerable:!0,configurable:!0,get:()=>r.value,set:e=>r.value=e}):t[o]=r}}(u,o,null),l)for(const a in l){const e=l[a];_(e)&&(o[a]=e.bind(n))}if(r){const t=r.call(n,n);A(t)&&(e.data=vt(t))}if(so=!0,i)for(const w in i){const e=i[w],t=_(e)?e.bind(n,n):_(e.get)?e.get.bind(n,n):a,r=!_(e)&&_(e.set)?e.set.bind(n):a,l=ai({get:t,set:r});Object.defineProperty(o,w,{enumerable:!0,configurable:!0,get:()=>l.value,set:e=>l.value=e})}if(s)for(const a in s)po(s[a],o,n,a);if(c){const e=_(c)?c.call(n):c;Reflect.ownKeys(e).forEach((t=>{_o(t,e[t])}))}function I(e,t){w(t)?t.forEach((t=>e(t.bind(n)))):t&&e(t.bind(n))}if(d&&uo(d,e,"c"),I(Dn,p),I($n,h),I(Nn,f),I(jn,v),I(On,m),I(Mn,g),I(Kn,T),I(qn,S),I(Vn,P),I(Hn,y),I(Wn,C),I(Un,z),w(R))if(R.length){const t=e.exposed||(e.exposed={});R.forEach((e=>{Object.defineProperty(t,e,{get:()=>n[e],set:t=>n[e]=t})}))}else e.exposed||(e.exposed={});k&&e.render===a&&(e.render=k),null!=E&&(e.inheritAttrs=E),O&&(e.components=O),M&&(e.directives=M)}function uo(e,t,n){Ut(w(e)?e.map((e=>e.bind(t.proxy))):e.bind(t.proxy),t,n)}function po(e,t,n,o){const r=o.includes(".")?ur(n,o):()=>n[o];if(P(e)){const n=t[e];_(n)&&lr(r,n)}else if(_(e))lr(r,e.bind(n));else if(A(e))if(w(e))e.forEach((e=>po(e,t,n,o)));else{const o=_(e.handler)?e.handler.bind(n):t[e.handler];_(o)&&lr(r,o,e)}}function ho(e){const t=e.type,{mixins:n,extends:o}=t,{mixins:r,optionsCache:i,config:{optionMergeStrategies:a}}=e.appContext,l=i.get(t);let s;return l?s=l:r.length||n||o?(s={},r.length&&r.forEach((e=>fo(s,e,a,!0))),fo(s,t,a)):s=t,A(t)&&i.set(t,s),s}function fo(e,t,n,o=!1){const{mixins:r,extends:i}=t;i&&fo(e,i,n,!0),r&&r.forEach((t=>fo(e,t,n,!0)));for(const a in t)if(o&&"expose"===a);else{const o=vo[a]||n&&n[a];e[a]=o?o(e[a],t[a]):t[a]}return e}const vo={data:mo,props:xo,emits:xo,methods:yo,computed:yo,beforeCreate:bo,created:bo,beforeMount:bo,mounted:bo,beforeUpdate:bo,updated:bo,beforeDestroy:bo,beforeUnmount:bo,destroyed:bo,unmounted:bo,activated:bo,deactivated:bo,errorCaptured:bo,serverPrefetch:bo,components:yo,directives:yo,watch:function(e,t){if(!e)return t;if(!t)return e;const n=b(Object.create(null),e);for(const o in t)n[o]=bo(e[o],t[o]);return n},provide:mo,inject:function(e,t){return yo(go(e),go(t))}};function mo(e,t){return t?e?function(){return b(_(e)?e.call(this,this):e,_(t)?t.call(this,this):t)}:t:e}function go(e){if(w(e)){const t={};for(let n=0;n(i.has(e)||(e&&_(e.install)?(i.add(e),e.install(l,...t)):_(e)&&(i.add(e),e(l,...t))),l),mixin:e=>(r.mixins.includes(e)||r.mixins.push(e),l),component:(e,t)=>t?(r.components[e]=t,l):r.components[e],directive:(e,t)=>t?(r.directives[e]=t,l):r.directives[e],mount(i,s,c){if(!a){const u=Lr(n,o);return u.appContext=r,!0===c?c="svg":!1===c&&(c=void 0),s&&t?t(u,i):e(u,i,c),a=!0,l._container=i,i.__vue_app__=l,ri(u.component)}},unmount(){a&&(e(null,l._container),delete l._container.__vue_app__)},provide:(e,t)=>(r.provides[e]=t,l),runWithContext(e){const t=So;So=l;try{return e()}finally{So=t}}};return l}}let So=null;function _o(e,t){if(Kr){let n=Kr.provides;const o=Kr.parent&&Kr.parent.provides;o===n&&(n=Kr.provides=Object.create(o)),n[e]=t}}function Po(e,t,n=!1){const o=Kr||un;if(o||So){const r=So?So._context.provides:o?null==o.parent?o.vnode.appContext&&o.vnode.appContext.provides:o.parent.provides:void 0;if(r&&e in r)return r[e];if(arguments.length>1)return n&&_(t)?t.call(o&&o.proxy):t}}const To={},Ao=()=>Object.create(To),zo=e=>Object.getPrototypeOf(e)===To;function Ro(e,t,n,o){const[i,a]=e.propsOptions;let l,s=!1;if(t)for(let r in t){if(F(r))continue;const c=t[r];let u;i&&C(i,u=B(r))?a&&a.includes(u)?(l||(l={}))[u]=c:n[u]=c:fr(e.emitsOptions,r)||r in o&&c===o[r]||(o[r]=c,s=!0)}if(a){const t=kt(n),o=l||r;for(let r=0;r{u=!0;const[n,o]=Mo(e,t,!0);b(s,n),o&&c.push(...o)};!n&&t.mixins.length&&t.mixins.forEach(o),e.extends&&o(e.extends),e.mixins&&e.mixins.forEach(o)}if(!l&&!u)return A(e)&&o.set(e,i),i;if(w(l))for(let i=0;i"_"===e[0]||"$stable"===e,Lo=e=>w(e)?e.map(Nr):[Nr(e)],Bo=(e,t,n)=>{if(t._n)return t;const o=hn(((...e)=>Lo(t(...e))),n);return o._c=!1,o},Do=(e,t,n)=>{const o=e._ctx;for(const r in e){if(Io(r))continue;const n=e[r];if(_(n))t[r]=Bo(0,n,o);else if(null!=n){const e=Lo(n);t[r]=()=>e}}},$o=(e,t)=>{const n=Lo(t);e.slots.default=()=>n},No=(e,t,n)=>{for(const o in t)(n||"_"!==o)&&(e[o]=t[o])};function jo(e,t,n,o,i=!1){if(w(e))return void e.forEach(((e,r)=>jo(e,t&&(w(t)?t[r]:t),n,o,i)));if(Rn(o)&&!i)return;const a=4&o.shapeFlag?ri(o.component):o.el,l=i?null:a,{i:s,r:c}=e,u=t&&t.r,d=s.refs===r?s.refs={}:s.refs,p=s.setupState;if(null!=u&&u!==c&&(P(u)?(d[u]=null,C(p,u)&&(p[u]=null)):Rt(u)&&(u.value=null)),_(c))Wt(c,s,12,[l,d]);else{const t=P(c),o=Rt(c);if(t||o){const r=()=>{if(e.f){const n=t?C(p,c)?p[c]:d[c]:c.value;i?w(n)&&y(n,a):w(n)?n.includes(a)||n.push(a):t?(d[c]=[a],C(p,c)&&(p[c]=d[c])):(c.value=[a],e.k&&(d[e.k]=c.value))}else t?(d[c]=l,C(p,c)&&(p[c]=l)):o&&(c.value=l,e.k&&(d[e.k]=l))};l?(r.id=-1,Qo(r,n)):r()}}}const Ho=Symbol("_vte"),Wo=e=>e&&(e.disabled||""===e.disabled),Uo=e=>"undefined"!=typeof SVGElement&&e instanceof SVGElement,Vo=e=>"function"==typeof MathMLElement&&e instanceof MathMLElement,qo=(e,t)=>{const n=e&&e.to;return P(n)?t?t(n):null:n};function Ko(e,t,n,{o:{insert:o},m:r},i=2){0===i&&o(e.targetAnchor,t,n);const{el:a,anchor:l,shapeFlag:s,children:c,props:u}=e,d=2===i;if(d&&o(a,t,n),(!d||Wo(u))&&16&s)for(let p=0;p{16&b&&u(y,e,t,r,i,a,l,s)};g?m(n,c):d&&m(d,p)}else{t.el=e.el,t.targetStart=e.targetStart;const o=t.anchor=e.anchor,u=t.target=e.target,h=t.targetAnchor=e.targetAnchor,v=Wo(e.props),m=v?n:u,b=v?o:h;if("svg"===a||Uo(u)?a="svg":("mathml"===a||Vo(u))&&(a="mathml"),x?(p(e.dynamicChildren,x,m,r,i,a,l),tr(e,t,!0)):s||d(e,t,m,b,r,i,a,l,!1),g)v?t.props&&e.props&&t.props.to!==e.props.to&&(t.props.to=e.props.to):Ko(t,n,o,c,1);else if((t.props&&t.props.to)!==(e.props&&e.props.to)){const e=t.target=qo(t.props,f);e&&Ko(t,e,null,c,0)}else v&&Ko(t,u,h,c,1)}Xo(t)},remove(e,t,n,{um:o,o:{remove:r}},i){const{shapeFlag:a,children:l,anchor:s,targetStart:c,targetAnchor:u,target:d,props:p}=e;if(d&&(r(c),r(u)),i&&r(s),16&a){const e=i||!Wo(p);for(let r=0;r{if(e===t)return;e&&!Or(e,t)&&(o=J(e),G(e,r,i,!0),e=null),-2===t.patchFlag&&(s=!1,t.dynamicChildren=null);const{type:c,ref:u,shapeFlag:d}=t;switch(c){case xr:b(e,t,n,o);break;case Cr:y(e,t,n,o);break;case wr:null==e&&x(t,n,o,a);break;case yr:O(e,t,n,o,r,i,a,l,s);break;default:1&d?S(e,t,n,o,r,i,a,l,s):6&d?M(e,t,n,o,r,i,a,l,s):(64&d||128&d)&&c.process(e,t,n,o,r,i,a,l,s,ne)}null!=u&&r&&jo(u,e&&e.ref,i,t||e,!t)},b=(e,t,o,r)=>{if(null==e)n(t.el=c(t.children),o,r);else{const n=t.el=e.el;t.children!==e.children&&d(n,t.children)}},y=(e,t,o,r)=>{null==e?n(t.el=u(t.children||""),o,r):t.el=e.el},x=(e,t,n,o)=>{[e.el,e.anchor]=m(e.children,t,n,o,e.el,e.anchor)},w=({el:e,anchor:t},o,r)=>{let i;for(;e&&e!==t;)i=f(e),n(e,o,r),e=i;n(t,o,r)},k=({el:e,anchor:t})=>{let n;for(;e&&e!==t;)n=f(e),o(e),e=n;o(t)},S=(e,t,n,o,r,i,a,l,s)=>{"svg"===t.type?a="svg":"math"===t.type&&(a="mathml"),null==e?_(t,n,o,r,i,a,l,s):A(e,t,r,i,a,l,s)},_=(e,t,o,r,i,a,c,u)=>{let d,h;const{props:f,shapeFlag:v,transition:m,dirs:g}=e;if(d=e.el=s(e.type,a,f&&f.is,f),8&v?p(d,e.children):16&v&&T(e.children,d,null,r,i,Jo(e,a),c,u),g&&vn(e,null,r,"created"),P(d,e,e.scopeId,c,r),f){for(const e in f)"value"===e||F(e)||l(d,e,null,f[e],a,r);"value"in f&&l(d,"value",null,f.value,a),(h=f.onVnodeBeforeMount)&&Ur(h,r,e)}g&&vn(e,null,r,"beforeMount");const b=function(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}(i,m);b&&m.beforeEnter(d),n(d,t,o),((h=f&&f.onVnodeMounted)||b||g)&&Qo((()=>{h&&Ur(h,r,e),b&&m.enter(d),g&&vn(e,null,r,"mounted")}),i)},P=(e,t,n,o,r)=>{if(n&&v(e,n),o)for(let i=0;i{for(let c=s;c{const c=t.el=e.el;let{patchFlag:u,dynamicChildren:d,dirs:h}=t;u|=16&e.patchFlag;const f=e.props||r,v=t.props||r;let m;if(n&&er(n,!1),(m=v.onVnodeBeforeUpdate)&&Ur(m,n,t,e),h&&vn(t,e,n,"beforeUpdate"),n&&er(n,!0),(f.innerHTML&&null==v.innerHTML||f.textContent&&null==v.textContent)&&p(c,""),d?R(e.dynamicChildren,d,c,n,o,Jo(t,i),a):s||j(e,t,c,null,n,o,Jo(t,i),a,!1),u>0){if(16&u)E(c,f,v,n,i);else if(2&u&&f.class!==v.class&&l(c,"class",null,v.class,i),4&u&&l(c,"style",f.style,v.style,i),8&u){const e=t.dynamicProps;for(let t=0;t{m&&Ur(m,n,t,e),h&&vn(t,e,n,"updated")}),o)},R=(e,t,n,o,r,i,a)=>{for(let l=0;l{if(t!==n){if(t!==r)for(const r in t)F(r)||r in n||l(e,r,t[r],null,i,o);for(const r in n){if(F(r))continue;const a=n[r],s=t[r];a!==s&&"value"!==r&&l(e,r,s,a,i,o)}"value"in n&&l(e,"value",t.value,n.value,i)}},O=(e,t,o,r,i,a,l,s,u)=>{const d=t.el=e?e.el:c(""),p=t.anchor=e?e.anchor:c("");let{patchFlag:h,dynamicChildren:f,slotScopeIds:v}=t;v&&(s=s?s.concat(v):v),null==e?(n(d,o,r),n(p,o,r),T(t.children||[],o,p,i,a,l,s,u)):h>0&&64&h&&f&&e.dynamicChildren?(R(e.dynamicChildren,f,o,i,a,l,s),(null!=t.key||i&&t===i.subTree)&&tr(e,t,!0)):j(e,t,o,p,i,a,l,s,u)},M=(e,t,n,o,r,i,a,l,s)=>{t.slotScopeIds=l,null==e?512&t.shapeFlag?r.ctx.activate(t,n,o,a,s):I(t,n,o,r,i,a,s):L(e,t,s)},I=(e,t,n,o,i,a,l)=>{const s=e.component=function(e,t,n){const o=e.type,i=(t?t.appContext:e.appContext)||Vr,a={uid:qr++,vnode:e,type:o,parent:t,appContext:i,root:null,next:null,subTree:null,effect:null,update:null,scope:new se(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:t?t.provides:Object.create(i.provides),accessCache:null,renderCache:[],components:null,directives:null,propsOptions:Mo(o,i),emitsOptions:hr(o,i),emit:null,emitted:null,propsDefaults:r,inheritAttrs:o.inheritAttrs,ctx:r,data:r,props:r,attrs:r,slots:r,refs:r,setupState:r,setupContext:null,suspense:n,suspenseId:n?n.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return a.ctx={_:a},a.root=t?t.root:a,a.emit=pr.bind(null,a),e.ce&&e.ce(a),a}(e,o,i);if(En(e)&&(s.ctx.renderer=ne),function(e,t=!1,n=!1){t&&Yr(t);const{props:o,children:r}=e.vnode,i=Jr(e);(function(e,t,n,o=!1){const r={},i=Ao();e.propsDefaults=Object.create(null),Ro(e,t,r,i);for(const a in e.propsOptions[0])a in r||(r[a]=void 0);n?e.props=o?r:mt(r):e.type.props?e.props=r:e.props=i,e.attrs=i})(e,o,i,t),((e,t,n)=>{const o=e.slots=Ao();if(32&e.vnode.shapeFlag){const e=t._;e?(No(o,t,n),n&&U(o,"_",e,!0)):Do(t,o)}else t&&$o(e,t)})(e,r,n);i&&function(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,ao);const{setup:o}=n;if(o){const n=e.setupContext=o.length>1?function(e){const t=t=>{e.exposed=t||{}};return{attrs:new Proxy(e.attrs,oi),slots:e.slots,emit:e.emit,expose:t}}(e):null,r=Qr(e);ye();const i=Wt(o,e,0,[e.props,n]);if(xe(),r(),z(i)){if(i.then(Zr,Zr),t)return i.then((n=>{ti(e,n,t)})).catch((t=>{Vt(t,e,0)}));e.asyncDep=i}else ti(e,i,t)}else ni(e)}(e,t);t&&Yr(!1)}(s,!1,l),s.asyncDep){if(i&&i.registerDep(s,D,l),!e.el){const e=s.subTree=Lr(Cr);y(null,e,t,n)}}else D(s,e,t,n,i,a,l)},L=(e,t,n)=>{const o=t.component=e.component;if(function(e,t,n){const{props:o,children:r,component:i}=e,{props:a,children:l,patchFlag:s}=t,c=i.emitsOptions;if(t.dirs||t.transition)return!0;if(!(n&&s>=0))return!(!r&&!l||l&&l.$stable)||o!==a&&(o?!a||br(o,a,c):!!a);if(1024&s)return!0;if(16&s)return o?br(o,a,c):!!a;if(8&s){const e=t.dynamicProps;for(let t=0;tXt&&Gt.splice(t,1)}(o.update),o.effect.dirty=!0,o.update()}else t.el=e.el,o.vnode=t},D=(e,t,n,o,r,i,l)=>{const s=()=>{if(e.isMounted){let{next:t,bu:n,u:o,parent:a,vnode:c}=e;{const n=nr(e);if(n)return t&&(t.el=c.el,N(e,t,l)),void n.asyncDep.then((()=>{e.isUnmounted||s()}))}let u,d=t;er(e,!1),t?(t.el=c.el,N(e,t,l)):t=c,n&&W(n),(u=t.props&&t.props.onVnodeBeforeUpdate)&&Ur(u,a,t,c),er(e,!0);const p=vr(e),f=e.subTree;e.subTree=p,g(f,p,h(f.el),J(f),e,r,i),t.el=p.el,null===d&&function({vnode:e,parent:t},n){for(;t;){const o=t.subTree;if(o.suspense&&o.suspense.activeBranch===e&&(o.el=e.el),o!==e)break;(e=t.vnode).el=n,t=t.parent}}(e,p.el),o&&Qo(o,r),(u=t.props&&t.props.onVnodeUpdated)&&Qo((()=>Ur(u,a,t,c)),r)}else{let a;const{el:l,props:s}=t,{bm:c,m:u,parent:d}=e,p=Rn(t);if(er(e,!1),c&&W(c),!p&&(a=s&&s.onVnodeBeforeMount)&&Ur(a,d,t),er(e,!0),l&&re){const n=()=>{e.subTree=vr(e),re(l,e.subTree,e,r,null)};p?t.type.__asyncLoader().then((()=>!e.isUnmounted&&n())):n()}else{const a=e.subTree=vr(e);g(null,a,n,o,e,r,i),t.el=a.el}if(u&&Qo(u,r),!p&&(a=s&&s.onVnodeMounted)){const e=t;Qo((()=>Ur(a,d,e)),r)}(256&t.shapeFlag||d&&Rn(d.vnode)&&256&d.vnode.shapeFlag)&&e.a&&Qo(e.a,r),e.isMounted=!0,t=n=o=null}},c=e.effect=new pe(s,a,(()=>nn(u)),e.scope),u=e.update=()=>{c.dirty&&c.run()};u.i=e,u.id=e.uid,er(e,!0),u()},N=(e,t,n)=>{t.component=e;const o=e.vnode.props;e.vnode=t,e.next=null,function(e,t,n,o){const{props:r,attrs:i,vnode:{patchFlag:a}}=e,l=kt(r),[s]=e.propsOptions;let c=!1;if(!(o||a>0)||16&a){let o;Ro(e,t,r,i)&&(c=!0);for(const i in l)t&&(C(t,i)||(o=$(i))!==i&&C(t,o))||(s?!n||void 0===n[i]&&void 0===n[o]||(r[i]=Eo(s,l,i,void 0,e,!0)):delete r[i]);if(i!==l)for(const e in i)t&&C(t,e)||(delete i[e],c=!0)}else if(8&a){const n=e.vnode.dynamicProps;for(let o=0;o{const{vnode:o,slots:i}=e;let a=!0,l=r;if(32&o.shapeFlag){const e=t._;e?n&&1===e?a=!1:No(i,t,n):(a=!t.$stable,Do(t,i)),l=t}else t&&($o(e,t),l={default:1});if(a)for(const r in i)Io(r)||null!=l[r]||delete i[r]})(e,t.children,n),ye(),rn(e),xe()},j=(e,t,n,o,r,i,a,l,s=!1)=>{const c=e&&e.children,u=e?e.shapeFlag:0,d=t.children,{patchFlag:h,shapeFlag:f}=t;if(h>0){if(128&h)return void V(c,d,n,o,r,i,a,l,s);if(256&h)return void H(c,d,n,o,r,i,a,l,s)}8&f?(16&u&&Z(c,r,i),d!==c&&p(n,d)):16&u?16&f?V(c,d,n,o,r,i,a,l,s):Z(c,r,i,!0):(8&u&&p(n,""),16&f&&T(d,n,o,r,i,a,l,s))},H=(e,t,n,o,r,a,l,s,c)=>{t=t||i;const u=(e=e||i).length,d=t.length,p=Math.min(u,d);let h;for(h=0;hd?Z(e,r,a,!0,!1,p):T(t,n,o,r,a,l,s,c,p)},V=(e,t,n,o,r,a,l,s,c)=>{let u=0;const d=t.length;let p=e.length-1,h=d-1;for(;u<=p&&u<=h;){const o=e[u],i=t[u]=c?jr(t[u]):Nr(t[u]);if(!Or(o,i))break;g(o,i,n,null,r,a,l,s,c),u++}for(;u<=p&&u<=h;){const o=e[p],i=t[h]=c?jr(t[h]):Nr(t[h]);if(!Or(o,i))break;g(o,i,n,null,r,a,l,s,c),p--,h--}if(u>p){if(u<=h){const e=h+1,i=eh)for(;u<=p;)G(e[u],r,a,!0),u++;else{const f=u,v=u,m=new Map;for(u=v;u<=h;u++){const e=t[u]=c?jr(t[u]):Nr(t[u]);null!=e.key&&m.set(e.key,u)}let b,y=0;const x=h-v+1;let C=!1,w=0;const k=new Array(x);for(u=0;u=x){G(o,r,a,!0);continue}let i;if(null!=o.key)i=m.get(o.key);else for(b=v;b<=h;b++)if(0===k[b-v]&&Or(o,t[b])){i=b;break}void 0===i?G(o,r,a,!0):(k[i-v]=u+1,i>=w?w=i:C=!0,g(o,t[i],n,null,r,a,l,s,c),y++)}const S=C?function(e){const t=e.slice(),n=[0];let o,r,i,a,l;const s=e.length;for(o=0;o>1,e[n[l]]0&&(t[o]=n[i-1]),n[i]=o)}}for(i=n.length,a=n[i-1];i-- >0;)n[i]=a,a=t[a];return n}(k):i;for(b=S.length-1,u=x-1;u>=0;u--){const e=v+u,i=t[e],p=e+1{const{el:a,type:l,transition:s,children:c,shapeFlag:u}=e;if(6&u)q(e.component.subTree,t,o,r);else if(128&u)e.suspense.move(t,o,r);else if(64&u)l.move(e,t,o,ne);else if(l!==yr)if(l!==wr)if(2!==r&&1&u&&s)if(0===r)s.beforeEnter(a),n(a,t,o),Qo((()=>s.enter(a)),i);else{const{leave:e,delayLeave:r,afterLeave:i}=s,l=()=>n(a,t,o),c=()=>{e(a,(()=>{l(),i&&i()}))};r?r(a,l,c):c()}else n(a,t,o);else w(e,t,o);else{n(a,t,o);for(let e=0;e{const{type:i,props:a,ref:l,children:s,dynamicChildren:c,shapeFlag:u,patchFlag:d,dirs:p,cacheIndex:h}=e;if(-2===d&&(r=!1),null!=l&&jo(l,null,n,e,!0),null!=h&&(t.renderCache[h]=void 0),256&u)return void t.ctx.deactivate(e);const f=1&u&&p,v=!Rn(e);let m;if(v&&(m=a&&a.onVnodeBeforeUnmount)&&Ur(m,t,e),6&u)Q(e.component,n,o);else{if(128&u)return void e.suspense.unmount(n,o);f&&vn(e,null,t,"beforeUnmount"),64&u?e.type.remove(e,t,n,ne,o):c&&!c.hasOnce&&(i!==yr||d>0&&64&d)?Z(c,t,n,!1,!0):(i===yr&&384&d||!r&&16&u)&&Z(s,t,n),o&&X(e)}(v&&(m=a&&a.onVnodeUnmounted)||f)&&Qo((()=>{m&&Ur(m,t,e),f&&vn(e,null,t,"unmounted")}),n)},X=e=>{const{type:t,el:n,anchor:r,transition:i}=e;if(t===yr)return void Y(n,r);if(t===wr)return void k(e);const a=()=>{o(n),i&&!i.persisted&&i.afterLeave&&i.afterLeave()};if(1&e.shapeFlag&&i&&!i.persisted){const{leave:t,delayLeave:o}=i,r=()=>t(n,a);o?o(e.el,a,r):r()}else a()},Y=(e,t)=>{let n;for(;e!==t;)n=f(e),o(e),e=n;o(t)},Q=(e,t,n)=>{const{bum:o,scope:r,update:i,subTree:a,um:l,m:s,a:c}=e;or(s),or(c),o&&W(o),r.stop(),i&&(i.active=!1,G(a,e,t,n)),l&&Qo(l,t),Qo((()=>{e.isUnmounted=!0}),t),t&&t.pendingBranch&&!t.isUnmounted&&e.asyncDep&&!e.asyncResolved&&e.suspenseId===t.pendingId&&(t.deps--,0===t.deps&&t.resolve())},Z=(e,t,n,o=!1,r=!1,i=0)=>{for(let a=i;a{if(6&e.shapeFlag)return J(e.component.subTree);if(128&e.shapeFlag)return e.suspense.next();const t=f(e.anchor||e.el),n=t&&t[Ho];return n?f(n):t};let ee=!1;const te=(e,t,n)=>{null==e?t._vnode&&G(t._vnode,null,null,!0):g(t._vnode||null,e,t,null,null,null,n),t._vnode=e,ee||(ee=!0,rn(),an(),ee=!1)},ne={p:g,um:G,m:q,r:X,mt:I,mc:T,pc:j,pbc:R,n:J,o:e};let oe,re;return t&&([oe,re]=t(ne)),{render:te,hydrate:oe,createApp:ko(te,oe)}}(e)}function Jo({type:e,props:t},n){return"svg"===n&&"foreignObject"===e||"mathml"===n&&"annotation-xml"===e&&t&&t.encoding&&t.encoding.includes("html")?void 0:n}function er({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function tr(e,t,n=!1){const o=e.children,r=t.children;if(w(o)&&w(r))for(let i=0;i{e(...t),P()}}const u=Kr,d=e=>!0===o?e:dr(e,!1===o?1:void 0);let p,h,f=!1,v=!1;if(Rt(e)?(p=()=>e.value,f=Ct(e)):yt(e)?(p=()=>d(e),f=!0):w(e)?(v=!0,f=e.some((e=>yt(e)||Ct(e))),p=()=>e.map((e=>Rt(e)?e.value:yt(e)?d(e):_(e)?Wt(e,u,2):void 0))):p=_(e)?t?()=>Wt(e,u,2):()=>(h&&h(),Ut(e,u,3,[g])):a,t&&o){const e=p;p=()=>dr(e())}let m,g=e=>{h=k.onStop=()=>{Wt(e,u,4),h=k.onStop=void 0}};if(ei){if(g=a,t?n&&Ut(t,u,3,[p(),v?[]:void 0,g]):p(),"sync"!==i)return a;{const e=Po(rr);m=e.__watcherHandles||(e.__watcherHandles=[])}}let b=v?new Array(e.length).fill(ar):ar;const x=()=>{if(k.active&&k.dirty)if(t){const e=k.run();(o||f||(v?e.some(((e,t)=>H(e,b[t]))):H(e,b)))&&(h&&h(),Ut(t,u,3,[e,b===ar?void 0:v&&b[0]===ar?[]:b,g]),b=e)}else k.run()};let C;x.allowRecurse=!!t,"sync"===i?C=x:"post"===i?C=()=>Qo(x,u&&u.suspense):(x.pre=!0,u&&(x.id=u.uid),C=()=>nn(x));const k=new pe(p,a,C),S=ue(),P=()=>{k.stop(),S&&y(S.effects,k)};return t?n?x():b=k.run():"post"===i?Qo(k.run.bind(k),u&&u.suspense):k.run(),m&&m.push(P),P}function cr(e,t,n){const o=this.proxy,r=P(e)?e.includes(".")?ur(o,e):()=>o[e]:e.bind(o,o);let i;_(t)?i=t:(i=t.handler,n=t);const a=Qr(this),l=sr(r,i.bind(o),n);return a(),l}function ur(e,t){const n=t.split(".");return()=>{let t=e;for(let e=0;e{dr(e,t,n)}));else if(O(e)){for(const o in e)dr(e[o],t,n);for(const o of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,o)&&dr(e[o],t,n)}return e}function pr(e,t,...n){if(e.isUnmounted)return;const o=e.vnode.props||r;let i=n;const a=t.startsWith("update:"),l=a&&((e,t)=>"modelValue"===t||"model-value"===t?e.modelModifiers:e[`${t}Modifiers`]||e[`${B(t)}Modifiers`]||e[`${$(t)}Modifiers`])(o,t.slice(7));let s;l&&(l.trim&&(i=n.map((e=>P(e)?e.trim():e))),l.number&&(i=n.map(V)));let c=o[s=j(t)]||o[s=j(B(t))];!c&&a&&(c=o[s=j($(t))]),c&&Ut(c,e,6,i);const u=o[s+"Once"];if(u){if(e.emitted){if(e.emitted[s])return}else e.emitted={};e.emitted[s]=!0,Ut(u,e,6,i)}}function hr(e,t,n=!1){const o=t.emitsCache,r=o.get(e);if(void 0!==r)return r;const i=e.emits;let a={},l=!1;if(!_(e)){const o=e=>{const n=hr(e,t,!0);n&&(l=!0,b(a,n))};!n&&t.mixins.length&&t.mixins.forEach(o),e.extends&&o(e.extends),e.mixins&&e.mixins.forEach(o)}return i||l?(w(i)?i.forEach((e=>a[e]=null)):b(a,i),A(e)&&o.set(e,a),a):(A(e)&&o.set(e,null),null)}function fr(e,t){return!(!e||!s(t))&&(t=t.slice(2).replace(/Once$/,""),C(e,t[0].toLowerCase()+t.slice(1))||C(e,$(t))||C(e,t))}function vr(e){const{type:t,vnode:n,proxy:o,withProxy:r,propsOptions:[i],slots:a,attrs:l,emit:s,render:c,renderCache:d,props:p,data:h,setupState:f,ctx:v,inheritAttrs:m}=e,g=pn(e);let b,y;try{if(4&n.shapeFlag){const e=r||o,t=e;b=Nr(c.call(t,e,d,p,f,h,v)),y=l}else{const e=t;b=Nr(e.length>1?e(p,{attrs:l,slots:a,emit:s}):e(p,null)),y=t.props?l:mr(l)}}catch(C){kr.length=0,Vt(C,e,1),b=Lr(Cr)}let x=b;if(y&&!1!==m){const e=Object.keys(y),{shapeFlag:t}=x;e.length&&7&t&&(i&&e.some(u)&&(y=gr(y,i)),x=Br(x,y,!1,!0))}return n.dirs&&(x=Br(x,null,!1,!0),x.dirs=x.dirs?x.dirs.concat(n.dirs):n.dirs),n.transition&&(x.transition=n.transition),b=x,pn(g),b}const mr=e=>{let t;for(const n in e)("class"===n||"style"===n||s(n))&&((t||(t={}))[n]=e[n]);return t},gr=(e,t)=>{const n={};for(const o in e)u(o)&&o.slice(9)in t||(n[o]=e[o]);return n};function br(e,t,n){const o=Object.keys(t);if(o.length!==Object.keys(e).length)return!0;for(let r=0;r0?Sr||i:null,kr.pop(),Sr=kr[kr.length-1]||null,Pr>0&&Sr&&Sr.push(e),e}function zr(e,t,n,o,r,i){return Ar(Ir(e,t,n,o,r,i,!0))}function Rr(e,t,n,o,r){return Ar(Lr(e,t,n,o,r,!0))}function Er(e){return!!e&&!0===e.__v_isVNode}function Or(e,t){return e.type===t.type&&e.key===t.key}const Mr=({key:e})=>null!=e?e:null,Fr=({ref:e,ref_key:t,ref_for:n})=>("number"==typeof e&&(e=""+e),null!=e?P(e)||Rt(e)||_(e)?{i:un,r:e,k:t,f:!!n}:e:null);function Ir(e,t=null,n=null,o=0,r=null,i=(e===yr?0:1),a=!1,l=!1){const s={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Mr(t),ref:t&&Fr(t),scopeId:dn,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:o,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:un};return l?(Hr(s,n),128&i&&e.normalize(s)):n&&(s.shapeFlag|=P(n)?8:16),Pr>0&&!a&&Sr&&(s.patchFlag>0||6&i)&&32!==s.patchFlag&&Sr.push(s),s}const Lr=function(e,t=null,n=null,o=0,r=null,i=!1){if(e&&e!==Yn||(e=Cr),Er(e)){const o=Br(e,t,!0);return n&&Hr(o,n),Pr>0&&!i&&Sr&&(6&o.shapeFlag?Sr[Sr.indexOf(e)]=o:Sr.push(o)),o.patchFlag=-2,o}var a;if(_(a=e)&&"__vccOpts"in a&&(e=e.__vccOpts),t){t=function(e){return e?wt(e)||zo(e)?b({},e):e:null}(t);let{class:e,style:n}=t;e&&!P(e)&&(t.class=J(e)),A(n)&&(wt(n)&&!w(n)&&(n=b({},n)),t.style=G(n))}const l=P(e)?1:(e=>e.__isSuspense)(e)?128:(e=>e.__isTeleport)(e)?64:A(e)?4:_(e)?2:0;return Ir(e,t,n,o,r,l,i,!0)};function Br(e,t,n=!1,o=!1){const{props:r,ref:i,patchFlag:a,children:l,transition:s}=e,c=t?Wr(r||{},t):r,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&Mr(c),ref:t&&t.ref?n&&i?w(i)?i.concat(Fr(t)):[i,Fr(t)]:Fr(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:l,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==yr?-1===a?16:16|a:a,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:s,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Br(e.ssContent),ssFallback:e.ssFallback&&Br(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return s&&o&&Tn(u,s.clone(u)),u}function Dr(e=" ",t=0){return Lr(xr,null,e,t)}function $r(e="",t=!1){return t?(_r(),Rr(Cr,null,e)):Lr(Cr,null,e)}function Nr(e){return null==e||"boolean"==typeof e?Lr(Cr):w(e)?Lr(yr,null,e.slice()):"object"==typeof e?jr(e):Lr(xr,null,String(e))}function jr(e){return null===e.el&&-1!==e.patchFlag||e.memo?e:Br(e)}function Hr(e,t){let n=0;const{shapeFlag:o}=e;if(null==t)t=null;else if(w(t))n=16;else if("object"==typeof t){if(65&o){const n=t.default;return void(n&&(n._c&&(n._d=!1),Hr(e,n()),n._c&&(n._d=!0)))}{n=32;const o=t._;o||zo(t)?3===o&&un&&(1===un.slots._?t._=1:(t._=2,e.patchFlag|=1024)):t._ctx=un}}else _(t)?(t={default:t,_ctx:un},n=32):(t=String(t),64&o?(n=16,t=[Dr(t)]):n=8);e.children=t,e.shapeFlag|=n}function Wr(...e){const t={};for(let n=0;nKr||un;let Xr,Yr;{const e=K(),t=(t,n)=>{let o;return(o=e[t])||(o=e[t]=[]),o.push(n),e=>{o.length>1?o.forEach((t=>t(e))):o[0](e)}};Xr=t("__VUE_INSTANCE_SETTERS__",(e=>Kr=e)),Yr=t("__VUE_SSR_SETTERS__",(e=>ei=e))}const Qr=e=>{const t=Kr;return Xr(e),e.scope.on(),()=>{e.scope.off(),Xr(t)}},Zr=()=>{Kr&&Kr.scope.off(),Xr(null)};function Jr(e){return 4&e.vnode.shapeFlag}let ei=!1;function ti(e,t,n){_(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:A(t)&&(e.setupState=Bt(t)),ni(e)}function ni(e,t,n){const o=e.type;e.render||(e.render=o.render||a);{const t=Qr(e);ye();try{co(e)}finally{xe(),t()}}}const oi={get:(e,t)=>(Re(e,0,""),e[t])};function ri(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(Bt(St(e.exposed)),{get:(t,n)=>n in t?t[n]:n in ro?ro[n](e):void 0,has:(e,t)=>t in e||t in ro})):e.proxy}function ii(e,t=!0){return _(e)?e.displayName||e.name:e.name||t&&e.__name}const ai=(e,t)=>{const n=function(e,t,n=!1){let o,r;const i=_(e);return i?(o=e,r=a):(o=e.get,r=e.set),new Tt(o,r,i||!r,n)}(e,0,ei);return n};function li(e,t,n){const o=arguments.length;return 2===o?A(t)&&!w(t)?Er(t)?Lr(e,null,[t]):Lr(e,t):Lr(e,null,t):(o>3?n=Array.prototype.slice.call(arguments,2):3===o&&Er(n)&&(n=[n]),Lr(e,t,n))}const si="3.4.38",ci="undefined"!=typeof document?document:null,ui=ci&&ci.createElement("template"),di={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,o)=>{const r="svg"===t?ci.createElementNS("http://www.w3.org/2000/svg",e):"mathml"===t?ci.createElementNS("http://www.w3.org/1998/Math/MathML",e):n?ci.createElement(e,{is:n}):ci.createElement(e);return"select"===e&&o&&null!=o.multiple&&r.setAttribute("multiple",o.multiple),r},createText:e=>ci.createTextNode(e),createComment:e=>ci.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>ci.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,o,r,i){const a=n?n.previousSibling:t.lastChild;if(r&&(r===i||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),r!==i&&(r=r.nextSibling););else{ui.innerHTML="svg"===o?`${e}`:"mathml"===o?`${e}`:e;const r=ui.content;if("svg"===o||"mathml"===o){const e=r.firstChild;for(;e.firstChild;)r.appendChild(e.firstChild);r.removeChild(e)}t.insertBefore(r,n)}return[a?a.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},pi="transition",hi="animation",fi=Symbol("_vtc"),vi=(e,{slots:t})=>li(wn,xi(e),t);vi.displayName="Transition";const mi={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},gi=vi.props=b({},xn,mi),bi=(e,t=[])=>{w(e)?e.forEach((e=>e(...t))):e&&e(...t)},yi=e=>!!e&&(w(e)?e.some((e=>e.length>1)):e.length>1);function xi(e){const t={};for(const b in e)b in mi||(t[b]=e[b]);if(!1===e.css)return t;const{name:n="v",type:o,duration:r,enterFromClass:i=`${n}-enter-from`,enterActiveClass:a=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:s=i,appearActiveClass:c=a,appearToClass:u=l,leaveFromClass:d=`${n}-leave-from`,leaveActiveClass:p=`${n}-leave-active`,leaveToClass:h=`${n}-leave-to`}=e,f=function(e){if(null==e)return null;if(A(e))return[Ci(e.enter),Ci(e.leave)];{const t=Ci(e);return[t,t]}}(r),v=f&&f[0],m=f&&f[1],{onBeforeEnter:g,onEnter:y,onEnterCancelled:x,onLeave:C,onLeaveCancelled:w,onBeforeAppear:k=g,onAppear:S=y,onAppearCancelled:_=x}=t,P=(e,t,n)=>{ki(e,t?u:l),ki(e,t?c:a),n&&n()},T=(e,t)=>{e._isLeaving=!1,ki(e,d),ki(e,h),ki(e,p),t&&t()},z=e=>(t,n)=>{const r=e?S:y,a=()=>P(t,e,n);bi(r,[t,a]),Si((()=>{ki(t,e?s:i),wi(t,e?u:l),yi(r)||Pi(t,o,v,a)}))};return b(t,{onBeforeEnter(e){bi(g,[e]),wi(e,i),wi(e,a)},onBeforeAppear(e){bi(k,[e]),wi(e,s),wi(e,c)},onEnter:z(!1),onAppear:z(!0),onLeave(e,t){e._isLeaving=!0;const n=()=>T(e,t);wi(e,d),wi(e,p),Ri(),Si((()=>{e._isLeaving&&(ki(e,d),wi(e,h),yi(C)||Pi(e,o,m,n))})),bi(C,[e,n])},onEnterCancelled(e){P(e,!1),bi(x,[e])},onAppearCancelled(e){P(e,!0),bi(_,[e])},onLeaveCancelled(e){T(e),bi(w,[e])}})}function Ci(e){const t=(e=>{const t=P(e)?Number(e):NaN;return isNaN(t)?e:t})(e);return t}function wi(e,t){t.split(/\s+/).forEach((t=>t&&e.classList.add(t))),(e[fi]||(e[fi]=new Set)).add(t)}function ki(e,t){t.split(/\s+/).forEach((t=>t&&e.classList.remove(t)));const n=e[fi];n&&(n.delete(t),n.size||(e[fi]=void 0))}function Si(e){requestAnimationFrame((()=>{requestAnimationFrame(e)}))}let _i=0;function Pi(e,t,n,o){const r=e._endId=++_i,i=()=>{r===e._endId&&o()};if(n)return setTimeout(i,n);const{type:a,timeout:l,propCount:s}=Ti(e,t);if(!a)return o();const c=a+"end";let u=0;const d=()=>{e.removeEventListener(c,p),i()},p=t=>{t.target===e&&++u>=s&&d()};setTimeout((()=>{u(n[e]||"").split(", "),r=o(`${pi}Delay`),i=o(`${pi}Duration`),a=Ai(r,i),l=o(`${hi}Delay`),s=o(`${hi}Duration`),c=Ai(l,s);let u=null,d=0,p=0;return t===pi?a>0&&(u=pi,d=a,p=i.length):t===hi?c>0&&(u=hi,d=c,p=s.length):(d=Math.max(a,c),u=d>0?a>c?pi:hi:null,p=u?u===pi?i.length:s.length:0),{type:u,timeout:d,propCount:p,hasTransform:u===pi&&/\b(transform|all)(,|$)/.test(o(`${pi}Property`).toString())}}function Ai(e,t){for(;e.lengthzi(t)+zi(e[n]))))}function zi(e){return"auto"===e?0:1e3*Number(e.slice(0,-1).replace(",","."))}function Ri(){return document.body.offsetHeight}const Ei=Symbol("_vod"),Oi=Symbol("_vsh"),Mi={beforeMount(e,{value:t},{transition:n}){e[Ei]="none"===e.style.display?"":e.style.display,n&&t?n.beforeEnter(e):Fi(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:o}){!t!=!n&&(o?t?(o.beforeEnter(e),Fi(e,!0),o.enter(e)):o.leave(e,(()=>{Fi(e,!1)})):Fi(e,t))},beforeUnmount(e,{value:t}){Fi(e,t)}};function Fi(e,t){e.style.display=t?e[Ei]:"none",e[Oi]=!t}const Ii=Symbol(""),Li=/(^|;)\s*display\s*:/,Bi=/\s*!important$/;function Di(e,t,n){if(w(n))n.forEach((n=>Di(e,t,n)));else if(null==n&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const o=function(e,t){const n=Ni[t];if(n)return n;let o=B(t);if("filter"!==o&&o in e)return Ni[t]=o;o=N(o);for(let r=0;r<$i.length;r++){const n=$i[r]+o;if(n in e)return Ni[t]=n}return t}(e,t);Bi.test(n)?e.setProperty($(o),n.replace(Bi,""),"important"):e[o]=n}}const $i=["Webkit","Moz","ms"],Ni={},ji="http://www.w3.org/1999/xlink";function Hi(e,t,n,o,r,i=ee(t)){o&&t.startsWith("xlink:")?null==n?e.removeAttributeNS(ji,t.slice(6,t.length)):e.setAttributeNS(ji,t,n):null==n||i&&!te(n)?e.removeAttribute(t):e.setAttribute(t,i?"":T(n)?String(n):n)}function Wi(e,t,n,o){e.addEventListener(t,n,o)}const Ui=Symbol("_vei");function Vi(e,t,n,o,r=null){const i=e[Ui]||(e[Ui]={}),a=i[t];if(o&&a)a.value=o;else{const[n,l]=function(e){let t;if(qi.test(e)){let n;for(t={};n=e.match(qi);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}const n=":"===e[2]?e.slice(3):$(e.slice(2));return[n,t]}(t);if(o){const a=i[t]=function(e,t){const n=e=>{if(e._vts){if(e._vts<=n.attached)return}else e._vts=Date.now();Ut(function(e,t){if(w(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map((e=>t=>!t._stopped&&e&&e(t)))}return t}(e,n.value),t,5,[e])};return n.value=e,n.attached=Ki||(Gi.then((()=>Ki=0)),Ki=Date.now()),n}(o,r);Wi(e,n,a,l)}else a&&(function(e,t,n,o){e.removeEventListener(t,n,o)}(e,n,a,l),i[t]=void 0)}}const qi=/(?:Once|Passive|Capture)$/;let Ki=0;const Gi=Promise.resolve(),Xi=e=>111===e.charCodeAt(0)&&110===e.charCodeAt(1)&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Yi=new WeakMap,Qi=new WeakMap,Zi=Symbol("_moveCb"),Ji=Symbol("_enterCb"),ea={name:"TransitionGroup",props:b({},gi,{tag:String,moveClass:String}),setup(e,{slots:t}){const n=Gr(),o=bn();let r,i;return jn((()=>{if(!r.length)return;const t=e.moveClass||`${e.name||"v"}-move`;if(!function(e,t,n){const o=e.cloneNode(),r=e[fi];r&&r.forEach((e=>{e.split(/\s+/).forEach((e=>e&&o.classList.remove(e)))})),n.split(/\s+/).forEach((e=>e&&o.classList.add(e))),o.style.display="none";const i=1===t.nodeType?t:t.parentNode;i.appendChild(o);const{hasTransform:a}=Ti(o);return i.removeChild(o),a}(r[0].el,n.vnode.el,t))return;r.forEach(na),r.forEach(oa);const o=r.filter(ra);Ri(),o.forEach((e=>{const n=e.el,o=n.style;wi(n,t),o.transform=o.webkitTransform=o.transitionDuration="";const r=n[Zi]=e=>{e&&e.target!==n||e&&!/transform$/.test(e.propertyName)||(n.removeEventListener("transitionend",r),n[Zi]=null,ki(n,t))};n.addEventListener("transitionend",r)}))})),()=>{const a=kt(e),l=xi(a);let s=a.tag||yr;if(r=[],i)for(let e=0;e{const t=e.props["onUpdate:modelValue"]||!1;return w(t)?e=>W(t,e):t};function aa(e){e.target.composing=!0}function la(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const sa=Symbol("_assign"),ca={created(e,{modifiers:{lazy:t,trim:n,number:o}},r){e[sa]=ia(r);const i=o||r.props&&"number"===r.props.type;Wi(e,t?"change":"input",(t=>{if(t.target.composing)return;let o=e.value;n&&(o=o.trim()),i&&(o=V(o)),e[sa](o)})),n&&Wi(e,"change",(()=>{e.value=e.value.trim()})),t||(Wi(e,"compositionstart",aa),Wi(e,"compositionend",la),Wi(e,"change",la))},mounted(e,{value:t}){e.value=null==t?"":t},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:o,trim:r,number:i}},a){if(e[sa]=ia(a),e.composing)return;const l=null==t?"":t;if((!i&&"number"!==e.type||/^0\d/.test(e.value)?e.value:V(e.value))!==l){if(document.activeElement===e&&"range"!==e.type){if(o&&t===n)return;if(r&&e.value.trim()===l)return}e.value=l}}},ua=["ctrl","shift","alt","meta"],da={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&0!==e.button,middle:e=>"button"in e&&1!==e.button,right:e=>"button"in e&&2!==e.button,exact:(e,t)=>ua.some((n=>e[`${n}Key`]&&!t.includes(n)))},pa=(e,t)=>{const n=e._withMods||(e._withMods={}),o=t.join(".");return n[o]||(n[o]=(n,...o)=>{for(let e=0;e{const n=e._withKeys||(e._withKeys={}),o=t.join(".");return n[o]||(n[o]=n=>{if(!("key"in n))return;const o=$(n.key);return t.some((e=>e===o||ha[e]===o))?e(n):void 0})},va=b({patchProp:(e,t,n,o,r,i)=>{const a="svg"===r;"class"===t?function(e,t,n){const o=e[fi];o&&(t=(t?[t,...o]:[...o]).join(" ")),null==t?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}(e,o,a):"style"===t?function(e,t,n){const o=e.style,r=P(n);let i=!1;if(n&&!r){if(t)if(P(t))for(const e of t.split(";")){const t=e.slice(0,e.indexOf(":")).trim();null==n[t]&&Di(o,t,"")}else for(const e in t)null==n[e]&&Di(o,e,"");for(const e in n)"display"===e&&(i=!0),Di(o,e,n[e])}else if(r){if(t!==n){const e=o[Ii];e&&(n+=";"+e),o.cssText=n,i=Li.test(n)}}else t&&e.removeAttribute("style");Ei in e&&(e[Ei]=i?o.display:"",e[Oi]&&(o.display="none"))}(e,n,o):s(t)?u(t)||Vi(e,t,0,o,i):("."===t[0]?(t=t.slice(1),1):"^"===t[0]?(t=t.slice(1),0):function(e,t,n,o){if(o)return"innerHTML"===t||"textContent"===t||!!(t in e&&Xi(t)&&_(n));if("spellcheck"===t||"draggable"===t||"translate"===t)return!1;if("form"===t)return!1;if("list"===t&&"INPUT"===e.tagName)return!1;if("type"===t&&"TEXTAREA"===e.tagName)return!1;if("width"===t||"height"===t){const t=e.tagName;if("IMG"===t||"VIDEO"===t||"CANVAS"===t||"SOURCE"===t)return!1}return(!Xi(t)||!P(n))&&t in e}(e,t,o,a))?(function(e,t,n,o){if("innerHTML"===t||"textContent"===t){if(null==n)return;return void(e[t]=n)}const r=e.tagName;if("value"===t&&"PROGRESS"!==r&&!r.includes("-")){const o="OPTION"===r?e.getAttribute("value")||"":e.value,i=null==n?"":String(n);return o===i&&"_value"in e||(e.value=i),null==n&&e.removeAttribute(t),void(e._value=n)}let i=!1;if(""===n||null==n){const o=typeof e[t];"boolean"===o?n=te(n):null==n&&"string"===o?(n="",i=!0):"number"===o&&(n=0,i=!0)}try{e[t]=n}catch(a){}i&&e.removeAttribute(t)}(e,t,o),e.tagName.includes("-")||"value"!==t&&"checked"!==t&&"selected"!==t||Hi(e,t,o,a,0,"value"!==t)):("true-value"===t?e._trueValue=o:"false-value"===t&&(e._falseValue=o),Hi(e,t,o,a))}},di);let ma;const ga=(...e)=>{const t=(ma||(ma=Zo(va))).createApp(...e),{mount:n}=t;return t.mount=e=>{const o=function(e){return P(e)?document.querySelector(e):e}(e);if(!o)return;const r=t._component;_(r)||r.render||r.template||(r.template=o.innerHTML),o.innerHTML="";const i=n(o,!1,function(e){return e instanceof SVGElement?"svg":"function"==typeof MathMLElement&&e instanceof MathMLElement?"mathml":void 0}(o));return o instanceof Element&&(o.removeAttribute("v-cloak"),o.setAttribute("data-v-app","")),i},t},ba="undefined"!=typeof document,ya=Object.assign;function xa(e,t){const n={};for(const o in t){const r=t[o];n[o]=wa(r)?r.map(e):e(r)}return n}const Ca=()=>{},wa=Array.isArray,ka=/#/g,Sa=/&/g,_a=/\//g,Pa=/=/g,Ta=/\?/g,Aa=/\+/g,za=/%5B/g,Ra=/%5D/g,Ea=/%5E/g,Oa=/%60/g,Ma=/%7B/g,Fa=/%7C/g,Ia=/%7D/g,La=/%20/g;function Ba(e){return encodeURI(""+e).replace(Fa,"|").replace(za,"[").replace(Ra,"]")}function Da(e){return Ba(e).replace(Aa,"%2B").replace(La,"+").replace(ka,"%23").replace(Sa,"%26").replace(Oa,"`").replace(Ma,"{").replace(Ia,"}").replace(Ea,"^")}function $a(e){return null==e?"":function(e){return Ba(e).replace(ka,"%23").replace(Ta,"%3F")}(e).replace(_a,"%2F")}function Na(e){try{return decodeURIComponent(""+e)}catch(t){}return""+e}const ja=/\/$/;function Ha(e,t,n="/"){let o,r={},i="",a="";const l=t.indexOf("#");let s=t.indexOf("?");return l=0&&(s=-1),s>-1&&(o=t.slice(0,s),i=t.slice(s+1,l>-1?l:t.length),r=e(i)),l>-1&&(o=o||t.slice(0,l),a=t.slice(l,t.length)),o=function(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),o=e.split("/"),r=o[o.length-1];".."!==r&&"."!==r||o.push("");let i,a,l=n.length-1;for(i=0;i1&&l--}return n.slice(0,l).join("/")+"/"+o.slice(i).join("/")}(null!=o?o:t,n),{fullPath:o+(i&&"?")+i+a,path:o,query:r,hash:Na(a)}}function Wa(e,t){return t&&e.toLowerCase().startsWith(t.toLowerCase())?e.slice(t.length)||"/":e}function Ua(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Va(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!qa(e[n],t[n]))return!1;return!0}function qa(e,t){return wa(e)?Ka(e,t):wa(t)?Ka(t,e):e===t}function Ka(e,t){return wa(t)?e.length===t.length&&e.every(((e,n)=>e===t[n])):1===e.length&&e[0]===t}const Ga={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};var Xa,Ya,Qa,Za;function Ja(e){if(!e)if(ba){const t=document.querySelector("base");e=(e=t&&t.getAttribute("href")||"/").replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return"/"!==e[0]&&"#"!==e[0]&&(e="/"+e),e.replace(ja,"")}(Ya=Xa||(Xa={})).pop="pop",Ya.push="push",(Za=Qa||(Qa={})).back="back",Za.forward="forward",Za.unknown="";const el=/^[^#]+#/;function tl(e,t){return e.replace(el,"#")+t}const nl=()=>({left:window.scrollX,top:window.scrollY});function ol(e){let t;if("el"in e){const n=e.el,o="string"==typeof n&&n.startsWith("#"),r="string"==typeof n?o?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!r)return;t=function(e,t){const n=document.documentElement.getBoundingClientRect(),o=e.getBoundingClientRect();return{behavior:t.behavior,left:o.left-n.left-(t.left||0),top:o.top-n.top-(t.top||0)}}(r,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(null!=t.left?t.left:window.scrollX,null!=t.top?t.top:window.scrollY)}function rl(e,t){return(history.state?history.state.position-t:-1)+e}const il=new Map;function al(e,t){const{pathname:n,search:o,hash:r}=t,i=e.indexOf("#");if(i>-1){let t=r.includes(e.slice(i))?e.slice(i).length:1,n=r.slice(t);return"/"!==n[0]&&(n="/"+n),Wa(n,"")}return Wa(n,e)+o+r}function ll(e,t,n,o=!1,r=!1){return{back:e,current:t,forward:n,replaced:o,position:window.history.length,scroll:r?nl():null}}function sl(e){const{history:t,location:n}=window,o={value:al(e,n)},r={value:t.state};function i(o,i,a){const l=e.indexOf("#"),s=l>-1?(n.host&&document.querySelector("base")?e:e.slice(l))+o:location.protocol+"//"+location.host+e+o;try{t[a?"replaceState":"pushState"](i,"",s),r.value=i}catch(c){n[a?"replace":"assign"](s)}}return r.value||i(o.value,{back:null,current:o.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0),{location:o,state:r,push:function(e,n){const a=ya({},r.value,t.state,{forward:e,scroll:nl()});i(a.current,a,!0),i(e,ya({},ll(o.value,e,null),{position:a.position+1},n),!1),o.value=e},replace:function(e,n){i(e,ya({},t.state,ll(r.value.back,e,r.value.forward,!0),n,{position:r.value.position}),!0),o.value=e}}}function cl(e){const t=sl(e=Ja(e)),n=function(e,t,n,o){let r=[],i=[],a=null;const l=({state:i})=>{const l=al(e,location),s=n.value,c=t.value;let u=0;if(i){if(n.value=l,t.value=i,a&&a===s)return void(a=null);u=c?i.position-c.position:0}else o(l);r.forEach((e=>{e(n.value,s,{delta:u,type:Xa.pop,direction:u?u>0?Qa.forward:Qa.back:Qa.unknown})}))};function s(){const{history:e}=window;e.state&&e.replaceState(ya({},e.state,{scroll:nl()}),"")}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",s,{passive:!0}),{pauseListeners:function(){a=n.value},listen:function(e){r.push(e);const t=()=>{const t=r.indexOf(e);t>-1&&r.splice(t,1)};return i.push(t),t},destroy:function(){for(const e of i)e();i=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",s)}}}(e,t.state,t.location,t.replace),o=ya({location:"",base:e,go:function(e,t=!0){t||n.pauseListeners(),history.go(e)},createHref:tl.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function ul(e){return"string"==typeof e||"symbol"==typeof e}const dl=Symbol("");var pl,hl;function fl(e,t){return ya(new Error,{type:e,[dl]:!0},t)}function vl(e,t){return e instanceof Error&&dl in e&&(null==t||!!(e.type&t))}(hl=pl||(pl={}))[hl.aborted=4]="aborted",hl[hl.cancelled=8]="cancelled",hl[hl.duplicated=16]="duplicated";const ml="[^/]+?",gl={sensitive:!1,strict:!1,start:!0,end:!0},bl=/[.+*?^${}()[\]/\\]/g;function yl(e,t){let n=0;for(;nt.length?1===t.length&&80===t[0]?1:-1:0}function xl(e,t){let n=0;const o=e.score,r=t.score;for(;n0&&t[t.length-1]<0}const wl={type:0,value:""},kl=/[a-zA-Z0-9_]/;function Sl(e,t,n){const o=function(e,t){const n=ya({},gl,t),o=[];let r=n.start?"^":"";const i=[];for(const s of e){const e=s.length?[]:[90];n.strict&&!s.length&&(r+="/");for(let t=0;t1&&("*"===l||"+"===l)&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:c,regexp:u,repeatable:"*"===l||"+"===l,optional:"*"===l||"?"===l})):t("Invalid state to consume buffer"),c="")}function p(){c+=l}for(;s{i(p)}:Ca}function i(e){if(ul(e)){const t=o.get(e);t&&(o.delete(e),n.splice(n.indexOf(t),1),t.children.forEach(i),t.alias.forEach(i))}else{const t=n.indexOf(e);t>-1&&(n.splice(t,1),e.record.name&&o.delete(e.record.name),e.children.forEach(i),e.alias.forEach(i))}}function a(e){const t=function(e,t){let n=0,o=t.length;for(;n!==o;){const r=n+o>>1;xl(e,t[r])<0?o=r:n=r+1}const r=function(e){let t=e;for(;t=t.parent;)if(El(t)&&0===xl(e,t))return t}(e);return r&&(o=t.lastIndexOf(r,o-1)),o}(e,n);n.splice(t,0,e),e.record.name&&!Al(e)&&o.set(e.record.name,e)}return t=Rl({strict:!1,end:!0,sensitive:!1},t),e.forEach((e=>r(e))),{addRoute:r,resolve:function(e,t){let r,i,a,l={};if("name"in e&&e.name){if(r=o.get(e.name),!r)throw fl(1,{location:e});a=r.record.name,l=ya(Pl(t.params,r.keys.filter((e=>!e.optional)).concat(r.parent?r.parent.keys.filter((e=>e.optional)):[]).map((e=>e.name))),e.params&&Pl(e.params,r.keys.map((e=>e.name)))),i=r.stringify(l)}else if(null!=e.path)i=e.path,r=n.find((e=>e.re.test(i))),r&&(l=r.parse(i),a=r.record.name);else{if(r=t.name?o.get(t.name):n.find((e=>e.re.test(t.path))),!r)throw fl(1,{location:e,currentLocation:t});a=r.record.name,l=ya({},t.params,e.params),i=r.stringify(l)}const s=[];let c=r;for(;c;)s.unshift(c.record),c=c.parent;return{name:a,path:i,params:l,matched:s,meta:zl(s)}},removeRoute:i,clearRoutes:function(){n.length=0,o.clear()},getRoutes:function(){return n},getRecordMatcher:function(e){return o.get(e)}}}function Pl(e,t){const n={};for(const o of t)o in e&&(n[o]=e[o]);return n}function Tl(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const o in e.components)t[o]="object"==typeof n?n[o]:n;return t}function Al(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function zl(e){return e.reduce(((e,t)=>ya(e,t.meta)),{})}function Rl(e,t){const n={};for(const o in e)n[o]=o in t?t[o]:e[o];return n}function El({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function Ol(e){const t={};if(""===e||"?"===e)return t;const n=("?"===e[0]?e.slice(1):e).split("&");for(let o=0;oe&&Da(e))):[o&&Da(o)]).forEach((e=>{void 0!==e&&(t+=(t.length?"&":"")+n,null!=e&&(t+="="+e))})):void 0!==o&&(t+=(t.length?"&":"")+n)}return t}function Fl(e){const t={};for(const n in e){const o=e[n];void 0!==o&&(t[n]=wa(o)?o.map((e=>null==e?null:""+e)):null==o?o:""+o)}return t}const Il=Symbol(""),Ll=Symbol(""),Bl=Symbol(""),Dl=Symbol(""),$l=Symbol("");function Nl(){let e=[];return{add:function(t){return e.push(t),()=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)}},list:()=>e.slice(),reset:function(){e=[]}}}function jl(e,t,n,o,r,i=(e=>e())){const a=o&&(o.enterCallbacks[r]=o.enterCallbacks[r]||[]);return()=>new Promise(((l,s)=>{const c=e=>{var i;!1===e?s(fl(4,{from:n,to:t})):e instanceof Error?s(e):"string"==typeof(i=e)||i&&"object"==typeof i?s(fl(2,{from:t,to:e})):(a&&o.enterCallbacks[r]===a&&"function"==typeof e&&a.push(e),l())},u=i((()=>e.call(o&&o.instances[r],t,n,c)));let d=Promise.resolve(u);e.length<3&&(d=d.then(c)),d.catch((e=>s(e)))}))}function Hl(e,t,n,o,r=(e=>e())){const i=[];for(const l of e)for(const e in l.components){let s=l.components[e];if("beforeRouteEnter"===t||l.instances[e])if("object"==typeof(a=s)||"displayName"in a||"props"in a||"__vccOpts"in a){const a=(s.__vccOpts||s)[t];a&&i.push(jl(a,n,o,l,e,r))}else{let a=s();i.push((()=>a.then((i=>{if(!i)return Promise.reject(new Error(`Couldn't resolve component "${e}" at "${l.path}"`));const a=(s=i).__esModule||"Module"===s[Symbol.toStringTag]?i.default:i;var s;l.components[e]=a;const c=(a.__vccOpts||a)[t];return c&&jl(c,n,o,l,e,r)()}))))}}var a;return i}function Wl(e){const t=Po(Bl),n=Po(Dl),o=ai((()=>{const n=It(e.to);return t.resolve(n)})),r=ai((()=>{const{matched:e}=o.value,{length:t}=e,r=e[t-1],i=n.matched;if(!r||!i.length)return-1;const a=i.findIndex(Ua.bind(null,r));if(a>-1)return a;const l=Vl(e[t-2]);return t>1&&Vl(r)===l&&i[i.length-1].path!==l?i.findIndex(Ua.bind(null,e[t-2])):a})),i=ai((()=>r.value>-1&&function(e,t){for(const n in t){const o=t[n],r=e[n];if("string"==typeof o){if(o!==r)return!1}else if(!wa(r)||r.length!==o.length||o.some(((e,t)=>e!==r[t])))return!1}return!0}(n.params,o.value.params))),a=ai((()=>r.value>-1&&r.value===n.matched.length-1&&Va(n.params,o.value.params)));return{route:o,href:ai((()=>o.value.href)),isActive:i,isExactActive:a,navigate:function(n={}){return function(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||e.defaultPrevented||void 0!==e.button&&0!==e.button)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}(n)?t[It(e.replace)?"replace":"push"](It(e.to)).catch(Ca):Promise.resolve()}}}const Ul=zn({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:Wl,setup(e,{slots:t}){const n=vt(Wl(e)),{options:o}=Po(Bl),r=ai((()=>({[ql(e.activeClass,o.linkActiveClass,"router-link-active")]:n.isActive,[ql(e.exactActiveClass,o.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive})));return()=>{const o=t.default&&t.default(n);return e.custom?o:li("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:r.value},o)}}});function Vl(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const ql=(e,t,n)=>null!=e?e:null!=t?t:n;function Kl(e,t){if(!e)return null;const n=e(t);return 1===n.length?n[0]:n}const Gl=zn({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const o=Po($l),r=ai((()=>e.route||o.value)),i=Po(Ll,0),a=ai((()=>{let e=It(i);const{matched:t}=r.value;let n;for(;(n=t[e])&&!n.components;)e++;return e})),l=ai((()=>r.value.matched[a.value]));_o(Ll,ai((()=>a.value+1))),_o(Il,l),_o($l,r);const s=Et();return lr((()=>[s.value,l.value,e.name]),(([e,t,n],[o,r,i])=>{t&&(t.instances[n]=e,r&&r!==t&&e&&e===o&&(t.leaveGuards.size||(t.leaveGuards=r.leaveGuards),t.updateGuards.size||(t.updateGuards=r.updateGuards))),!e||!t||r&&Ua(t,r)&&o||(t.enterCallbacks[n]||[]).forEach((t=>t(e)))}),{flush:"post"}),()=>{const o=r.value,i=e.name,a=l.value,c=a&&a.components[i];if(!c)return Kl(n.default,{Component:c,route:o});const u=a.props[i],d=u?!0===u?o.params:"function"==typeof u?u(o):u:null,p=li(c,ya({},d,t,{onVnodeUnmounted:e=>{e.component.isUnmounted&&(a.instances[i]=null)},ref:s}));return Kl(n.default,{Component:p,route:o})||p}}});function Xl(){return Po(Bl)}function Yl(e){return Po(Dl)}const Ql={},Zl=function(e,t,n){if(!t||0===t.length)return e();const o=document.getElementsByTagName("link");return Promise.all(t.map((e=>{if((e=function(e){return"/"+e}(e))in Ql)return;Ql[e]=!0;const t=e.endsWith(".css"),r=t?'[rel="stylesheet"]':"";if(n)for(let n=o.length-1;n>=0;n--){const r=o[n];if(r.href===e&&(!t||"stylesheet"===r.rel))return}else if(document.querySelector(`link[href="${e}"]${r}`))return;const i=document.createElement("link");return i.rel=t?"stylesheet":"modulepreload",t||(i.as="script",i.crossOrigin=""),i.href=e,document.head.appendChild(i),t?new Promise(((t,n)=>{i.addEventListener("load",t),i.addEventListener("error",(()=>n(new Error(`Unable to preload CSS for ${e}`))))})):void 0}))).then((()=>e())).catch((e=>{const t=new Event("vite:preloadError",{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}))},Jl={name:"dashboard",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"dashboard",meta:{isHidden:!1},children:[{name:"dashboard",path:"/dashboard",component:()=>Zl((()=>Promise.resolve().then((()=>Hq))),void 0),meta:{title:"仪表盘",icon:"mdi:home",order:0}}]},es=Object.freeze(Object.defineProperty({__proto__:null,default:Jl},Symbol.toStringTag,{value:"Module"})),ts={name:"Invite",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/invite",meta:{isHidden:!1},children:[{name:"Invite",path:"invite",component:()=>Zl((()=>Promise.resolve().then((()=>bK))),void 0),meta:{title:"我的邀请",icon:"mdi:invite",order:1,group:{key:"finance",label:"财务"}}}]},ns=Object.freeze(Object.defineProperty({__proto__:null,default:ts},Symbol.toStringTag,{value:"Module"})),os={name:"knowledge",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/knowledge",meta:{isHidden:!1},children:[{name:"Knowledge",path:"knowledge",component:()=>Zl((()=>Promise.resolve().then((()=>SK))),void 0),meta:{title:"使用文档",icon:"mdi-book-open-variant",order:10}}]},rs=Object.freeze(Object.defineProperty({__proto__:null,default:os},Symbol.toStringTag,{value:"Module"})),is={name:"Node",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/node",meta:{isHidden:!1},children:[{name:"Node",path:"node",component:()=>Zl((()=>Promise.resolve().then((()=>WK))),void 0),meta:{title:"节点状态",icon:"mdi-check-circle-outline",order:11,group:{key:"subscribe",label:"订阅"}}}]},as=Object.freeze(Object.defineProperty({__proto__:null,default:is},Symbol.toStringTag,{value:"Module"})),ls={name:"Order",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/order",meta:{isHidden:!1},children:[{name:"Order",path:"order",component:()=>Zl((()=>Promise.resolve().then((()=>VK))),void 0),meta:{title:"我的订单",icon:"mdi-format-list-bulleted",order:0,group:{key:"finance",label:"财务"}}},{name:"OrderDetail",path:"order/:trade_no",component:()=>Zl((()=>Promise.resolve().then((()=>vX))),void 0),meta:{title:"订单详情",icon:"mdi:doc",order:1,isHidden:!0}}]},ss=Object.freeze(Object.defineProperty({__proto__:null,default:ls},Symbol.toStringTag,{value:"Module"})),cs={name:"plan",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/plan",meta:{isHidden:!1},children:[{name:"Plan",path:"plan",component:()=>Zl((()=>Promise.resolve().then((()=>MX))),void 0),meta:{title:"购买订阅",icon:"mdi-shopping-outline",order:10,group:{key:"subscribe",label:"订阅"}}},{name:"PlanDetail",path:"plan/:plan_id",component:()=>Zl((()=>Promise.resolve().then((()=>rY))),void 0),meta:{title:"配置订阅",icon:"mdi:doc",order:1,isHidden:!0}}]},us=Object.freeze(Object.defineProperty({__proto__:null,default:cs},Symbol.toStringTag,{value:"Module"})),ds={name:"profile",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/profile",meta:{isHidden:!1},children:[{name:"Profile",path:"profile",component:()=>Zl((()=>Promise.resolve().then((()=>PY))),void 0),meta:{title:"个人中心",icon:"mdi-account-outline",order:0,group:{key:"user",label:"用户"}}}]},ps=Object.freeze(Object.defineProperty({__proto__:null,default:ds},Symbol.toStringTag,{value:"Module"})),hs={name:"ticket",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/ticket",meta:{isHidden:!1},children:[{name:"Ticket",path:"ticket",component:()=>Zl((()=>Promise.resolve().then((()=>zY))),void 0),meta:{title:"我的工单",icon:"mdi-comment-alert-outline",order:0,group:{key:"user",label:"用户"}}},{name:"TicketDetail",path:"ticket/:ticket_id",component:()=>Zl((()=>Promise.resolve().then((()=>FY))),void 0),meta:{title:"工单详情",order:0,isHidden:!0}}]},fs=Object.freeze(Object.defineProperty({__proto__:null,default:hs},Symbol.toStringTag,{value:"Module"})),vs={name:"traffic",path:"/",component:()=>Zl((()=>Promise.resolve().then((()=>qj))),void 0),redirect:"/traffic",meta:{isHidden:!1},children:[{name:"Traffic",path:"traffic",component:()=>Zl((()=>Promise.resolve().then((()=>LY))),void 0),meta:{title:"流量明细",icon:"mdi-poll",order:0,group:{key:"user",label:"用户"}}}]},ms=Object.freeze(Object.defineProperty({__proto__:null,default:vs},Symbol.toStringTag,{value:"Module"})),gs=[{name:"Home",path:"/",redirect:"/dashboard",meta:{title:"首页",isHidden:!0}},{name:"404",path:"/404",component:()=>Zl((()=>Promise.resolve().then((()=>$Y))),void 0),meta:{title:"404",isHidden:!0}},{name:"LOGIN",path:"/login",component:()=>Zl((()=>Promise.resolve().then((()=>FQ))),void 0),meta:{title:"登录页",isHidden:!0}},{name:"Register",path:"/register",component:()=>Zl((()=>Promise.resolve().then((()=>FQ))),void 0),meta:{title:"注册",isHidden:!0}},{name:"forgetpassword",path:"/forgetpassword",component:()=>Zl((()=>Promise.resolve().then((()=>FQ))),void 0),meta:{title:"重置密码",isHidden:!0}}],bs={name:"NotFound",path:"/:pathMatch(.*)*",redirect:"/404",meta:{title:"Not Found"}},ys=Object.assign({"/src/views/dashboard/route.ts":es,"/src/views/invite/route.ts":ns,"/src/views/knowledge/route.ts":rs,"/src/views/node/route.ts":as,"/src/views/order/route.ts":ss,"/src/views/plan/route.ts":us,"/src/views/profile/route.ts":ps,"/src/views/ticket/route.ts":fs,"/src/views/traffic/route.ts":ms}),xs=[];Object.keys(ys).forEach((e=>{xs.push(ys[e].default)}));const Cs=(null==(n=window.settings)?void 0:n.title)||"Xboard";let ws;const ks=e=>ws=e,Ss=Symbol();function _s(e){return e&&"object"==typeof e&&"[object Object]"===Object.prototype.toString.call(e)&&"function"!=typeof e.toJSON}var Ps,Ts;(Ts=Ps||(Ps={})).direct="direct",Ts.patchObject="patch object",Ts.patchFunction="patch function";const As=()=>{};function zs(e,t,n,o=As){e.push(t);const r=()=>{const n=e.indexOf(t);n>-1&&(e.splice(n,1),o())};return!n&&ue()&&de(r),r}function Rs(e,...t){e.slice().forEach((e=>{e(...t)}))}const Es=e=>e(),Os=Symbol(),Ms=Symbol();function Fs(e,t){e instanceof Map&&t instanceof Map?t.forEach(((t,n)=>e.set(n,t))):e instanceof Set&&t instanceof Set&&t.forEach(e.add,e);for(const n in t){if(!t.hasOwnProperty(n))continue;const o=t[n],r=e[n];_s(r)&&_s(o)&&e.hasOwnProperty(n)&&!Rt(o)&&!yt(o)?e[n]=Fs(r,o):e[n]=o}return e}const Is=Symbol(),{assign:Ls}=Object;function Bs(e,t,n,o){const{state:r,actions:i,getters:a}=t,l=n.state.value[e];let s;return s=Ds(e,(function(){l||(n.state.value[e]=r?r():{});const t=function(e){const t=w(e)?new Array(e.length):{};for(const n in e)t[n]=Ht(e,n);return t}(n.state.value[e]);return Ls(t,i,Object.keys(a||{}).reduce(((t,o)=>(t[o]=St(ai((()=>{ks(n);const t=n._s.get(e);return a[o].call(t,t)}))),t)),{}))}),t,n,0,!0),s}function Ds(e,t,n={},o,r,i){let a;const l=Ls({actions:{}},n),s={deep:!0};let c,u,d,p=[],h=[];const f=o.state.value[e];let v;function m(t){let n;c=u=!1,"function"==typeof t?(t(o.state.value[e]),n={type:Ps.patchFunction,storeId:e,events:d}):(Fs(o.state.value[e],t),n={type:Ps.patchObject,payload:t,storeId:e,events:d});const r=v=Symbol();tn().then((()=>{v===r&&(c=!0)})),u=!0,Rs(p,n,o.state.value[e])}i||f||(o.state.value[e]={}),Et({});const g=i?function(){const{state:e}=n,t=e?e():{};this.$patch((e=>{Ls(e,t)}))}:As,b=(t,n="")=>{if(Os in t)return t[Ms]=n,t;const r=function(){ks(o);const n=Array.from(arguments),i=[],a=[];let l;Rs(h,{args:n,name:r[Ms],store:y,after:function(e){i.push(e)},onError:function(e){a.push(e)}});try{l=t.apply(this&&this.$id===e?this:y,n)}catch(s){throw Rs(a,s),s}return l instanceof Promise?l.then((e=>(Rs(i,e),e))).catch((e=>(Rs(a,e),Promise.reject(e)))):(Rs(i,l),l)};return r[Os]=!0,r[Ms]=n,r},y=vt({_p:o,$id:e,$onAction:zs.bind(null,h),$patch:m,$reset:g,$subscribe(t,n={}){const r=zs(p,t,n.detached,(()=>i())),i=a.run((()=>lr((()=>o.state.value[e]),(o=>{("sync"===n.flush?u:c)&&t({storeId:e,type:Ps.direct,events:d},o)}),Ls({},s,n))));return r},$dispose:function(){a.stop(),p=[],h=[],o._s.delete(e)}});o._s.set(e,y);const x=(o._a&&o._a.runWithContext||Es)((()=>o._e.run((()=>(a=ce()).run((()=>t({action:b})))))));for(const k in x){const t=x[k];if(Rt(t)&&(!Rt(w=t)||!w.effect)||yt(t))i||(!f||_s(C=t)&&C.hasOwnProperty(Is)||(Rt(t)?t.value=f[k]:Fs(t,f[k])),o.state.value[e][k]=t);else if("function"==typeof t){const e=b(t,k);x[k]=e,l.actions[k]=t}}var C,w;return Ls(y,x),Ls(kt(y),x),Object.defineProperty(y,"$state",{get:()=>o.state.value[e],set:e=>{m((t=>{Ls(t,e)}))}}),o._p.forEach((e=>{Ls(y,a.run((()=>e({store:y,app:o._a,pinia:o,options:l}))))})),f&&i&&n.hydrate&&n.hydrate(y.$state,f),c=!0,u=!0,y}function $s(e,t,n){let o,r;const i="function"==typeof t;function a(e,n){return(e=e||(Kr||un||So?Po(Ss,null):null))&&ks(e),(e=ws)._s.has(o)||(i?Ds(o,t,r,e):Bs(o,r,e)),e._s.get(o)}return"string"==typeof e?(o=e,r=i?n:t):(r=e,o=e.id),a.$id=o,a}function Ns(e,t){return function(){return e.apply(t,arguments)}}const{toString:js}=Object.prototype,{getPrototypeOf:Hs}=Object,Ws=(Us=Object.create(null),e=>{const t=js.call(e);return Us[t]||(Us[t]=t.slice(8,-1).toLowerCase())});var Us;const Vs=e=>(e=e.toLowerCase(),t=>Ws(t)===e),qs=e=>t=>typeof t===e,{isArray:Ks}=Array,Gs=qs("undefined"),Xs=Vs("ArrayBuffer"),Ys=qs("string"),Qs=qs("function"),Zs=qs("number"),Js=e=>null!==e&&"object"==typeof e,ec=e=>{if("object"!==Ws(e))return!1;const t=Hs(e);return!(null!==t&&t!==Object.prototype&&null!==Object.getPrototypeOf(t)||Symbol.toStringTag in e||Symbol.iterator in e)},tc=Vs("Date"),nc=Vs("File"),oc=Vs("Blob"),rc=Vs("FileList"),ic=Vs("URLSearchParams"),[ac,lc,sc,cc]=["ReadableStream","Request","Response","Headers"].map(Vs);function uc(e,t,{allOwnKeys:n=!1}={}){if(null==e)return;let o,r;if("object"!=typeof e&&(e=[e]),Ks(e))for(o=0,r=e.length;o0;)if(o=n[r],t===o.toLowerCase())return o;return null}const pc="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,hc=e=>!Gs(e)&&e!==pc,fc=(vc="undefined"!=typeof Uint8Array&&Hs(Uint8Array),e=>vc&&e instanceof vc);var vc;const mc=Vs("HTMLFormElement"),gc=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),bc=Vs("RegExp"),yc=(e,t)=>{const n=Object.getOwnPropertyDescriptors(e),o={};uc(n,((n,r)=>{let i;!1!==(i=t(n,r,e))&&(o[r]=i||n)})),Object.defineProperties(e,o)},xc="abcdefghijklmnopqrstuvwxyz",Cc="0123456789",wc={DIGIT:Cc,ALPHA:xc,ALPHA_DIGIT:xc+xc.toUpperCase()+Cc},kc=Vs("AsyncFunction"),Sc=(_c="function"==typeof setImmediate,Pc=Qs(pc.postMessage),_c?setImmediate:Pc?(Tc=`axios@${Math.random()}`,Ac=[],pc.addEventListener("message",(({source:e,data:t})=>{e===pc&&t===Tc&&Ac.length&&Ac.shift()()}),!1),e=>{Ac.push(e),pc.postMessage(Tc,"*")}):e=>setTimeout(e));var _c,Pc,Tc,Ac;const zc="undefined"!=typeof queueMicrotask?queueMicrotask.bind(pc):"undefined"!=typeof process&&process.nextTick||Sc,Rc={isArray:Ks,isArrayBuffer:Xs,isBuffer:function(e){return null!==e&&!Gs(e)&&null!==e.constructor&&!Gs(e.constructor)&&Qs(e.constructor.isBuffer)&&e.constructor.isBuffer(e)},isFormData:e=>{let t;return e&&("function"==typeof FormData&&e instanceof FormData||Qs(e.append)&&("formdata"===(t=Ws(e))||"object"===t&&Qs(e.toString)&&"[object FormData]"===e.toString()))},isArrayBufferView:function(e){let t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&Xs(e.buffer),t},isString:Ys,isNumber:Zs,isBoolean:e=>!0===e||!1===e,isObject:Js,isPlainObject:ec,isReadableStream:ac,isRequest:lc,isResponse:sc,isHeaders:cc,isUndefined:Gs,isDate:tc,isFile:nc,isBlob:oc,isRegExp:bc,isFunction:Qs,isStream:e=>Js(e)&&Qs(e.pipe),isURLSearchParams:ic,isTypedArray:fc,isFileList:rc,forEach:uc,merge:function e(){const{caseless:t}=hc(this)&&this||{},n={},o=(o,r)=>{const i=t&&dc(n,r)||r;ec(n[i])&&ec(o)?n[i]=e(n[i],o):ec(o)?n[i]=e({},o):Ks(o)?n[i]=o.slice():n[i]=o};for(let r=0,i=arguments.length;r(uc(t,((t,o)=>{n&&Qs(t)?e[o]=Ns(t,n):e[o]=t}),{allOwnKeys:o}),e),trim:e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""),stripBOM:e=>(65279===e.charCodeAt(0)&&(e=e.slice(1)),e),inherits:(e,t,n,o)=>{e.prototype=Object.create(t.prototype,o),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:(e,t,n,o)=>{let r,i,a;const l={};if(t=t||{},null==e)return t;do{for(r=Object.getOwnPropertyNames(e),i=r.length;i-- >0;)a=r[i],o&&!o(a,e,t)||l[a]||(t[a]=e[a],l[a]=!0);e=!1!==n&&Hs(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:Ws,kindOfTest:Vs,endsWith:(e,t,n)=>{e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;const o=e.indexOf(t,n);return-1!==o&&o===n},toArray:e=>{if(!e)return null;if(Ks(e))return e;let t=e.length;if(!Zs(t))return null;const n=new Array(t);for(;t-- >0;)n[t]=e[t];return n},forEachEntry:(e,t)=>{const n=(e&&e[Symbol.iterator]).call(e);let o;for(;(o=n.next())&&!o.done;){const n=o.value;t.call(e,n[0],n[1])}},matchAll:(e,t)=>{let n;const o=[];for(;null!==(n=e.exec(t));)o.push(n);return o},isHTMLForm:mc,hasOwnProperty:gc,hasOwnProp:gc,reduceDescriptors:yc,freezeMethods:e=>{yc(e,((t,n)=>{if(Qs(e)&&-1!==["arguments","caller","callee"].indexOf(n))return!1;const o=e[n];Qs(o)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+n+"'")}))}))},toObjectSet:(e,t)=>{const n={},o=e=>{e.forEach((e=>{n[e]=!0}))};return Ks(e)?o(e):o(String(e).split(t)),n},toCamelCase:e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n})),noop:()=>{},toFiniteNumber:(e,t)=>null!=e&&Number.isFinite(e=+e)?e:t,findKey:dc,global:pc,isContextDefined:hc,ALPHABET:wc,generateString:(e=16,t=wc.ALPHA_DIGIT)=>{let n="";const{length:o}=t;for(;e--;)n+=t[Math.random()*o|0];return n},isSpecCompliantForm:function(e){return!!(e&&Qs(e.append)&&"FormData"===e[Symbol.toStringTag]&&e[Symbol.iterator])},toJSONObject:e=>{const t=new Array(10),n=(e,o)=>{if(Js(e)){if(t.indexOf(e)>=0)return;if(!("toJSON"in e)){t[o]=e;const r=Ks(e)?[]:{};return uc(e,((e,t)=>{const i=n(e,o+1);!Gs(i)&&(r[t]=i)})),t[o]=void 0,r}}return e};return n(e,0)},isAsyncFn:kc,isThenable:e=>e&&(Js(e)||Qs(e))&&Qs(e.then)&&Qs(e.catch),setImmediate:Sc,asap:zc};function Ec(e,t,n,o,r){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),n&&(this.config=n),o&&(this.request=o),r&&(this.response=r,this.status=r.status?r.status:null)}Rc.inherits(Ec,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:Rc.toJSONObject(this.config),code:this.code,status:this.status}}});const Oc=Ec.prototype,Mc={};function Fc(e){return Rc.isPlainObject(e)||Rc.isArray(e)}function Ic(e){return Rc.endsWith(e,"[]")?e.slice(0,-2):e}function Lc(e,t,n){return e?e.concat(t).map((function(e,t){return e=Ic(e),!n&&t?"["+e+"]":e})).join(n?".":""):t}["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((e=>{Mc[e]={value:e}})),Object.defineProperties(Ec,Mc),Object.defineProperty(Oc,"isAxiosError",{value:!0}),Ec.from=(e,t,n,o,r,i)=>{const a=Object.create(Oc);return Rc.toFlatObject(e,a,(function(e){return e!==Error.prototype}),(e=>"isAxiosError"!==e)),Ec.call(a,e.message,t,n,o,r),a.cause=e,a.name=e.name,i&&Object.assign(a,i),a};const Bc=Rc.toFlatObject(Rc,{},null,(function(e){return/^is[A-Z]/.test(e)}));function Dc(e,t,n){if(!Rc.isObject(e))throw new TypeError("target must be an object");t=t||new FormData;const o=(n=Rc.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!Rc.isUndefined(t[e])}))).metaTokens,r=n.visitor||c,i=n.dots,a=n.indexes,l=(n.Blob||"undefined"!=typeof Blob&&Blob)&&Rc.isSpecCompliantForm(t);if(!Rc.isFunction(r))throw new TypeError("visitor must be a function");function s(e){if(null===e)return"";if(Rc.isDate(e))return e.toISOString();if(!l&&Rc.isBlob(e))throw new Ec("Blob is not supported. Use a Buffer instead.");return Rc.isArrayBuffer(e)||Rc.isTypedArray(e)?l&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function c(e,n,r){let l=e;if(e&&!r&&"object"==typeof e)if(Rc.endsWith(n,"{}"))n=o?n:n.slice(0,-2),e=JSON.stringify(e);else if(Rc.isArray(e)&&function(e){return Rc.isArray(e)&&!e.some(Fc)}(e)||(Rc.isFileList(e)||Rc.endsWith(n,"[]"))&&(l=Rc.toArray(e)))return n=Ic(n),l.forEach((function(e,o){!Rc.isUndefined(e)&&null!==e&&t.append(!0===a?Lc([n],o,i):null===a?n:n+"[]",s(e))})),!1;return!!Fc(e)||(t.append(Lc(r,n,i),s(e)),!1)}const u=[],d=Object.assign(Bc,{defaultVisitor:c,convertValue:s,isVisitable:Fc});if(!Rc.isObject(e))throw new TypeError("data must be an object");return function e(n,o){if(!Rc.isUndefined(n)){if(-1!==u.indexOf(n))throw Error("Circular reference detected in "+o.join("."));u.push(n),Rc.forEach(n,(function(n,i){!0===(!(Rc.isUndefined(n)||null===n)&&r.call(t,n,Rc.isString(i)?i.trim():i,o,d))&&e(n,o?o.concat(i):[i])})),u.pop()}}(e),t}function $c(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function Nc(e,t){this._pairs=[],e&&Dc(e,this,t)}const jc=Nc.prototype;function Hc(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Wc(e,t,n){if(!t)return e;const o=n&&n.encode||Hc,r=n&&n.serialize;let i;if(i=r?r(t,n):Rc.isURLSearchParams(t)?t.toString():new Nc(t,n).toString(o),i){const t=e.indexOf("#");-1!==t&&(e=e.slice(0,t)),e+=(-1===e.indexOf("?")?"?":"&")+i}return e}jc.append=function(e,t){this._pairs.push([e,t])},jc.toString=function(e){const t=e?function(t){return e.call(this,t,$c)}:$c;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};const Uc=class{constructor(){this.handlers=[]}use(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(e){Rc.forEach(this.handlers,(function(t){null!==t&&e(t)}))}},Vc={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},qc={isBrowser:!0,classes:{URLSearchParams:"undefined"!=typeof URLSearchParams?URLSearchParams:Nc,FormData:"undefined"!=typeof FormData?FormData:null,Blob:"undefined"!=typeof Blob?Blob:null},protocols:["http","https","file","blob","url","data"]},Kc="undefined"!=typeof window&&"undefined"!=typeof document,Gc="object"==typeof navigator&&navigator||void 0,Xc=Kc&&(!Gc||["ReactNative","NativeScript","NS"].indexOf(Gc.product)<0),Yc="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,Qc=Kc&&window.location.href||"http://localhost",Zc=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv:Kc,hasStandardBrowserEnv:Xc,hasStandardBrowserWebWorkerEnv:Yc,navigator:Gc,origin:Qc},Symbol.toStringTag,{value:"Module"})),Jc=d(d({},Zc),qc);function eu(e){function t(e,n,o,r){let i=e[r++];if("__proto__"===i)return!0;const a=Number.isFinite(+i),l=r>=e.length;return i=!i&&Rc.isArray(o)?o.length:i,l?(Rc.hasOwnProp(o,i)?o[i]=[o[i],n]:o[i]=n,!a):(o[i]&&Rc.isObject(o[i])||(o[i]=[]),t(e,n,o[i],r)&&Rc.isArray(o[i])&&(o[i]=function(e){const t={},n=Object.keys(e);let o;const r=n.length;let i;for(o=0;o{t(function(e){return Rc.matchAll(/\w+|\[(\w*)]/g,e).map((e=>"[]"===e[0]?"":e[1]||e[0]))}(e),o,n,0)})),n}return null}const tu={transitional:Vc,adapter:["xhr","http","fetch"],transformRequest:[function(e,t){const n=t.getContentType()||"",o=n.indexOf("application/json")>-1,r=Rc.isObject(e);if(r&&Rc.isHTMLForm(e)&&(e=new FormData(e)),Rc.isFormData(e))return o?JSON.stringify(eu(e)):e;if(Rc.isArrayBuffer(e)||Rc.isBuffer(e)||Rc.isStream(e)||Rc.isFile(e)||Rc.isBlob(e)||Rc.isReadableStream(e))return e;if(Rc.isArrayBufferView(e))return e.buffer;if(Rc.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();let i;if(r){if(n.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return Dc(e,new Jc.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,o){return Jc.isNode&&Rc.isBuffer(e)?(this.append(t,e.toString("base64")),!1):o.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((i=Rc.isFileList(e))||n.indexOf("multipart/form-data")>-1){const t=this.env&&this.env.FormData;return Dc(i?{"files[]":e}:e,t&&new t,this.formSerializer)}}return r||o?(t.setContentType("application/json",!1),function(e,t,n){if(Rc.isString(e))try{return(t||JSON.parse)(e),Rc.trim(e)}catch(o){if("SyntaxError"!==o.name)throw o}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){const t=this.transitional||tu.transitional,n=t&&t.forcedJSONParsing,o="json"===this.responseType;if(Rc.isResponse(e)||Rc.isReadableStream(e))return e;if(e&&Rc.isString(e)&&(n&&!this.responseType||o)){const n=!(t&&t.silentJSONParsing)&&o;try{return JSON.parse(e)}catch(r){if(n){if("SyntaxError"===r.name)throw Ec.from(r,Ec.ERR_BAD_RESPONSE,this,null,this.response);throw r}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:Jc.classes.FormData,Blob:Jc.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};Rc.forEach(["delete","get","head","post","put","patch"],(e=>{tu.headers[e]={}}));const nu=tu,ou=Rc.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),ru=Symbol("internals");function iu(e){return e&&String(e).trim().toLowerCase()}function au(e){return!1===e||null==e?e:Rc.isArray(e)?e.map(au):String(e)}function lu(e,t,n,o,r){return Rc.isFunction(o)?o.call(this,t,n):(r&&(t=n),Rc.isString(t)?Rc.isString(o)?-1!==t.indexOf(o):Rc.isRegExp(o)?o.test(t):void 0:void 0)}class su{constructor(e){e&&this.set(e)}set(e,t,n){const o=this;function r(e,t,n){const r=iu(t);if(!r)throw new Error("header name must be a non-empty string");const i=Rc.findKey(o,r);(!i||void 0===o[i]||!0===n||void 0===n&&!1!==o[i])&&(o[i||t]=au(e))}const i=(e,t)=>Rc.forEach(e,((e,n)=>r(e,n,t)));if(Rc.isPlainObject(e)||e instanceof this.constructor)i(e,t);else if(Rc.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim()))i((e=>{const t={};let n,o,r;return e&&e.split("\n").forEach((function(e){r=e.indexOf(":"),n=e.substring(0,r).trim().toLowerCase(),o=e.substring(r+1).trim(),!n||t[n]&&ou[n]||("set-cookie"===n?t[n]?t[n].push(o):t[n]=[o]:t[n]=t[n]?t[n]+", "+o:o)})),t})(e),t);else if(Rc.isHeaders(e))for(const[a,l]of e.entries())r(l,a,n);else null!=e&&r(t,e,n);return this}get(e,t){if(e=iu(e)){const n=Rc.findKey(this,e);if(n){const e=this[n];if(!t)return e;if(!0===t)return function(e){const t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let o;for(;o=n.exec(e);)t[o[1]]=o[2];return t}(e);if(Rc.isFunction(t))return t.call(this,e,n);if(Rc.isRegExp(t))return t.exec(e);throw new TypeError("parser must be boolean|regexp|function")}}}has(e,t){if(e=iu(e)){const n=Rc.findKey(this,e);return!(!n||void 0===this[n]||t&&!lu(0,this[n],n,t))}return!1}delete(e,t){const n=this;let o=!1;function r(e){if(e=iu(e)){const r=Rc.findKey(n,e);!r||t&&!lu(0,n[r],r,t)||(delete n[r],o=!0)}}return Rc.isArray(e)?e.forEach(r):r(e),o}clear(e){const t=Object.keys(this);let n=t.length,o=!1;for(;n--;){const r=t[n];e&&!lu(0,this[r],r,e,!0)||(delete this[r],o=!0)}return o}normalize(e){const t=this,n={};return Rc.forEach(this,((o,r)=>{const i=Rc.findKey(n,r);if(i)return t[i]=au(o),void delete t[r];const a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,((e,t,n)=>t.toUpperCase()+n))}(r):String(r).trim();a!==r&&delete t[r],t[a]=au(o),n[a]=!0})),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){const t=Object.create(null);return Rc.forEach(this,((n,o)=>{null!=n&&!1!==n&&(t[o]=e&&Rc.isArray(n)?n.join(", "):n)})),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map((([e,t])=>e+": "+t)).join("\n")}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){const n=new this(e);return t.forEach((e=>n.set(e))),n}static accessor(e){const t=(this[ru]=this[ru]={accessors:{}}).accessors,n=this.prototype;function o(e){const o=iu(e);t[o]||(function(e,t){const n=Rc.toCamelCase(" "+t);["get","set","has"].forEach((o=>{Object.defineProperty(e,o+n,{value:function(e,n,r){return this[o].call(this,t,e,n,r)},configurable:!0})}))}(n,e),t[o]=!0)}return Rc.isArray(e)?e.forEach(o):o(e),this}}su.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]),Rc.reduceDescriptors(su.prototype,(({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}})),Rc.freezeMethods(su);const cu=su;function uu(e,t){const n=this||nu,o=t||n,r=cu.from(o.headers);let i=o.data;return Rc.forEach(e,(function(e){i=e.call(n,i,r.normalize(),t?t.status:void 0)})),r.normalize(),i}function du(e){return!(!e||!e.__CANCEL__)}function pu(e,t,n){Ec.call(this,null==e?"canceled":e,Ec.ERR_CANCELED,t,n),this.name="CanceledError"}function hu(e,t,n){const o=n.config.validateStatus;n.status&&o&&!o(n.status)?t(new Ec("Request failed with status code "+n.status,[Ec.ERR_BAD_REQUEST,Ec.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n)):e(n)}Rc.inherits(pu,Ec,{__CANCEL__:!0});const fu=(e,t,n=3)=>{let o=0;const r=function(e,t){e=e||10;const n=new Array(e),o=new Array(e);let r,i=0,a=0;return t=void 0!==t?t:1e3,function(l){const s=Date.now(),c=o[a];r||(r=s),n[i]=l,o[i]=s;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),s-r{r=i,n=null,o&&(clearTimeout(o),o=null),e.apply(null,t)};return[(...e)=>{const t=Date.now(),l=t-r;l>=i?a(e,t):(n=e,o||(o=setTimeout((()=>{o=null,a(n)}),i-l)))},()=>n&&a(n)]}((n=>{const i=n.loaded,a=n.lengthComputable?n.total:void 0,l=i-o,s=r(l);o=i,e({loaded:i,total:a,progress:a?i/a:void 0,bytes:l,rate:s||void 0,estimated:s&&a&&i<=a?(a-i)/s:void 0,event:n,lengthComputable:null!=a,[t?"download":"upload"]:!0})}),n)},vu=(e,t)=>{const n=null!=e;return[o=>t[0]({lengthComputable:n,total:e,loaded:o}),t[1]]},mu=e=>(...t)=>Rc.asap((()=>e(...t))),gu=Jc.hasStandardBrowserEnv?function(){const e=Jc.navigator&&/(msie|trident)/i.test(Jc.navigator.userAgent),t=document.createElement("a");let n;function o(n){let o=n;return e&&(t.setAttribute("href",o),o=t.href),t.setAttribute("href",o),{href:t.href,protocol:t.protocol?t.protocol.replace(/:$/,""):"",host:t.host,search:t.search?t.search.replace(/^\?/,""):"",hash:t.hash?t.hash.replace(/^#/,""):"",hostname:t.hostname,port:t.port,pathname:"/"===t.pathname.charAt(0)?t.pathname:"/"+t.pathname}}return n=o(window.location.href),function(e){const t=Rc.isString(e)?o(e):e;return t.protocol===n.protocol&&t.host===n.host}}():function(){return!0},bu=Jc.hasStandardBrowserEnv?{write(e,t,n,o,r,i){const a=[e+"="+encodeURIComponent(t)];Rc.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),Rc.isString(o)&&a.push("path="+o),Rc.isString(r)&&a.push("domain="+r),!0===i&&a.push("secure"),document.cookie=a.join("; ")},read(e){const t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove(e){this.write(e,"",Date.now()-864e5)}}:{write(){},read:()=>null,remove(){}};function yu(e,t){return e&&!/^([a-z][a-z\d+\-.]*:)?\/\//i.test(t)?function(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}(e,t):t}const xu=e=>e instanceof cu?d({},e):e;function Cu(e,t){t=t||{};const n={};function o(e,t,n){return Rc.isPlainObject(e)&&Rc.isPlainObject(t)?Rc.merge.call({caseless:n},e,t):Rc.isPlainObject(t)?Rc.merge({},t):Rc.isArray(t)?t.slice():t}function r(e,t,n){return Rc.isUndefined(t)?Rc.isUndefined(e)?void 0:o(void 0,e,n):o(e,t,n)}function i(e,t){if(!Rc.isUndefined(t))return o(void 0,t)}function a(e,t){return Rc.isUndefined(t)?Rc.isUndefined(e)?void 0:o(void 0,e):o(void 0,t)}function l(n,r,i){return i in t?o(n,r):i in e?o(void 0,n):void 0}const s={url:i,method:i,data:i,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,withXSRFToken:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,beforeRedirect:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:l,headers:(e,t)=>r(xu(e),xu(t),!0)};return Rc.forEach(Object.keys(Object.assign({},e,t)),(function(o){const i=s[o]||r,a=i(e[o],t[o],o);Rc.isUndefined(a)&&i!==l||(n[o]=a)})),n}const wu=e=>{const t=Cu({},e);let n,{data:o,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:l,auth:s}=t;if(t.headers=l=cu.from(l),t.url=Wc(yu(t.baseURL,t.url),e.params,e.paramsSerializer),s&&l.set("Authorization","Basic "+btoa((s.username||"")+":"+(s.password?unescape(encodeURIComponent(s.password)):""))),Rc.isFormData(o))if(Jc.hasStandardBrowserEnv||Jc.hasStandardBrowserWebWorkerEnv)l.setContentType(void 0);else if(!1!==(n=l.getContentType())){const[e,...t]=n?n.split(";").map((e=>e.trim())).filter(Boolean):[];l.setContentType([e||"multipart/form-data",...t].join("; "))}if(Jc.hasStandardBrowserEnv&&(r&&Rc.isFunction(r)&&(r=r(t)),r||!1!==r&&gu(t.url))){const e=i&&a&&bu.read(a);e&&l.set(i,e)}return t},ku="undefined"!=typeof XMLHttpRequest&&function(e){return new Promise((function(t,n){const o=wu(e);let r=o.data;const i=cu.from(o.headers).normalize();let a,l,s,c,u,{responseType:d,onUploadProgress:p,onDownloadProgress:h}=o;function f(){c&&c(),u&&u(),o.cancelToken&&o.cancelToken.unsubscribe(a),o.signal&&o.signal.removeEventListener("abort",a)}let v=new XMLHttpRequest;function m(){if(!v)return;const o=cu.from("getAllResponseHeaders"in v&&v.getAllResponseHeaders());hu((function(e){t(e),f()}),(function(e){n(e),f()}),{data:d&&"text"!==d&&"json"!==d?v.response:v.responseText,status:v.status,statusText:v.statusText,headers:o,config:e,request:v}),v=null}v.open(o.method.toUpperCase(),o.url,!0),v.timeout=o.timeout,"onloadend"in v?v.onloadend=m:v.onreadystatechange=function(){v&&4===v.readyState&&(0!==v.status||v.responseURL&&0===v.responseURL.indexOf("file:"))&&setTimeout(m)},v.onabort=function(){v&&(n(new Ec("Request aborted",Ec.ECONNABORTED,e,v)),v=null)},v.onerror=function(){n(new Ec("Network Error",Ec.ERR_NETWORK,e,v)),v=null},v.ontimeout=function(){let t=o.timeout?"timeout of "+o.timeout+"ms exceeded":"timeout exceeded";const r=o.transitional||Vc;o.timeoutErrorMessage&&(t=o.timeoutErrorMessage),n(new Ec(t,r.clarifyTimeoutError?Ec.ETIMEDOUT:Ec.ECONNABORTED,e,v)),v=null},void 0===r&&i.setContentType(null),"setRequestHeader"in v&&Rc.forEach(i.toJSON(),(function(e,t){v.setRequestHeader(t,e)})),Rc.isUndefined(o.withCredentials)||(v.withCredentials=!!o.withCredentials),d&&"json"!==d&&(v.responseType=o.responseType),h&&([s,u]=fu(h,!0),v.addEventListener("progress",s)),p&&v.upload&&([l,c]=fu(p),v.upload.addEventListener("progress",l),v.upload.addEventListener("loadend",c)),(o.cancelToken||o.signal)&&(a=t=>{v&&(n(!t||t.type?new pu(null,e,v):t),v.abort(),v=null)},o.cancelToken&&o.cancelToken.subscribe(a),o.signal&&(o.signal.aborted?a():o.signal.addEventListener("abort",a)));const g=function(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}(o.url);g&&-1===Jc.protocols.indexOf(g)?n(new Ec("Unsupported protocol "+g+":",Ec.ERR_BAD_REQUEST,e)):v.send(r||null)}))},Su=(e,t)=>{let n,o=new AbortController;const r=function(e){if(!n){n=!0,a();const t=e instanceof Error?e:this.reason;o.abort(t instanceof Ec?t:new pu(t instanceof Error?t.message:t))}};let i=t&&setTimeout((()=>{r(new Ec(`timeout ${t} of ms exceeded`,Ec.ETIMEDOUT))}),t);const a=()=>{e&&(i&&clearTimeout(i),i=null,e.forEach((e=>{e&&(e.removeEventListener?e.removeEventListener("abort",r):e.unsubscribe(r))})),e=null)};e.forEach((e=>e&&e.addEventListener&&e.addEventListener("abort",r)));const{signal:l}=o;return l.unsubscribe=a,[l,()=>{i&&clearTimeout(i),i=null}]},_u=function*(e,t){let n=e.byteLength;if(!t||n{var o=(e,t,r,i)=>{try{var a=n[e](t),l=(t=a.value)instanceof m,s=a.done;Promise.resolve(l?t[0]:t).then((n=>l?o("return"===e?e:"next",t[1]?{done:n.done,value:n.value}:n,r,i):r({value:n,done:s}))).catch((e=>o("throw",e,r,i)))}catch(c){i(c)}},r=e=>i[e]=t=>new Promise(((n,r)=>o(e,t,n,r))),i={};return n=n.apply(e,t),i[Symbol.asyncIterator]=()=>i,r("next"),r("throw"),r("return"),i})(this,null,(function*(){try{for(var o,r,i,a=((e,t,n)=>(t=e[c("asyncIterator")])?t.call(e):(e=e[c("iterator")](),t={},(n=(n,o)=>(o=e[n])&&(t[n]=t=>new Promise(((n,r,i)=>(t=o.call(e,t),i=t.done,Promise.resolve(t.value).then((e=>n({value:e,done:i})),r))))))("next"),n("return"),t))(e);o=!(r=yield new m(a.next())).done;o=!1){const e=r.value;yield*g(_u(ArrayBuffer.isView(e)?e:yield new m(n(String(e))),t))}}catch(r){i=[r]}finally{try{o&&(r=a.return)&&(yield new m(r.call(a)))}finally{if(i)throw i[0]}}}))},Tu=(e,t,n,o,r)=>{const i=Pu(e,t,r);let a,l=0,s=e=>{a||(a=!0,o&&o(e))};return new ReadableStream({pull(e){return v(this,null,(function*(){try{const{done:t,value:o}=yield i.next();if(t)return s(),void e.close();let r=o.byteLength;if(n){let e=l+=r;n(e)}e.enqueue(new Uint8Array(o))}catch(t){throw s(t),t}}))},cancel:e=>(s(e),i.return())},{highWaterMark:2})},Au="function"==typeof fetch&&"function"==typeof Request&&"function"==typeof Response,zu=Au&&"function"==typeof ReadableStream,Ru=Au&&("function"==typeof TextEncoder?(Eu=new TextEncoder,e=>Eu.encode(e)):t=>v(e,null,(function*(){return new Uint8Array(yield new Response(t).arrayBuffer())})));var Eu;const Ou=(e,...t)=>{try{return!!e(...t)}catch(n){return!1}},Mu=zu&&Ou((()=>{let e=!1;const t=new Request(Jc.origin,{body:new ReadableStream,method:"POST",get duplex(){return e=!0,"half"}}).headers.has("Content-Type");return e&&!t})),Fu=zu&&Ou((()=>Rc.isReadableStream(new Response("").body))),Iu={stream:Fu&&(e=>e.body)};var Lu;Au&&(Lu=new Response,["text","arrayBuffer","blob","formData","stream"].forEach((e=>{!Iu[e]&&(Iu[e]=Rc.isFunction(Lu[e])?t=>t[e]():(t,n)=>{throw new Ec(`Response type '${e}' is not supported`,Ec.ERR_NOT_SUPPORT,n)})})));const Bu=(t,n)=>v(e,null,(function*(){const o=Rc.toFiniteNumber(t.getContentLength());return null==o?(t=>v(e,null,(function*(){return null==t?0:Rc.isBlob(t)?t.size:Rc.isSpecCompliantForm(t)?(yield new Request(t).arrayBuffer()).byteLength:Rc.isArrayBufferView(t)||Rc.isArrayBuffer(t)?t.byteLength:(Rc.isURLSearchParams(t)&&(t+=""),Rc.isString(t)?(yield Ru(t)).byteLength:void 0)})))(n):o})),Du={http:null,xhr:ku,fetch:Au&&(t=>v(e,null,(function*(){let{url:e,method:n,data:o,signal:r,cancelToken:i,timeout:a,onDownloadProgress:l,onUploadProgress:s,responseType:c,headers:u,withCredentials:h="same-origin",fetchOptions:f}=wu(t);c=c?(c+"").toLowerCase():"text";let v,m,[g,b]=r||i||a?Su([r,i],a):[];const y=()=>{!v&&setTimeout((()=>{g&&g.unsubscribe()})),v=!0};let x;try{if(s&&Mu&&"get"!==n&&"head"!==n&&0!==(x=yield Bu(u,o))){let t,n=new Request(e,{method:"POST",body:o,duplex:"half"});if(Rc.isFormData(o)&&(t=n.headers.get("content-type"))&&u.setContentType(t),n.body){const[e,t]=vu(x,fu(mu(s)));o=Tu(n.body,65536,e,t,Ru)}}Rc.isString(h)||(h=h?"include":"omit");const r="credentials"in Request.prototype;m=new Request(e,p(d({},f),{signal:g,method:n.toUpperCase(),headers:u.normalize().toJSON(),body:o,duplex:"half",credentials:r?h:void 0}));let i=yield fetch(m);const a=Fu&&("stream"===c||"response"===c);if(Fu&&(l||a)){const e={};["status","statusText","headers"].forEach((t=>{e[t]=i[t]}));const t=Rc.toFiniteNumber(i.headers.get("content-length")),[n,o]=l&&vu(t,fu(mu(l),!0))||[];i=new Response(Tu(i.body,65536,n,(()=>{o&&o(),a&&y()}),Ru),e)}c=c||"text";let v=yield Iu[Rc.findKey(Iu,c)||"text"](i,t);return!a&&y(),b&&b(),yield new Promise(((e,n)=>{hu(e,n,{data:v,headers:cu.from(i.headers),status:i.status,statusText:i.statusText,config:t,request:m})}))}catch(C){if(y(),C&&"TypeError"===C.name&&/fetch/i.test(C.message))throw Object.assign(new Ec("Network Error",Ec.ERR_NETWORK,t,m),{cause:C.cause||C});throw Ec.from(C,C&&C.code,t,m)}})))};Rc.forEach(Du,((e,t)=>{if(e){try{Object.defineProperty(e,"name",{value:t})}catch(n){}Object.defineProperty(e,"adapterName",{value:t})}}));const $u=e=>`- ${e}`,Nu=e=>Rc.isFunction(e)||null===e||!1===e,ju=e=>{e=Rc.isArray(e)?e:[e];const{length:t}=e;let n,o;const r={};for(let i=0;i`adapter ${e} `+(!1===t?"is not supported by the environment":"is not available in the build")));throw new Ec("There is no suitable adapter to dispatch the request "+(t?e.length>1?"since :\n"+e.map($u).join("\n"):" "+$u(e[0]):"as no adapter specified"),"ERR_NOT_SUPPORT")}return o};function Hu(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new pu(null,e)}function Wu(e){return Hu(e),e.headers=cu.from(e.headers),e.data=uu.call(e,e.transformRequest),-1!==["post","put","patch"].indexOf(e.method)&&e.headers.setContentType("application/x-www-form-urlencoded",!1),ju(e.adapter||nu.adapter)(e).then((function(t){return Hu(e),t.data=uu.call(e,e.transformResponse,t),t.headers=cu.from(t.headers),t}),(function(t){return du(t)||(Hu(e),t&&t.response&&(t.response.data=uu.call(e,e.transformResponse,t.response),t.response.headers=cu.from(t.response.headers))),Promise.reject(t)}))}const Uu="1.7.5",Vu={};["object","boolean","number","function","string","symbol"].forEach(((e,t)=>{Vu[e]=function(n){return typeof n===e||"a"+(t<1?"n ":" ")+e}}));const qu={};Vu.transitional=function(e,t,n){return(o,r,i)=>{if(!1===e)throw new Ec(function(e,t){return"[Axios v1.7.5] Transitional option '"+e+"'"+t+(n?". "+n:"")}(r," has been removed"+(t?" in "+t:"")),Ec.ERR_DEPRECATED);return t&&!qu[r]&&(qu[r]=!0),!e||e(o,r,i)}};const Ku={assertOptions:function(e,t,n){if("object"!=typeof e)throw new Ec("options must be an object",Ec.ERR_BAD_OPTION_VALUE);const o=Object.keys(e);let r=o.length;for(;r-- >0;){const i=o[r],a=t[i];if(a){const t=e[i],n=void 0===t||a(t,i,e);if(!0!==n)throw new Ec("option "+i+" must be "+n,Ec.ERR_BAD_OPTION_VALUE)}else if(!0!==n)throw new Ec("Unknown option "+i,Ec.ERR_BAD_OPTION)}},validators:Vu},Gu=Ku.validators;class Xu{constructor(e){this.defaults=e,this.interceptors={request:new Uc,response:new Uc}}request(e,t){return v(this,null,(function*(){try{return yield this._request(e,t)}catch(n){if(n instanceof Error){let e;Error.captureStackTrace?Error.captureStackTrace(e={}):e=new Error;const t=e.stack?e.stack.replace(/^.+\n/,""):"";try{n.stack?t&&!String(n.stack).endsWith(t.replace(/^.+\n.+\n/,""))&&(n.stack+="\n"+t):n.stack=t}catch(o){}}throw n}}))}_request(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{},t=Cu(this.defaults,t);const{transitional:n,paramsSerializer:o,headers:r}=t;void 0!==n&&Ku.assertOptions(n,{silentJSONParsing:Gu.transitional(Gu.boolean),forcedJSONParsing:Gu.transitional(Gu.boolean),clarifyTimeoutError:Gu.transitional(Gu.boolean)},!1),null!=o&&(Rc.isFunction(o)?t.paramsSerializer={serialize:o}:Ku.assertOptions(o,{encode:Gu.function,serialize:Gu.function},!0)),t.method=(t.method||this.defaults.method||"get").toLowerCase();let i=r&&Rc.merge(r.common,r[t.method]);r&&Rc.forEach(["delete","get","head","post","put","patch","common"],(e=>{delete r[e]})),t.headers=cu.concat(i,r);const a=[];let l=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(l=l&&e.synchronous,a.unshift(e.fulfilled,e.rejected))}));const s=[];let c;this.interceptors.response.forEach((function(e){s.push(e.fulfilled,e.rejected)}));let u,d=0;if(!l){const e=[Wu.bind(this),void 0];for(e.unshift.apply(e,a),e.push.apply(e,s),u=e.length,c=Promise.resolve(t);d{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null})),this.promise.then=e=>{let t;const o=new Promise((e=>{n.subscribe(e),t=e})).then(e);return o.cancel=function(){n.unsubscribe(t)},o},e((function(e,o,r){n.reason||(n.reason=new pu(e,o,r),t(n.reason))}))}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;const t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}static source(){let e;return{token:new Qu((function(t){e=t})),cancel:e}}}const Zu=Qu,Ju={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Ju).forEach((([e,t])=>{Ju[t]=e}));const ed=Ju,td=function e(t){const n=new Yu(t),o=Ns(Yu.prototype.request,n);return Rc.extend(o,Yu.prototype,n,{allOwnKeys:!0}),Rc.extend(o,n,null,{allOwnKeys:!0}),o.create=function(n){return e(Cu(t,n))},o}(nu);td.Axios=Yu,td.CanceledError=pu,td.CancelToken=Zu,td.isCancel=du,td.VERSION=Uu,td.toFormData=Dc,td.AxiosError=Ec,td.Cancel=td.CanceledError,td.all=function(e){return Promise.all(e)},td.spread=function(e){return function(t){return e.apply(null,t)}},td.isAxiosError=function(e){return Rc.isObject(e)&&!0===e.isAxiosError},td.mergeConfig=Cu,td.AxiosHeaders=cu,td.formToJSON=e=>eu(Rc.isHTMLForm(e)?new FormData(e):e),td.getAdapter=ju,td.HttpStatusCode=ed,td.default=td;const nd=td,od=[{url:"/passport/auth/login",method:"POST"},{url:"/passport/auth/token2Login",method:"GET"},{url:"/passport/auth/register",method:"POST"},{url:"/passport/auth/register",method:"POST"},{url:"/guest/comm/config",method:"GET"},{url:"/passport/comm/sendEmailVerify",method:"POST"},{url:"/passport/auth/forget",method:"POST"},{url:"/passport/auth/telegramLogin",method:"POST"}];function rd(e){try{if("object"==typeof JSON.parse(e))return!0}catch(t){return!1}}class id{constructor(e){f(this,"storage"),f(this,"prefixKey"),this.storage=e.storage,this.prefixKey=e.prefixKey}getKey(e){return`${this.prefixKey}${e}`.toUpperCase()}set(e,t,n=null){const o=JSON.stringify({value:t,time:Date.now(),expire:null!==n?(new Date).getTime()+1e3*n:null});this.storage.setItem(this.getKey(e),o)}get(e,t=null){const n=this.storage.getItem(this.getKey(e));if(!n)return{value:t,time:0};try{const o=JSON.parse(n),{value:r,time:i,expire:a}=o;return function(e){return function(e){return null===e}(e)||function(e){return void 0===e}(e)}(a)||a>(new Date).getTime()?{value:r,time:i}:(this.remove(e),{value:t,time:0})}catch(o){return this.remove(e),{value:t,time:0}}}remove(e){this.storage.removeItem(this.getKey(e))}clear(){this.storage.clear()}}function ad({prefixKey:e="",storage:t=sessionStorage}){return new id({prefixKey:e,storage:t})}const ld="Vue_Naive_",sd=function(e={}){return ad({prefixKey:e.prefixKey||"",storage:localStorage})}({prefixKey:ld}),cd=function(e={}){return ad({prefixKey:e.prefixKey||"",storage:sessionStorage})}({prefixKey:ld}),ud="access_token";function dd(){return sd.get(ud)}function pd(){sd.remove(ud)}function hd(){const e=It(qN.currentRoute),t=!e.meta.requireAuth&&!["/404","/login"].includes(qN.currentRoute.value.path);qN.replace({path:"/login",query:t?p(d({},e.query),{redirect:e.path}):{}})}var fd="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function vd(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function md(e){if(e.__esModule)return e;var t=e.default;if("function"==typeof t){var n=function e(){return this instanceof e?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};n.prototype=t.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(e).forEach((function(t){var o=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,o.get?o:{enumerable:!0,get:function(){return e[t]}})})),n}var gd={exports:{}};gd.exports=function(){var e=1e3,t=6e4,n=36e5,o="millisecond",r="second",i="minute",a="hour",l="day",s="week",c="month",u="quarter",d="year",p="date",h="Invalid Date",f=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,v=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,m={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(e){var t=["th","st","nd","rd"],n=e%100;return"["+e+(t[(n-20)%10]||t[n]||t[0])+"]"}},g=function(e,t,n){var o=String(e);return!o||o.length>=t?e:""+Array(t+1-o.length).join(n)+e},b={s:g,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),o=Math.floor(n/60),r=n%60;return(t<=0?"+":"-")+g(o,2,"0")+":"+g(r,2,"0")},m:function e(t,n){if(t.date()1)return e(a[0])}else{var l=t.name;x[l]=t,r=l}return!o&&r&&(y=r),r||!o&&y},S=function(e,t){if(w(e))return e.clone();var n="object"==typeof t?t:{};return n.date=e,n.args=arguments,new P(n)},_=b;_.l=k,_.i=w,_.w=function(e,t){return S(e,{locale:t.$L,utc:t.$u,x:t.$x,$offset:t.$offset})};var P=function(){function m(e){this.$L=k(e.locale,null,!0),this.parse(e),this.$x=this.$x||e.x||{},this[C]=!0}var g=m.prototype;return g.parse=function(e){this.$d=function(e){var t=e.date,n=e.utc;if(null===t)return new Date(NaN);if(_.u(t))return new Date;if(t instanceof Date)return new Date(t);if("string"==typeof t&&!/Z$/i.test(t)){var o=t.match(f);if(o){var r=o[2]-1||0,i=(o[7]||"0").substring(0,3);return n?new Date(Date.UTC(o[1],r,o[3]||1,o[4]||0,o[5]||0,o[6]||0,i)):new Date(o[1],r,o[3]||1,o[4]||0,o[5]||0,o[6]||0,i)}}return new Date(t)}(e),this.init()},g.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},g.$utils=function(){return _},g.isValid=function(){return!(this.$d.toString()===h)},g.isSame=function(e,t){var n=S(e);return this.startOf(t)<=n&&n<=this.endOf(t)},g.isAfter=function(e,t){return S(e)1&&void 0!==arguments[1]?arguments[1]:{container:document.body},n="";return"string"==typeof e?n=d(e,t):e instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(null==e?void 0:e.type)?n=d(e.value,t):(n=s()(e),c("copy")),n};function h(e){return(h="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var f=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.action,n=void 0===t?"copy":t,o=e.container,r=e.target,i=e.text;if("copy"!==n&&"cut"!==n)throw new Error('Invalid "action" value, use either "copy" or "cut"');if(void 0!==r){if(!r||"object"!==h(r)||1!==r.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===n&&r.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===n&&(r.hasAttribute("readonly")||r.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes')}return i?p(i,{container:o}):r?"cut"===n?u(r):p(r,{container:o}):void 0};function v(e){return(v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function m(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText,this.container="object"===v(e.container)?e.container:document.body}},{key:"listenClick",value:function(e){var t=this;this.listener=a()(e,"click",(function(e){return t.onClick(e)}))}},{key:"onClick",value:function(e){var t=e.delegateTarget||e.currentTarget,n=this.action(t)||"copy",o=f({action:n,container:this.container,target:this.target(t),text:this.text(t)});this.emit(o?"success":"error",{action:n,text:o,trigger:t,clearSelection:function(){t&&t.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(e){return x("action",e)}},{key:"defaultTarget",value:function(e){var t=x("target",e);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(e){return x("text",e)}},{key:"destroy",value:function(){this.listener.destroy()}}],o=[{key:"copy",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{container:document.body};return p(e,t)}},{key:"cut",value:function(e){return u(e)}},{key:"isSupported",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof e?[e]:e,n=!!document.queryCommandSupported;return t.forEach((function(e){n=n&&!!document.queryCommandSupported(e)})),n}}],n&&m(t.prototype,n),o&&m(t,o),i}(r()),w=C},828:function(e){if("undefined"!=typeof Element&&!Element.prototype.matches){var t=Element.prototype;t.matches=t.matchesSelector||t.mozMatchesSelector||t.msMatchesSelector||t.oMatchesSelector||t.webkitMatchesSelector}e.exports=function(e,t){for(;e&&9!==e.nodeType;){if("function"==typeof e.matches&&e.matches(t))return e;e=e.parentNode}}},438:function(e,t,n){var o=n(828);function r(e,t,n,o,r){var a=i.apply(this,arguments);return e.addEventListener(n,a,r),{destroy:function(){e.removeEventListener(n,a,r)}}}function i(e,t,n,r){return function(n){n.delegateTarget=o(n.target,t),n.delegateTarget&&r.call(e,n)}}e.exports=function(e,t,n,o,i){return"function"==typeof e.addEventListener?r.apply(null,arguments):"function"==typeof n?r.bind(null,document).apply(null,arguments):("string"==typeof e&&(e=document.querySelectorAll(e)),Array.prototype.map.call(e,(function(e){return r(e,t,n,o,i)})))}},879:function(e,t){t.node=function(e){return void 0!==e&&e instanceof HTMLElement&&1===e.nodeType},t.nodeList=function(e){var n=Object.prototype.toString.call(e);return void 0!==e&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in e&&(0===e.length||t.node(e[0]))},t.string=function(e){return"string"==typeof e||e instanceof String},t.fn=function(e){return"[object Function]"===Object.prototype.toString.call(e)}},370:function(e,t,n){var o=n(879),r=n(438);e.exports=function(e,t,n){if(!e&&!t&&!n)throw new Error("Missing required arguments");if(!o.string(t))throw new TypeError("Second argument must be a String");if(!o.fn(n))throw new TypeError("Third argument must be a Function");if(o.node(e))return function(e,t,n){return e.addEventListener(t,n),{destroy:function(){e.removeEventListener(t,n)}}}(e,t,n);if(o.nodeList(e))return function(e,t,n){return Array.prototype.forEach.call(e,(function(e){e.addEventListener(t,n)})),{destroy:function(){Array.prototype.forEach.call(e,(function(e){e.removeEventListener(t,n)}))}}}(e,t,n);if(o.string(e))return function(e,t,n){return r(document.body,e,t,n)}(e,t,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}},817:function(e){e.exports=function(e){var t;if("SELECT"===e.nodeName)e.focus(),t=e.value;else if("INPUT"===e.nodeName||"TEXTAREA"===e.nodeName){var n=e.hasAttribute("readonly");n||e.setAttribute("readonly",""),e.select(),e.setSelectionRange(0,e.value.length),n||e.removeAttribute("readonly"),t=e.value}else{e.hasAttribute("contenteditable")&&e.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(e),o.removeAllRanges(),o.addRange(r),t=o.toString()}return t}},279:function(e){function t(){}t.prototype={on:function(e,t,n){var o=this.e||(this.e={});return(o[e]||(o[e]=[])).push({fn:t,ctx:n}),this},once:function(e,t,n){var o=this;function r(){o.off(e,r),t.apply(n,arguments)}return r._=t,this.on(e,r,n)},emit:function(e){for(var t=[].slice.call(arguments,1),n=((this.e||(this.e={}))[e]||[]).slice(),o=0,r=n.length;ot?Symbol.for(e):Symbol(e),kd=e=>JSON.stringify(e).replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029").replace(/\u0027/g,"\\u0027"),Sd=e=>"number"==typeof e&&isFinite(e),_d=e=>"[object RegExp]"===$d(e),Pd=e=>Nd(e)&&0===Object.keys(e).length,Td=Object.assign;let Ad;const zd=()=>Ad||(Ad="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{});function Rd(e){return e.replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}const Ed=Object.prototype.hasOwnProperty;function Od(e,t){return Ed.call(e,t)}const Md=Array.isArray,Fd=e=>"function"==typeof e,Id=e=>"string"==typeof e,Ld=e=>"boolean"==typeof e,Bd=e=>null!==e&&"object"==typeof e,Dd=Object.prototype.toString,$d=e=>Dd.call(e),Nd=e=>{if(!Bd(e))return!1;const t=Object.getPrototypeOf(e);return null===t||t.constructor===Object};function jd(e){let t=e;return()=>++t}function Hd(e,t){}const Wd=e=>!Bd(e)||Md(e);function Ud(e,t){if(Wd(e)||Wd(t))throw new Error("Invalid value");const n=[{src:e,des:t}];for(;n.length;){const{src:e,des:t}=n.pop();Object.keys(e).forEach((o=>{Wd(e[o])||Wd(t[o])?t[o]=e[o]:n.push({src:e[o],des:t[o]})}))}}function Vd(e,t,n){const o={start:e,end:t};return null!=n&&(o.source=n),o}const qd=/\{([0-9a-zA-Z]+)\}/g;function Kd(e,...t){return 1===t.length&&Yd(t[0])&&(t=t[0]),t&&t.hasOwnProperty||(t={}),e.replace(qd,((e,n)=>t.hasOwnProperty(n)?t[n]:""))}const Gd=Object.assign,Xd=e=>"string"==typeof e,Yd=e=>null!==e&&"object"==typeof e;function Qd(e,t=""){return e.reduce(((e,n,o)=>0===o?e+n:e+t+n),"")}const Zd=1,Jd=2,ep={[Zd]:"Use modulo before '{{0}}'."},tp=1,np=2,op=3,rp=4,ip=5,ap=6,lp=7,sp=8,cp=9,up=10,dp=11,pp=12,hp=13,fp=14,vp=15,mp=16,gp=17,bp={[tp]:"Expected token: '{0}'",[np]:"Invalid token in placeholder: '{0}'",[op]:"Unterminated single quote in placeholder",[rp]:"Unknown escape sequence: \\{0}",[ip]:"Invalid unicode escape sequence: {0}",[ap]:"Unbalanced closing brace",[lp]:"Unterminated closing brace",[sp]:"Empty placeholder",[cp]:"Not allowed nest placeholder",[up]:"Invalid linked format",[dp]:"Plural must have messages",[pp]:"Unexpected empty linked modifier",[hp]:"Unexpected empty linked key",[fp]:"Unexpected lexical analysis in token: '{0}'",[vp]:"unhandled codegen node type: '{0}'",[mp]:"unhandled mimifier node type: '{0}'"};function yp(e,t,n={}){const{domain:o,messages:r,args:i}=n,a=Kd((r||bp)[e]||"",...i||[]),l=new SyntaxError(String(a));return l.code=e,t&&(l.location=t),l.domain=o,l}function xp(e){throw e}const Cp=" ",wp="\n",kp=String.fromCharCode(8232),Sp=String.fromCharCode(8233);function _p(e){const t=e;let n=0,o=1,r=1,i=0;const a=e=>"\r"===t[e]&&t[e+1]===wp,l=e=>t[e]===Sp,s=e=>t[e]===kp,c=e=>a(e)||(e=>t[e]===wp)(e)||l(e)||s(e),u=e=>a(e)||l(e)||s(e)?wp:t[e];function d(){return i=0,c(n)&&(o++,r=0),a(n)&&n++,n++,r++,t[n]}return{index:()=>n,line:()=>o,column:()=>r,peekOffset:()=>i,charAt:u,currentChar:()=>u(n),currentPeek:()=>u(n+i),next:d,peek:function(){return a(n+i)&&i++,i++,t[n+i]},reset:function(){n=0,o=1,r=1,i=0},resetPeek:function(e=0){i=e},skipToPeek:function(){const e=n+i;for(;e!==n;)d();i=0}}}const Pp=void 0;function Tp(e,t={}){const n=!1!==t.location,o=_p(e),r=()=>o.index(),i=()=>{return e=o.line(),t=o.column(),n=o.index(),{line:e,column:t,offset:n};var e,t,n},a=i(),l=r(),s={currentType:14,offset:l,startLoc:a,endLoc:a,lastType:14,lastOffset:l,lastStartLoc:a,lastEndLoc:a,braceNest:0,inLinked:!1,text:""},c=()=>s,{onError:u}=t;function d(e,t,o,...r){const i=c();if(t.column+=o,t.offset+=o,u){const o=yp(e,n?Vd(i.startLoc,t):null,{domain:"tokenizer",args:r});u(o)}}function p(e,t,o){e.endLoc=i(),e.currentType=t;const r={type:t};return n&&(r.loc=Vd(e.startLoc,e.endLoc)),null!=o&&(r.value=o),r}const h=e=>p(e,14);function f(e,t){return e.currentChar()===t?(e.next(),t):(d(tp,i(),0,t),"")}function v(e){let t="";for(;e.currentPeek()===Cp||e.currentPeek()===wp;)t+=e.currentPeek(),e.peek();return t}function m(e){const t=v(e);return e.skipToPeek(),t}function g(e){if(e===Pp)return!1;const t=e.charCodeAt(0);return t>=97&&t<=122||t>=65&&t<=90||95===t}function b(e,t){const{currentType:n}=t;if(2!==n)return!1;v(e);const o=function(e){if(e===Pp)return!1;const t=e.charCodeAt(0);return t>=48&&t<=57}("-"===e.currentPeek()?e.peek():e.currentPeek());return e.resetPeek(),o}function y(e){v(e);const t="|"===e.currentPeek();return e.resetPeek(),t}function x(e,t=!0){const n=(t=!1,o="",r=!1)=>{const i=e.currentPeek();return"{"===i?"%"!==o&&t:"@"!==i&&i?"%"===i?(e.peek(),n(t,"%",!0)):"|"===i?!("%"!==o&&!r&&(o===Cp||o===wp)):i===Cp?(e.peek(),n(!0,Cp,r)):i!==wp||(e.peek(),n(!0,wp,r)):"%"===o||t},o=n();return t&&e.resetPeek(),o}function C(e,t){const n=e.currentChar();return n===Pp?Pp:t(n)?(e.next(),n):null}function w(e){const t=e.charCodeAt(0);return t>=97&&t<=122||t>=65&&t<=90||t>=48&&t<=57||95===t||36===t}function k(e){return C(e,w)}function S(e){const t=e.charCodeAt(0);return t>=97&&t<=122||t>=65&&t<=90||t>=48&&t<=57||95===t||36===t||45===t}function _(e){return C(e,S)}function P(e){const t=e.charCodeAt(0);return t>=48&&t<=57}function T(e){return C(e,P)}function A(e){const t=e.charCodeAt(0);return t>=48&&t<=57||t>=65&&t<=70||t>=97&&t<=102}function z(e){return C(e,A)}function R(e){let t="",n="";for(;t=T(e);)n+=t;return n}function E(e){let t="";for(;;){const n=e.currentChar();if("{"===n||"}"===n||"@"===n||"|"===n||!n)break;if("%"===n){if(!x(e))break;t+=n,e.next()}else if(n===Cp||n===wp)if(x(e))t+=n,e.next();else{if(y(e))break;t+=n,e.next()}else t+=n,e.next()}return t}function O(e){return"'"!==e&&e!==wp}function M(e){const t=e.currentChar();switch(t){case"\\":case"'":return e.next(),`\\${t}`;case"u":return F(e,t,4);case"U":return F(e,t,6);default:return d(rp,i(),0,t),""}}function F(e,t,n){f(e,t);let o="";for(let r=0;r=1&&d(cp,i(),0),e.next(),n=p(t,2,"{"),m(e),t.braceNest++,n;case"}":return t.braceNest>0&&2===t.currentType&&d(sp,i(),0),e.next(),n=p(t,3,"}"),t.braceNest--,t.braceNest>0&&m(e),t.inLinked&&0===t.braceNest&&(t.inLinked=!1),n;case"@":return t.braceNest>0&&d(lp,i(),0),n=D(e,t)||h(t),t.braceNest=0,n;default:{let o=!0,r=!0,a=!0;if(y(e))return t.braceNest>0&&d(lp,i(),0),n=p(t,1,L(e)),t.braceNest=0,t.inLinked=!1,n;if(t.braceNest>0&&(5===t.currentType||6===t.currentType||7===t.currentType))return d(lp,i(),0),t.braceNest=0,$(e,t);if(o=function(e,t){const{currentType:n}=t;if(2!==n)return!1;v(e);const o=g(e.currentPeek());return e.resetPeek(),o}(e,t))return n=p(t,5,function(e){m(e);let t="",n="";for(;t=_(e);)n+=t;return e.currentChar()===Pp&&d(lp,i(),0),n}(e)),m(e),n;if(r=b(e,t))return n=p(t,6,function(e){m(e);let t="";return"-"===e.currentChar()?(e.next(),t+=`-${R(e)}`):t+=R(e),e.currentChar()===Pp&&d(lp,i(),0),t}(e)),m(e),n;if(a=function(e,t){const{currentType:n}=t;if(2!==n)return!1;v(e);const o="'"===e.currentPeek();return e.resetPeek(),o}(e,t))return n=p(t,7,function(e){m(e),f(e,"'");let t="",n="";for(;t=C(e,O);)n+="\\"===t?M(e):t;const o=e.currentChar();return o===wp||o===Pp?(d(op,i(),0),o===wp&&(e.next(),f(e,"'")),n):(f(e,"'"),n)}(e)),m(e),n;if(!o&&!r&&!a)return n=p(t,13,function(e){m(e);let t="",n="";for(;t=C(e,I);)n+=t;return n}(e)),d(np,i(),0,n.value),m(e),n;break}}return n}function D(e,t){const{currentType:n}=t;let o=null;const r=e.currentChar();switch(8!==n&&9!==n&&12!==n&&10!==n||r!==wp&&r!==Cp||d(up,i(),0),r){case"@":return e.next(),o=p(t,8,"@"),t.inLinked=!0,o;case".":return m(e),e.next(),p(t,9,".");case":":return m(e),e.next(),p(t,10,":");default:return y(e)?(o=p(t,1,L(e)),t.braceNest=0,t.inLinked=!1,o):function(e,t){const{currentType:n}=t;if(8!==n)return!1;v(e);const o="."===e.currentPeek();return e.resetPeek(),o}(e,t)||function(e,t){const{currentType:n}=t;if(8!==n&&12!==n)return!1;v(e);const o=":"===e.currentPeek();return e.resetPeek(),o}(e,t)?(m(e),D(e,t)):function(e,t){const{currentType:n}=t;if(9!==n)return!1;v(e);const o=g(e.currentPeek());return e.resetPeek(),o}(e,t)?(m(e),p(t,12,function(e){let t="",n="";for(;t=k(e);)n+=t;return n}(e))):function(e,t){const{currentType:n}=t;if(10!==n)return!1;const o=()=>{const t=e.currentPeek();return"{"===t?g(e.peek()):!("@"===t||"%"===t||"|"===t||":"===t||"."===t||t===Cp||!t)&&(t===wp?(e.peek(),o()):x(e,!1))},r=o();return e.resetPeek(),r}(e,t)?(m(e),"{"===r?B(e,t)||o:p(t,11,function(e){const t=n=>{const o=e.currentChar();return"{"!==o&&"%"!==o&&"@"!==o&&"|"!==o&&"("!==o&&")"!==o&&o?o===Cp?n:(n+=o,e.next(),t(n)):n};return t("")}(e))):(8===n&&d(up,i(),0),t.braceNest=0,t.inLinked=!1,$(e,t))}}function $(e,t){let n={type:14};if(t.braceNest>0)return B(e,t)||h(t);if(t.inLinked)return D(e,t)||h(t);switch(e.currentChar()){case"{":return B(e,t)||h(t);case"}":return d(ap,i(),0),e.next(),p(t,3,"}");case"@":return D(e,t)||h(t);default:{if(y(e))return n=p(t,1,L(e)),t.braceNest=0,t.inLinked=!1,n;const{isModulo:o,hasSpace:r}=function(e){const t=v(e),n="%"===e.currentPeek()&&"{"===e.peek();return e.resetPeek(),{isModulo:n,hasSpace:t.length>0}}(e);if(o)return r?p(t,0,E(e)):p(t,4,function(e){m(e);const t=e.currentChar();return"%"!==t&&d(tp,i(),0,t),e.next(),"%"}(e));if(x(e))return p(t,0,E(e));break}}return n}return{nextToken:function(){const{currentType:e,offset:t,startLoc:n,endLoc:a}=s;return s.lastType=e,s.lastOffset=t,s.lastStartLoc=n,s.lastEndLoc=a,s.offset=r(),s.startLoc=i(),o.currentChar()===Pp?p(s,14):$(o,s)},currentOffset:r,currentPosition:i,context:c}}const Ap=/(?:\\\\|\\'|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g;function zp(e,t,n){switch(e){case"\\\\":return"\\";case"\\'":return"'";default:{const e=parseInt(t||n,16);return e<=55295||e>=57344?String.fromCodePoint(e):"�"}}}function Rp(e={}){const t=!1!==e.location,{onError:n,onWarn:o}=e;function r(e,o,r,i,...a){const l=e.currentPosition();if(l.offset+=i,l.column+=i,n){const e=yp(o,t?Vd(r,l):null,{domain:"parser",args:a});n(e)}}function i(e,n,r,i,...a){const l=e.currentPosition();if(l.offset+=i,l.column+=i,o){const e=t?Vd(r,l):null;o(function(e,t,...n){const o=Kd(ep[e]||"",...n||[]),r={message:String(o),code:e};return t&&(r.location=t),r}(n,e,a))}}function a(e,n,o){const r={type:e};return t&&(r.start=n,r.end=n,r.loc={start:o,end:o}),r}function l(e,n,o,r){r&&(e.type=r),t&&(e.end=n,e.loc&&(e.loc.end=o))}function s(e,t){const n=e.context(),o=a(3,n.offset,n.startLoc);return o.value=t,l(o,e.currentOffset(),e.currentPosition()),o}function c(e,t){const n=e.context(),{lastOffset:o,lastStartLoc:r}=n,i=a(5,o,r);return i.index=parseInt(t,10),e.nextToken(),l(i,e.currentOffset(),e.currentPosition()),i}function u(e,t,n){const o=e.context(),{lastOffset:r,lastStartLoc:i}=o,s=a(4,r,i);return s.key=t,!0===n&&(s.modulo=!0),e.nextToken(),l(s,e.currentOffset(),e.currentPosition()),s}function d(e,t){const n=e.context(),{lastOffset:o,lastStartLoc:r}=n,i=a(9,o,r);return i.value=t.replace(Ap,zp),e.nextToken(),l(i,e.currentOffset(),e.currentPosition()),i}function p(e){const t=e.context(),n=a(6,t.offset,t.startLoc);let o=e.nextToken();if(9===o.type){const t=function(e){const t=e.nextToken(),n=e.context(),{lastOffset:o,lastStartLoc:i}=n,s=a(8,o,i);return 12!==t.type?(r(e,pp,n.lastStartLoc,0),s.value="",l(s,o,i),{nextConsumeToken:t,node:s}):(null==t.value&&r(e,fp,n.lastStartLoc,0,Ep(t)),s.value=t.value||"",l(s,e.currentOffset(),e.currentPosition()),{node:s})}(e);n.modifier=t.node,o=t.nextConsumeToken||e.nextToken()}switch(10!==o.type&&r(e,fp,t.lastStartLoc,0,Ep(o)),o=e.nextToken(),2===o.type&&(o=e.nextToken()),o.type){case 11:null==o.value&&r(e,fp,t.lastStartLoc,0,Ep(o)),n.key=function(e,t){const n=e.context(),o=a(7,n.offset,n.startLoc);return o.value=t,l(o,e.currentOffset(),e.currentPosition()),o}(e,o.value||"");break;case 5:null==o.value&&r(e,fp,t.lastStartLoc,0,Ep(o)),n.key=u(e,o.value||"");break;case 6:null==o.value&&r(e,fp,t.lastStartLoc,0,Ep(o)),n.key=c(e,o.value||"");break;case 7:null==o.value&&r(e,fp,t.lastStartLoc,0,Ep(o)),n.key=d(e,o.value||"");break;default:{r(e,hp,t.lastStartLoc,0);const i=e.context(),s=a(7,i.offset,i.startLoc);return s.value="",l(s,i.offset,i.startLoc),n.key=s,l(n,i.offset,i.startLoc),{nextConsumeToken:o,node:n}}}return l(n,e.currentOffset(),e.currentPosition()),{node:n}}function h(e){const t=e.context(),n=a(2,1===t.currentType?e.currentOffset():t.offset,1===t.currentType?t.endLoc:t.startLoc);n.items=[];let o=null,h=null;do{const a=o||e.nextToken();switch(o=null,a.type){case 0:null==a.value&&r(e,fp,t.lastStartLoc,0,Ep(a)),n.items.push(s(e,a.value||""));break;case 6:null==a.value&&r(e,fp,t.lastStartLoc,0,Ep(a)),n.items.push(c(e,a.value||""));break;case 4:h=!0;break;case 5:null==a.value&&r(e,fp,t.lastStartLoc,0,Ep(a)),n.items.push(u(e,a.value||"",!!h)),h&&(i(e,Zd,t.lastStartLoc,0,Ep(a)),h=null);break;case 7:null==a.value&&r(e,fp,t.lastStartLoc,0,Ep(a)),n.items.push(d(e,a.value||""));break;case 8:{const t=p(e);n.items.push(t.node),o=t.nextConsumeToken||null;break}}}while(14!==t.currentType&&1!==t.currentType);return l(n,1===t.currentType?t.lastOffset:e.currentOffset(),1===t.currentType?t.lastEndLoc:e.currentPosition()),n}function f(e){const t=e.context(),{offset:n,startLoc:o}=t,i=h(e);return 14===t.currentType?i:function(e,t,n,o){const i=e.context();let s=0===o.items.length;const c=a(1,t,n);c.cases=[],c.cases.push(o);do{const t=h(e);s||(s=0===t.items.length),c.cases.push(t)}while(14!==i.currentType);return s&&r(e,dp,n,0),l(c,e.currentOffset(),e.currentPosition()),c}(e,n,o,i)}return{parse:function(n){const o=Tp(n,Gd({},e)),i=o.context(),s=a(0,i.offset,i.startLoc);return t&&s.loc&&(s.loc.source=n),s.body=f(o),e.onCacheKey&&(s.cacheKey=e.onCacheKey(n)),14!==i.currentType&&r(o,fp,i.lastStartLoc,0,n[i.offset]||""),l(s,o.currentOffset(),o.currentPosition()),s}}}function Ep(e){if(14===e.type)return"EOF";const t=(e.value||"").replace(/\r?\n/gu,"\\n");return t.length>10?t.slice(0,9)+"…":t}function Op(e,t){for(let n=0;nn,helper:e=>(n.helpers.add(e),e)}}(e);n.helper("normalize"),e.body&&Mp(e.body,n);const o=n.context();e.helpers=Array.from(o.helpers)}function Ip(e){if(1===e.items.length){const t=e.items[0];3!==t.type&&9!==t.type||(e.static=t.value,delete t.value)}else{const t=[];for(let n=0;n1){e.push(`${n("plural")}([`),e.indent(o());const r=t.cases.length;for(let n=0;nIp(e)))}(a),r&&Lp(a),{ast:a,code:""}):(Fp(a,n),((e,t={})=>{const n=Xd(t.mode)?t.mode:"normal",o=Xd(t.filename)?t.filename:"message.intl",r=!!t.sourceMap,i=null!=t.breakLineCode?t.breakLineCode:"arrow"===n?";":"\n",a=t.needIndent?t.needIndent:"arrow"!==n,l=e.helpers||[],s=function(e,t){const{sourceMap:n,filename:o,breakLineCode:r,needIndent:i}=t,a=!1!==t.location,l={filename:o,code:"",column:1,line:1,offset:0,map:void 0,breakLineCode:r,needIndent:i,indentLevel:0};function s(e,t){l.code+=e}function c(e,t=!0){const n=t?r:"";s(i?n+" ".repeat(e):n)}return a&&e.loc&&(l.source=e.loc.source),{context:()=>l,push:s,indent:function(e=!0){const t=++l.indentLevel;e&&c(t)},deindent:function(e=!0){const t=--l.indentLevel;e&&c(t)},newline:function(){c(l.indentLevel)},helper:e=>`_${e}`,needIndent:()=>l.needIndent}}(e,{mode:n,filename:o,sourceMap:r,breakLineCode:i,needIndent:a});s.push("normal"===n?"function __msg__ (ctx) {":"(ctx) => {"),s.indent(a),l.length>0&&(s.push(`const { ${Qd(l.map((e=>`${e}: _${e}`)),", ")} } = ctx`),s.newline()),s.push("return "),Bp(s,e),s.deindent(a),s.push("}"),delete e.helpers;const{code:c,map:u}=s.context();return{ast:e,code:c,map:u?u.toJSON():void 0}})(a,n))}const $p=[];$p[0]={w:[0],i:[3,0],"[":[4],o:[7]},$p[1]={w:[1],".":[2],"[":[4],o:[7]},$p[2]={w:[2],i:[3,0],0:[3,0]},$p[3]={i:[3,0],0:[3,0],w:[1,1],".":[2,1],"[":[4,1],o:[7,1]},$p[4]={"'":[5,0],'"':[6,0],"[":[4,2],"]":[1,3],o:8,l:[4,0]},$p[5]={"'":[4,0],o:8,l:[5,0]},$p[6]={'"':[4,0],o:8,l:[6,0]};const Np=/^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;function jp(e){if(null==e)return"o";switch(e.charCodeAt(0)){case 91:case 93:case 46:case 34:case 39:return e;case 95:case 36:case 45:return"i";case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"w"}return"i"}function Hp(e){const t=e.trim();return("0"!==e.charAt(0)||!isNaN(parseInt(e)))&&(n=t,Np.test(n)?function(e){const t=e.charCodeAt(0);return t!==e.charCodeAt(e.length-1)||34!==t&&39!==t?e:e.slice(1,-1)}(t):"*"+t);var n}const Wp=new Map;function Up(e,t){return Bd(e)?e[t]:null}const Vp=e=>e,qp=e=>"",Kp=e=>0===e.length?"":function(e,t=""){return e.reduce(((e,n,o)=>0===o?e+n:e+t+n),"")}(e),Gp=e=>null==e?"":Md(e)||Nd(e)&&e.toString===Dd?JSON.stringify(e,null,2):String(e);function Xp(e,t){return e=Math.abs(e),2===t?e?e>1?1:0:1:e?Math.min(e,2):0}function Yp(e={}){const t=e.locale,n=function(e){const t=Sd(e.pluralIndex)?e.pluralIndex:-1;return e.named&&(Sd(e.named.count)||Sd(e.named.n))?Sd(e.named.count)?e.named.count:Sd(e.named.n)?e.named.n:t:t}(e),o=Bd(e.pluralRules)&&Id(t)&&Fd(e.pluralRules[t])?e.pluralRules[t]:Xp,r=Bd(e.pluralRules)&&Id(t)&&Fd(e.pluralRules[t])?Xp:void 0,i=e.list||[],a=e.named||{};function l(t){const n=Fd(e.messages)?e.messages(t):!!Bd(e.messages)&&e.messages[t];return n||(e.parent?e.parent.message(t):qp)}Sd(e.pluralIndex)&&function(e,t){t.count||(t.count=e),t.n||(t.n=e)}(n,a);const s=Nd(e.processor)&&Fd(e.processor.normalize)?e.processor.normalize:Kp,c=Nd(e.processor)&&Fd(e.processor.interpolate)?e.processor.interpolate:Gp,u={list:e=>i[e],named:e=>a[e],plural:e=>e[o(n,e.length,r)],linked:(t,...n)=>{const[o,r]=n;let i="text",a="";1===n.length?Bd(o)?(a=o.modifier||a,i=o.type||i):Id(o)&&(a=o||a):2===n.length&&(Id(o)&&(a=o||a),Id(r)&&(i=r||i));const s=l(t)(u),c="vnode"===i&&Md(s)&&a?s[0]:s;return a?(d=a,e.modifiers?e.modifiers[d]:Vp)(c,i):c;var d},message:l,type:Nd(e.processor)&&Id(e.processor.type)?e.processor.type:"text",interpolate:c,normalize:s,values:Td({},i,a)};return u}let Qp=null;const Zp=Jp("function:translate");function Jp(e){return t=>Qp&&Qp.emit(e,t)}const eh=Jd,th=jd(eh),nh={NOT_FOUND_KEY:eh,FALLBACK_TO_TRANSLATE:th(),CANNOT_FORMAT_NUMBER:th(),FALLBACK_TO_NUMBER_FORMAT:th(),CANNOT_FORMAT_DATE:th(),FALLBACK_TO_DATE_FORMAT:th(),EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER:th(),__EXTEND_POINT__:th()},oh=gp,rh=jd(oh),ih={INVALID_ARGUMENT:oh,INVALID_DATE_ARGUMENT:rh(),INVALID_ISO_DATE_ARGUMENT:rh(),NOT_SUPPORT_NON_STRING_MESSAGE:rh(),NOT_SUPPORT_LOCALE_PROMISE_VALUE:rh(),NOT_SUPPORT_LOCALE_ASYNC_FUNCTION:rh(),NOT_SUPPORT_LOCALE_TYPE:rh(),__EXTEND_POINT__:rh()};function ah(e){return yp(e,null,void 0)}function lh(e,t){return null!=t.locale?ch(t.locale):ch(e.locale)}let sh;function ch(e){if(Id(e))return e;if(Fd(e)){if(e.resolvedOnce&&null!=sh)return sh;if("Function"===e.constructor.name){const n=e();if(Bd(t=n)&&Fd(t.then)&&Fd(t.catch))throw ah(ih.NOT_SUPPORT_LOCALE_PROMISE_VALUE);return sh=n}throw ah(ih.NOT_SUPPORT_LOCALE_ASYNC_FUNCTION)}throw ah(ih.NOT_SUPPORT_LOCALE_TYPE);var t}function uh(e,t,n){return[...new Set([n,...Md(t)?t:Bd(t)?Object.keys(t):Id(t)?[t]:[n]])]}function dh(e,t,n){const o=Id(n)?n:vh,r=e;r.__localeChainCache||(r.__localeChainCache=new Map);let i=r.__localeChainCache.get(o);if(!i){i=[];let e=[n];for(;Md(e);)e=ph(i,e,t);const a=Md(t)||!Nd(t)?t:t.default?t.default:null;e=Id(a)?[a]:a,Md(e)&&ph(i,e,!1),r.__localeChainCache.set(o,i)}return i}function ph(e,t,n){let o=!0;for(let r=0;r`${e.charAt(0).toLocaleUpperCase()}${e.substr(1)}`;let gh,bh,yh;function xh(e){gh=e}let Ch=null;const wh=()=>Ch;let kh=null;const Sh=e=>{kh=e};let _h=0;function Ph(e={}){const t=Fd(e.onWarn)?e.onWarn:Hd,n=Id(e.version)?e.version:"9.14.0",o=Id(e.locale)||Fd(e.locale)?e.locale:vh,r=Fd(o)?vh:o,i=Md(e.fallbackLocale)||Nd(e.fallbackLocale)||Id(e.fallbackLocale)||!1===e.fallbackLocale?e.fallbackLocale:r,a=Nd(e.messages)?e.messages:{[r]:{}},l=Nd(e.datetimeFormats)?e.datetimeFormats:{[r]:{}},s=Nd(e.numberFormats)?e.numberFormats:{[r]:{}},c=Td({},e.modifiers||{},{upper:(e,t)=>"text"===t&&Id(e)?e.toUpperCase():"vnode"===t&&Bd(e)&&"__v_isVNode"in e?e.children.toUpperCase():e,lower:(e,t)=>"text"===t&&Id(e)?e.toLowerCase():"vnode"===t&&Bd(e)&&"__v_isVNode"in e?e.children.toLowerCase():e,capitalize:(e,t)=>"text"===t&&Id(e)?mh(e):"vnode"===t&&Bd(e)&&"__v_isVNode"in e?mh(e.children):e}),u=e.pluralRules||{},d=Fd(e.missing)?e.missing:null,p=!Ld(e.missingWarn)&&!_d(e.missingWarn)||e.missingWarn,h=!Ld(e.fallbackWarn)&&!_d(e.fallbackWarn)||e.fallbackWarn,f=!!e.fallbackFormat,v=!!e.unresolving,m=Fd(e.postTranslation)?e.postTranslation:null,g=Nd(e.processor)?e.processor:null,b=!Ld(e.warnHtmlMessage)||e.warnHtmlMessage,y=!!e.escapeParameter,x=Fd(e.messageCompiler)?e.messageCompiler:gh,C=Fd(e.messageResolver)?e.messageResolver:bh||Up,w=Fd(e.localeFallbacker)?e.localeFallbacker:yh||uh,k=Bd(e.fallbackContext)?e.fallbackContext:void 0,S=e,_=Bd(S.__datetimeFormatters)?S.__datetimeFormatters:new Map,P=Bd(S.__numberFormatters)?S.__numberFormatters:new Map,T=Bd(S.__meta)?S.__meta:{};_h++;const A={version:n,cid:_h,locale:o,fallbackLocale:i,messages:a,modifiers:c,pluralRules:u,missing:d,missingWarn:p,fallbackWarn:h,fallbackFormat:f,unresolving:v,postTranslation:m,processor:g,warnHtmlMessage:b,escapeParameter:y,messageCompiler:x,messageResolver:C,localeFallbacker:w,fallbackContext:k,onWarn:t,__meta:T};return A.datetimeFormats=l,A.numberFormats=s,A.__datetimeFormatters=_,A.__numberFormatters=P,__INTLIFY_PROD_DEVTOOLS__&&function(e,t,n){Qp&&Qp.emit("i18n:init",{timestamp:Date.now(),i18n:e,version:t,meta:n})}(A,n,T),A}function Th(e,t,n,o,r){const{missing:i,onWarn:a}=e;if(null!==i){const o=i(e,n,t,r);return Id(o)?o:t}return t}function Ah(e,t,n){e.__localeChainCache=new Map,e.localeFallbacker(e,n,t)}function zh(e,t){const n=t.indexOf(e);if(-1===n)return!1;for(let i=n+1;ifunction(e,t){const n=t.b||t.body;if(1===(n.t||n.type)){const t=n,o=t.c||t.cases;return e.plural(o.reduce(((t,n)=>[...t,Eh(e,n)]),[]))}return Eh(e,n)}(t,e)}function Eh(e,t){const n=t.s||t.static;if(n)return"text"===e.type?n:e.normalize([n]);{const n=(t.i||t.items).reduce(((t,n)=>[...t,Oh(e,n)]),[]);return e.normalize(n)}}function Oh(e,t){const n=t.t||t.type;switch(n){case 3:{const e=t;return e.v||e.value}case 9:{const e=t;return e.v||e.value}case 4:{const n=t;return e.interpolate(e.named(n.k||n.key))}case 5:{const n=t;return e.interpolate(e.list(null!=n.i?n.i:n.index))}case 6:{const n=t,o=n.m||n.modifier;return e.linked(Oh(e,n.k||n.key),o?Oh(e,o):void 0,e.type)}case 7:{const e=t;return e.v||e.value}case 8:{const e=t;return e.v||e.value}default:throw new Error(`unhandled node type on format message part: ${n}`)}}const Mh=e=>e;let Fh=Object.create(null);const Ih=e=>Bd(e)&&(0===e.t||0===e.type)&&("b"in e||"body"in e);function Lh(e,t={}){let n=!1;const o=t.onError||xp;return t.onError=e=>{n=!0,o(e)},p(d({},Dp(e,t)),{detectError:n})}const Bh=(e,t)=>{if(!Id(e))throw ah(ih.NOT_SUPPORT_NON_STRING_MESSAGE);{!Ld(t.warnHtmlMessage)||t.warnHtmlMessage;const n=(t.onCacheKey||Mh)(e),o=Fh[n];if(o)return o;const{code:r,detectError:i}=Lh(e,t),a=new Function(`return ${r}`)();return i?a:Fh[n]=a}},Dh=()=>"",$h=e=>Fd(e);function Nh(e,...t){const{fallbackFormat:n,postTranslation:o,unresolving:r,messageCompiler:i,fallbackLocale:a,messages:l}=e,[s,c]=Wh(...t),u=(Ld(c.missingWarn)?c.missingWarn:e.missingWarn,Ld(c.fallbackWarn)?c.fallbackWarn:e.fallbackWarn,Ld(c.escapeParameter)?c.escapeParameter:e.escapeParameter),d=!!c.resolvedMessage,p=Id(c.default)||Ld(c.default)?Ld(c.default)?i?s:()=>s:c.default:n?i?s:()=>s:"",h=n||""!==p,f=lh(e,c);u&&function(e){Md(e.list)?e.list=e.list.map((e=>Id(e)?Rd(e):e)):Bd(e.named)&&Object.keys(e.named).forEach((t=>{Id(e.named[t])&&(e.named[t]=Rd(e.named[t]))}))}(c);let[v,m,g]=d?[s,f,l[f]||{}]:jh(e,s,f,a),b=v,y=s;if(d||Id(b)||Ih(b)||$h(b)||h&&(b=p,y=b),!(d||(Id(b)||Ih(b)||$h(b))&&Id(m)))return r?-1:s;let x=!1;const C=$h(b)?b:Hh(e,s,m,b,y,(()=>{x=!0}));if(x)return b;const w=function(e,t,n,o){const{modifiers:r,pluralRules:i,messageResolver:a,fallbackLocale:l,fallbackWarn:s,missingWarn:c,fallbackContext:u}=e,d=o=>{let r=a(n,o);if(null==r&&u){const[,,e]=jh(u,o,t,l);r=a(e,o)}if(Id(r)||Ih(r)){let n=!1;const i=Hh(e,o,t,r,o,(()=>{n=!0}));return n?Dh:i}return $h(r)?r:Dh},p={locale:t,modifiers:r,pluralRules:i,messages:d};return e.processor&&(p.processor=e.processor),o.list&&(p.list=o.list),o.named&&(p.named=o.named),Sd(o.plural)&&(p.pluralIndex=o.plural),p}(e,m,g,c),k=function(e,t,n){const o=t(n);return o}(0,C,Yp(w)),S=o?o(k,s):k;if(__INTLIFY_PROD_DEVTOOLS__){const t={timestamp:Date.now(),key:Id(s)?s:$h(b)?b.key:"",locale:m||($h(b)?b.locale:""),format:Id(b)?b:$h(b)?b.source:"",message:S};t.meta=Td({},e.__meta,wh()||{}),Zp(t)}return S}function jh(e,t,n,o,r,i){const{messages:a,onWarn:l,messageResolver:s,localeFallbacker:c}=e,u=c(e,o,n);let d,p={},h=null;for(let f=0;fo;return e.locale=n,e.key=t,e}const s=a(o,function(e,t,n,o,r,i){return{locale:t,key:n,warnHtmlMessage:r,onError:e=>{throw i&&i(e),e},onCacheKey:e=>((e,t,n)=>kd({l:e,k:t,s:n}))(t,n,e)}}(0,n,r,0,l,i));return s.locale=n,s.key=t,s.source=o,s}function Wh(...e){const[t,n,o]=e,r={};if(!(Id(t)||Sd(t)||$h(t)||Ih(t)))throw ah(ih.INVALID_ARGUMENT);const i=Sd(t)?String(t):($h(t),t);return Sd(n)?r.plural=n:Id(n)?r.default=n:Nd(n)&&!Pd(n)?r.named=n:Md(n)&&(r.list=n),Sd(o)?r.plural=o:Id(o)?r.default=o:Nd(o)&&Td(r,o),[i,r]}function Uh(e,...t){const{datetimeFormats:n,unresolving:o,fallbackLocale:r,onWarn:i,localeFallbacker:a}=e,{__datetimeFormatters:l}=e,[s,c,u,d]=qh(...t);Ld(u.missingWarn)?u.missingWarn:e.missingWarn,Ld(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn;const p=!!u.part,h=lh(e,u),f=a(e,r,h);if(!Id(s)||""===s)return new Intl.DateTimeFormat(h,d).format(c);let v,m={},g=null;for(let x=0;x{Vh.includes(e)?l[e]=n[e]:i[e]=n[e]})),Id(o)?i.locale=o:Nd(o)&&(l=o),Nd(r)&&(l=r),[i.key||"",a,i,l]}function Kh(e,t,n){const o=e;for(const r in n){const e=`${t}__${r}`;o.__datetimeFormatters.has(e)&&o.__datetimeFormatters.delete(e)}}function Gh(e,...t){const{numberFormats:n,unresolving:o,fallbackLocale:r,onWarn:i,localeFallbacker:a}=e,{__numberFormatters:l}=e,[s,c,u,d]=Yh(...t);Ld(u.missingWarn)?u.missingWarn:e.missingWarn,Ld(u.fallbackWarn)?u.fallbackWarn:e.fallbackWarn;const p=!!u.part,h=lh(e,u),f=a(e,r,h);if(!Id(s)||""===s)return new Intl.NumberFormat(h,d).format(c);let v,m={},g=null;for(let x=0;x{Xh.includes(e)?a[e]=n[e]:i[e]=n[e]})),Id(o)?i.locale=o:Nd(o)&&(a=o),Nd(r)&&(a=r),[i.key||"",l,i,a]}function Qh(e,t,n){const o=e;for(const r in n){const e=`${t}__${r}`;o.__numberFormatters.has(e)&&o.__numberFormatters.delete(e)}}"boolean"!=typeof __INTLIFY_PROD_DEVTOOLS__&&(zd().__INTLIFY_PROD_DEVTOOLS__=!1),"boolean"!=typeof __INTLIFY_JIT_COMPILATION__&&(zd().__INTLIFY_JIT_COMPILATION__=!1),"boolean"!=typeof __INTLIFY_DROP_MESSAGE_COMPILER__&&(zd().__INTLIFY_DROP_MESSAGE_COMPILER__=!1);const Zh=nh.__EXTEND_POINT__,Jh=jd(Zh);Jh(),Jh(),Jh(),Jh(),Jh(),Jh(),Jh(),Jh(),Jh();const ef=ih.__EXTEND_POINT__,tf=jd(ef),nf={UNEXPECTED_RETURN_TYPE:ef,INVALID_ARGUMENT:tf(),MUST_BE_CALL_SETUP_TOP:tf(),NOT_INSTALLED:tf(),NOT_AVAILABLE_IN_LEGACY_MODE:tf(),REQUIRED_VALUE:tf(),INVALID_VALUE:tf(),CANNOT_SETUP_VUE_DEVTOOLS_PLUGIN:tf(),NOT_INSTALLED_WITH_PROVIDE:tf(),UNEXPECTED_ERROR:tf(),NOT_COMPATIBLE_LEGACY_VUE_I18N:tf(),BRIDGE_SUPPORT_VUE_2_ONLY:tf(),MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION:tf(),NOT_AVAILABLE_COMPOSITION_IN_LEGACY:tf(),__EXTEND_POINT__:tf()};function of(e,...t){return yp(e,null,void 0)}const rf=wd("__translateVNode"),af=wd("__datetimeParts"),lf=wd("__numberParts"),sf=wd("__setPluralRules"),cf=wd("__injectWithOption"),uf=wd("__dispose");function df(e){if(!Bd(e))return e;for(const t in e)if(Od(e,t))if(t.includes(".")){const n=t.split("."),o=n.length-1;let r=e,i=!1;for(let e=0;e{if("locale"in e&&"resource"in e){const{locale:t,resource:n}=e;t?(a[t]=a[t]||{},Ud(n,a[t])):Ud(n,a)}else Id(e)&&Ud(JSON.parse(e),a)})),null==r&&i)for(const l in a)Od(a,l)&&df(a[l]);return a}function hf(e){return e.type}function ff(e,t,n){let o=Bd(t.messages)?t.messages:{};"__i18nGlobal"in n&&(o=pf(e.locale.value,{messages:o,__i18n:n.__i18nGlobal}));const r=Object.keys(o);if(r.length&&r.forEach((t=>{e.mergeLocaleMessage(t,o[t])})),Bd(t.datetimeFormats)){const n=Object.keys(t.datetimeFormats);n.length&&n.forEach((n=>{e.mergeDateTimeFormat(n,t.datetimeFormats[n])}))}if(Bd(t.numberFormats)){const n=Object.keys(t.numberFormats);n.length&&n.forEach((n=>{e.mergeNumberFormat(n,t.numberFormats[n])}))}}function vf(e){return Lr(xr,null,e,0)}const mf=()=>[],gf=()=>!1;let bf=0;function yf(e){return(t,n,o,r)=>e(n,o,Gr()||void 0,r)}function xf(e={},t){const{__root:n,__injectWithOption:o}=e,r=void 0===n,i=e.flatJson,a=Cd?Et:Ot,l=!!e.translateExistCompatible;let s=!Ld(e.inheritLocale)||e.inheritLocale;const c=a(n&&s?n.locale.value:Id(e.locale)?e.locale:vh),u=a(n&&s?n.fallbackLocale.value:Id(e.fallbackLocale)||Md(e.fallbackLocale)||Nd(e.fallbackLocale)||!1===e.fallbackLocale?e.fallbackLocale:c.value),d=a(pf(c.value,e)),p=a(Nd(e.datetimeFormats)?e.datetimeFormats:{[c.value]:{}}),h=a(Nd(e.numberFormats)?e.numberFormats:{[c.value]:{}});let f=n?n.missingWarn:!Ld(e.missingWarn)&&!_d(e.missingWarn)||e.missingWarn,v=n?n.fallbackWarn:!Ld(e.fallbackWarn)&&!_d(e.fallbackWarn)||e.fallbackWarn,m=n?n.fallbackRoot:!Ld(e.fallbackRoot)||e.fallbackRoot,g=!!e.fallbackFormat,b=Fd(e.missing)?e.missing:null,y=Fd(e.missing)?yf(e.missing):null,x=Fd(e.postTranslation)?e.postTranslation:null,C=n?n.warnHtmlMessage:!Ld(e.warnHtmlMessage)||e.warnHtmlMessage,w=!!e.escapeParameter;const k=n?n.modifiers:Nd(e.modifiers)?e.modifiers:{};let S,_=e.pluralRules||n&&n.pluralRules;S=(()=>{r&&Sh(null);const t={version:"9.14.0",locale:c.value,fallbackLocale:u.value,messages:d.value,modifiers:k,pluralRules:_,missing:null===y?void 0:y,missingWarn:f,fallbackWarn:v,fallbackFormat:g,unresolving:!0,postTranslation:null===x?void 0:x,warnHtmlMessage:C,escapeParameter:w,messageResolver:e.messageResolver,messageCompiler:e.messageCompiler,__meta:{framework:"vue"}};t.datetimeFormats=p.value,t.numberFormats=h.value,t.__datetimeFormatters=Nd(S)?S.__datetimeFormatters:void 0,t.__numberFormatters=Nd(S)?S.__numberFormatters:void 0;const n=Ph(t);return r&&Sh(n),n})(),Ah(S,c.value,u.value);const P=ai({get:()=>c.value,set:e=>{c.value=e,S.locale=c.value}}),T=ai({get:()=>u.value,set:e=>{u.value=e,S.fallbackLocale=u.value,Ah(S,c.value,e)}}),A=ai((()=>d.value)),z=ai((()=>p.value)),R=ai((()=>h.value)),E=(e,t,o,i,a,l)=>{let s;c.value,u.value,d.value,p.value,h.value;try{__INTLIFY_PROD_DEVTOOLS__,r||(S.fallbackContext=n?kh:void 0),s=e(S)}finally{__INTLIFY_PROD_DEVTOOLS__,r||(S.fallbackContext=void 0)}if("translate exists"!==o&&Sd(s)&&-1===s||"translate exists"===o&&!s){const[e,o]=t();return n&&m?i(n):a(e)}if(l(s))return s;throw of(nf.UNEXPECTED_RETURN_TYPE)};function O(...e){return E((t=>Reflect.apply(Nh,null,[t,...e])),(()=>Wh(...e)),"translate",(t=>Reflect.apply(t.t,t,[...e])),(e=>e),(e=>Id(e)))}const M={normalize:function(e){return e.map((e=>Id(e)||Sd(e)||Ld(e)?vf(String(e)):e))},interpolate:e=>e,type:"vnode"};function F(e){return d.value[e]||{}}bf++,n&&Cd&&(lr(n.locale,(e=>{s&&(c.value=e,S.locale=e,Ah(S,c.value,u.value))})),lr(n.fallbackLocale,(e=>{s&&(u.value=e,S.fallbackLocale=e,Ah(S,c.value,u.value))})));const I={id:bf,locale:P,fallbackLocale:T,get inheritLocale(){return s},set inheritLocale(e){s=e,e&&n&&(c.value=n.locale.value,u.value=n.fallbackLocale.value,Ah(S,c.value,u.value))},get availableLocales(){return Object.keys(d.value).sort()},messages:A,get modifiers(){return k},get pluralRules(){return _||{}},get isGlobal(){return r},get missingWarn(){return f},set missingWarn(e){f=e,S.missingWarn=f},get fallbackWarn(){return v},set fallbackWarn(e){v=e,S.fallbackWarn=v},get fallbackRoot(){return m},set fallbackRoot(e){m=e},get fallbackFormat(){return g},set fallbackFormat(e){g=e,S.fallbackFormat=g},get warnHtmlMessage(){return C},set warnHtmlMessage(e){C=e,S.warnHtmlMessage=e},get escapeParameter(){return w},set escapeParameter(e){w=e,S.escapeParameter=e},t:O,getLocaleMessage:F,setLocaleMessage:function(e,t){if(i){const n={[e]:t};for(const e in n)Od(n,e)&&df(n[e]);t=n[e]}d.value[e]=t,S.messages=d.value},mergeLocaleMessage:function(e,t){d.value[e]=d.value[e]||{};const n={[e]:t};if(i)for(const o in n)Od(n,o)&&df(n[o]);Ud(t=n[e],d.value[e]),S.messages=d.value},getPostTranslationHandler:function(){return Fd(x)?x:null},setPostTranslationHandler:function(e){x=e,S.postTranslation=e},getMissingHandler:function(){return b},setMissingHandler:function(e){null!==e&&(y=yf(e)),b=e,S.missing=y},[sf]:function(e){_=e,S.pluralRules=_}};return I.datetimeFormats=z,I.numberFormats=R,I.rt=function(...e){const[t,n,o]=e;if(o&&!Bd(o))throw of(nf.INVALID_ARGUMENT);return O(t,n,Td({resolvedMessage:!0},o||{}))},I.te=function(e,t){return E((()=>{if(!e)return!1;const n=F(Id(t)?t:c.value),o=S.messageResolver(n,e);return l?null!=o:Ih(o)||$h(o)||Id(o)}),(()=>[e]),"translate exists",(n=>Reflect.apply(n.te,n,[e,t])),gf,(e=>Ld(e)))},I.tm=function(e){const t=function(e){let t=null;const n=dh(S,u.value,c.value);for(let o=0;oReflect.apply(Uh,null,[t,...e])),(()=>qh(...e)),"datetime format",(t=>Reflect.apply(t.d,t,[...e])),(()=>""),(e=>Id(e)))},I.n=function(...e){return E((t=>Reflect.apply(Gh,null,[t,...e])),(()=>Yh(...e)),"number format",(t=>Reflect.apply(t.n,t,[...e])),(()=>""),(e=>Id(e)))},I.getDateTimeFormat=function(e){return p.value[e]||{}},I.setDateTimeFormat=function(e,t){p.value[e]=t,S.datetimeFormats=p.value,Kh(S,e,t)},I.mergeDateTimeFormat=function(e,t){p.value[e]=Td(p.value[e]||{},t),S.datetimeFormats=p.value,Kh(S,e,t)},I.getNumberFormat=function(e){return h.value[e]||{}},I.setNumberFormat=function(e,t){h.value[e]=t,S.numberFormats=h.value,Qh(S,e,t)},I.mergeNumberFormat=function(e,t){h.value[e]=Td(h.value[e]||{},t),S.numberFormats=h.value,Qh(S,e,t)},I[cf]=o,I[rf]=function(...e){return E((t=>{let n;const o=t;try{o.processor=M,n=Reflect.apply(Nh,null,[o,...e])}finally{o.processor=null}return n}),(()=>Wh(...e)),"translate",(t=>t[rf](...e)),(e=>[vf(e)]),(e=>Md(e)))},I[af]=function(...e){return E((t=>Reflect.apply(Uh,null,[t,...e])),(()=>qh(...e)),"datetime format",(t=>t[af](...e)),mf,(e=>Id(e)||Md(e)))},I[lf]=function(...e){return E((t=>Reflect.apply(Gh,null,[t,...e])),(()=>Yh(...e)),"number format",(t=>t[lf](...e)),mf,(e=>Id(e)||Md(e)))},I}function Cf(e={},t){{const t=xf(function(e){const t=Id(e.locale)?e.locale:vh,n=Id(e.fallbackLocale)||Md(e.fallbackLocale)||Nd(e.fallbackLocale)||!1===e.fallbackLocale?e.fallbackLocale:t,o=Fd(e.missing)?e.missing:void 0,r=!Ld(e.silentTranslationWarn)&&!_d(e.silentTranslationWarn)||!e.silentTranslationWarn,i=!Ld(e.silentFallbackWarn)&&!_d(e.silentFallbackWarn)||!e.silentFallbackWarn,a=!Ld(e.fallbackRoot)||e.fallbackRoot,l=!!e.formatFallbackMessages,s=Nd(e.modifiers)?e.modifiers:{},c=e.pluralizationRules,u=Fd(e.postTranslation)?e.postTranslation:void 0,d=!Id(e.warnHtmlInMessage)||"off"!==e.warnHtmlInMessage,p=!!e.escapeParameterHtml,h=!Ld(e.sync)||e.sync;let f=e.messages;if(Nd(e.sharedMessages)){const t=e.sharedMessages;f=Object.keys(t).reduce(((e,n)=>{const o=e[n]||(e[n]={});return Td(o,t[n]),e}),f||{})}const{__i18n:v,__root:m,__injectWithOption:g}=e,b=e.datetimeFormats,y=e.numberFormats,x=e.flatJson,C=e.translateExistCompatible;return{locale:t,fallbackLocale:n,messages:f,flatJson:x,datetimeFormats:b,numberFormats:y,missing:o,missingWarn:r,fallbackWarn:i,fallbackRoot:a,fallbackFormat:l,modifiers:s,pluralRules:c,postTranslation:u,warnHtmlMessage:d,escapeParameter:p,messageResolver:e.messageResolver,inheritLocale:h,translateExistCompatible:C,__i18n:v,__root:m,__injectWithOption:g}}(e)),{__extender:n}=e,o={id:t.id,get locale(){return t.locale.value},set locale(e){t.locale.value=e},get fallbackLocale(){return t.fallbackLocale.value},set fallbackLocale(e){t.fallbackLocale.value=e},get messages(){return t.messages.value},get datetimeFormats(){return t.datetimeFormats.value},get numberFormats(){return t.numberFormats.value},get availableLocales(){return t.availableLocales},get formatter(){return{interpolate:()=>[]}},set formatter(e){},get missing(){return t.getMissingHandler()},set missing(e){t.setMissingHandler(e)},get silentTranslationWarn(){return Ld(t.missingWarn)?!t.missingWarn:t.missingWarn},set silentTranslationWarn(e){t.missingWarn=Ld(e)?!e:e},get silentFallbackWarn(){return Ld(t.fallbackWarn)?!t.fallbackWarn:t.fallbackWarn},set silentFallbackWarn(e){t.fallbackWarn=Ld(e)?!e:e},get modifiers(){return t.modifiers},get formatFallbackMessages(){return t.fallbackFormat},set formatFallbackMessages(e){t.fallbackFormat=e},get postTranslation(){return t.getPostTranslationHandler()},set postTranslation(e){t.setPostTranslationHandler(e)},get sync(){return t.inheritLocale},set sync(e){t.inheritLocale=e},get warnHtmlInMessage(){return t.warnHtmlMessage?"warn":"off"},set warnHtmlInMessage(e){t.warnHtmlMessage="off"!==e},get escapeParameterHtml(){return t.escapeParameter},set escapeParameterHtml(e){t.escapeParameter=e},get preserveDirectiveContent(){return!0},set preserveDirectiveContent(e){},get pluralizationRules(){return t.pluralRules||{}},__composer:t,t(...e){const[n,o,r]=e,i={};let a=null,l=null;if(!Id(n))throw of(nf.INVALID_ARGUMENT);const s=n;return Id(o)?i.locale=o:Md(o)?a=o:Nd(o)&&(l=o),Md(r)?a=r:Nd(r)&&(l=r),Reflect.apply(t.t,t,[s,a||l||{},i])},rt:(...e)=>Reflect.apply(t.rt,t,[...e]),tc(...e){const[n,o,r]=e,i={plural:1};let a=null,l=null;if(!Id(n))throw of(nf.INVALID_ARGUMENT);const s=n;return Id(o)?i.locale=o:Sd(o)?i.plural=o:Md(o)?a=o:Nd(o)&&(l=o),Id(r)?i.locale=r:Md(r)?a=r:Nd(r)&&(l=r),Reflect.apply(t.t,t,[s,a||l||{},i])},te:(e,n)=>t.te(e,n),tm:e=>t.tm(e),getLocaleMessage:e=>t.getLocaleMessage(e),setLocaleMessage(e,n){t.setLocaleMessage(e,n)},mergeLocaleMessage(e,n){t.mergeLocaleMessage(e,n)},d:(...e)=>Reflect.apply(t.d,t,[...e]),getDateTimeFormat:e=>t.getDateTimeFormat(e),setDateTimeFormat(e,n){t.setDateTimeFormat(e,n)},mergeDateTimeFormat(e,n){t.mergeDateTimeFormat(e,n)},n:(...e)=>Reflect.apply(t.n,t,[...e]),getNumberFormat:e=>t.getNumberFormat(e),setNumberFormat(e,n){t.setNumberFormat(e,n)},mergeNumberFormat(e,n){t.mergeNumberFormat(e,n)},getChoiceIndex:(e,t)=>-1};return o.__extender=n,o}}const wf={tag:{type:[String,Object]},locale:{type:String},scope:{type:String,validator:e=>"parent"===e||"global"===e,default:"parent"},i18n:{type:Object}};function kf(e){return yr}const Sf=zn({name:"i18n-t",props:Td({keypath:{type:String,required:!0},plural:{type:[Number,String],validator:e=>Sd(e)||!isNaN(e)}},wf),setup(e,t){const{slots:n,attrs:o}=t,r=e.i18n||Mf({useScope:e.scope,__useComponent:!0});return()=>{const i=Object.keys(n).filter((e=>"_"!==e)),a={};e.locale&&(a.locale=e.locale),void 0!==e.plural&&(a.plural=Id(e.plural)?+e.plural:e.plural);const l=function({slots:e},t){return 1===t.length&&"default"===t[0]?(e.default?e.default():[]).reduce(((e,t)=>[...e,...t.type===yr?t.children:[t]]),[]):t.reduce(((t,n)=>{const o=e[n];return o&&(t[n]=o()),t}),{})}(t,i),s=r[rf](e.keypath,l,a),c=Td({},o);return li(Id(e.tag)||Bd(e.tag)?e.tag:kf(),c,s)}}});function _f(e,t,n,o){const{slots:r,attrs:i}=t;return()=>{const t={part:!0};let a={};e.locale&&(t.locale=e.locale),Id(e.format)?t.key=e.format:Bd(e.format)&&(Id(e.format.key)&&(t.key=e.format.key),a=Object.keys(e.format).reduce(((t,o)=>n.includes(o)?Td({},t,{[o]:e.format[o]}):t),{}));const l=o(e.value,t,a);let s=[t.key];Md(l)?s=l.map(((e,t)=>{const n=r[e.type],o=n?n({[e.type]:e.value,index:t,parts:l}):[e.value];var i;return Md(i=o)&&!Id(i[0])&&(o[0].key=`${e.type}-${t}`),o})):Id(l)&&(s=[l]);const c=Td({},i);return li(Id(e.tag)||Bd(e.tag)?e.tag:kf(),c,s)}}const Pf=zn({name:"i18n-n",props:Td({value:{type:Number,required:!0},format:{type:[String,Object]}},wf),setup(e,t){const n=e.i18n||Mf({useScope:e.scope,__useComponent:!0});return _f(e,t,Xh,((...e)=>n[lf](...e)))}}),Tf=zn({name:"i18n-d",props:Td({value:{type:[Number,Date],required:!0},format:{type:[String,Object]}},wf),setup(e,t){const n=e.i18n||Mf({useScope:e.scope,__useComponent:!0});return _f(e,t,Vh,((...e)=>n[af](...e)))}});function Af(e){if(Id(e))return{path:e};if(Nd(e)){if(!("path"in e))throw of(nf.REQUIRED_VALUE);return e}throw of(nf.INVALID_VALUE)}function zf(e){const{path:t,locale:n,args:o,choice:r,plural:i}=e,a={},l=o||{};return Id(n)&&(a.locale=n),Sd(r)&&(a.plural=r),Sd(i)&&(a.plural=i),[t,l,a]}function Rf(e,t,...n){const o=Nd(n[0])?n[0]:{},r=!!o.useI18nComponentName;(!Ld(o.globalInstall)||o.globalInstall)&&([r?"i18n":Sf.name,"I18nT"].forEach((t=>e.component(t,Sf))),[Pf.name,"I18nN"].forEach((t=>e.component(t,Pf))),[Tf.name,"I18nD"].forEach((t=>e.component(t,Tf)))),e.directive("t",function(e){const t=t=>{const{instance:n,modifiers:o,value:r}=t;if(!n||!n.$)throw of(nf.UNEXPECTED_ERROR);const i=function(e,t){const n=e;if("composition"===e.mode)return n.__getInstance(t)||e.global;{const o=n.__getInstance(t);return null!=o?o.__composer:e.global.__composer}}(e,n.$),a=Af(r);return[Reflect.apply(i.t,i,[...zf(a)]),i]};return{created:(n,o)=>{const[r,i]=t(o);Cd&&e.global===i&&(n.__i18nWatcher=lr(i.locale,(()=>{o.instance&&o.instance.$forceUpdate()}))),n.__composer=i,n.textContent=r},unmounted:e=>{Cd&&e.__i18nWatcher&&(e.__i18nWatcher(),e.__i18nWatcher=void 0,delete e.__i18nWatcher),e.__composer&&(e.__composer=void 0,delete e.__composer)},beforeUpdate:(e,{value:t})=>{if(e.__composer){const n=e.__composer,o=Af(t);e.textContent=Reflect.apply(n.t,n,[...zf(o)])}},getSSRProps:e=>{const[n]=t(e);return{textContent:n}}}}(t))}function Ef(e,t){e.locale=t.locale||e.locale,e.fallbackLocale=t.fallbackLocale||e.fallbackLocale,e.missing=t.missing||e.missing,e.silentTranslationWarn=t.silentTranslationWarn||e.silentFallbackWarn,e.silentFallbackWarn=t.silentFallbackWarn||e.silentFallbackWarn,e.formatFallbackMessages=t.formatFallbackMessages||e.formatFallbackMessages,e.postTranslation=t.postTranslation||e.postTranslation,e.warnHtmlInMessage=t.warnHtmlInMessage||e.warnHtmlInMessage,e.escapeParameterHtml=t.escapeParameterHtml||e.escapeParameterHtml,e.sync=t.sync||e.sync,e.__composer[sf](t.pluralizationRules||e.pluralizationRules);const n=pf(e.locale,{messages:t.messages,__i18n:t.__i18n});return Object.keys(n).forEach((t=>e.mergeLocaleMessage(t,n[t]))),t.datetimeFormats&&Object.keys(t.datetimeFormats).forEach((n=>e.mergeDateTimeFormat(n,t.datetimeFormats[n]))),t.numberFormats&&Object.keys(t.numberFormats).forEach((n=>e.mergeNumberFormat(n,t.numberFormats[n]))),e}const Of=wd("global-vue-i18n");function Mf(e={}){const t=Gr();if(null==t)throw of(nf.MUST_BE_CALL_SETUP_TOP);if(!t.isCE&&null!=t.appContext.app&&!t.appContext.app.__VUE_I18N_SYMBOL__)throw of(nf.NOT_INSTALLED);const n=function(e){{const t=Po(e.isCE?Of:e.appContext.app.__VUE_I18N_SYMBOL__);if(!t)throw of(e.isCE?nf.NOT_INSTALLED_WITH_PROVIDE:nf.UNEXPECTED_ERROR);return t}}(t),o=function(e){return"composition"===e.mode?e.global:e.global.__composer}(n),r=hf(t),i=function(e,t){return Pd(e)?"__i18n"in t?"local":"global":e.useScope?e.useScope:"local"}(e,r);if(__VUE_I18N_LEGACY_API__&&"legacy"===n.mode&&!e.__useComponent){if(!n.allowComposition)throw of(nf.NOT_AVAILABLE_IN_LEGACY_MODE);return function(e,t,n,o={}){const r="local"===t,i=Ot(null);if(r&&e.proxy&&!e.proxy.$options.i18n&&!e.proxy.$options.__i18n)throw of(nf.MUST_DEFINE_I18N_OPTION_IN_ALLOW_COMPOSITION);const a=Ld(o.inheritLocale)?o.inheritLocale:!Id(o.locale),l=Et(!r||a?n.locale.value:Id(o.locale)?o.locale:vh),s=Et(!r||a?n.fallbackLocale.value:Id(o.fallbackLocale)||Md(o.fallbackLocale)||Nd(o.fallbackLocale)||!1===o.fallbackLocale?o.fallbackLocale:l.value),c=Et(pf(l.value,o)),u=Et(Nd(o.datetimeFormats)?o.datetimeFormats:{[l.value]:{}}),d=Et(Nd(o.numberFormats)?o.numberFormats:{[l.value]:{}}),p=r?n.missingWarn:!Ld(o.missingWarn)&&!_d(o.missingWarn)||o.missingWarn,h=r?n.fallbackWarn:!Ld(o.fallbackWarn)&&!_d(o.fallbackWarn)||o.fallbackWarn,f=r?n.fallbackRoot:!Ld(o.fallbackRoot)||o.fallbackRoot,v=!!o.fallbackFormat,m=Fd(o.missing)?o.missing:null,g=Fd(o.postTranslation)?o.postTranslation:null,b=r?n.warnHtmlMessage:!Ld(o.warnHtmlMessage)||o.warnHtmlMessage,y=!!o.escapeParameter,x=r?n.modifiers:Nd(o.modifiers)?o.modifiers:{},C=o.pluralRules||r&&n.pluralRules;function w(){return[l.value,s.value,c.value,u.value,d.value]}const k=ai({get:()=>i.value?i.value.locale.value:l.value,set:e=>{i.value&&(i.value.locale.value=e),l.value=e}}),S=ai({get:()=>i.value?i.value.fallbackLocale.value:s.value,set:e=>{i.value&&(i.value.fallbackLocale.value=e),s.value=e}}),_=ai((()=>i.value?i.value.messages.value:c.value)),P=ai((()=>u.value)),T=ai((()=>d.value));function A(){return i.value?i.value.getPostTranslationHandler():g}function z(e){i.value&&i.value.setPostTranslationHandler(e)}function R(){return i.value?i.value.getMissingHandler():m}function E(e){i.value&&i.value.setMissingHandler(e)}function O(e){return w(),e()}function M(...e){return i.value?O((()=>Reflect.apply(i.value.t,null,[...e]))):O((()=>""))}function F(...e){return i.value?Reflect.apply(i.value.rt,null,[...e]):""}function I(...e){return i.value?O((()=>Reflect.apply(i.value.d,null,[...e]))):O((()=>""))}function L(...e){return i.value?O((()=>Reflect.apply(i.value.n,null,[...e]))):O((()=>""))}function B(e){return i.value?i.value.tm(e):{}}function D(e,t){return!!i.value&&i.value.te(e,t)}function $(e){return i.value?i.value.getLocaleMessage(e):{}}function N(e,t){i.value&&(i.value.setLocaleMessage(e,t),c.value[e]=t)}function j(e,t){i.value&&i.value.mergeLocaleMessage(e,t)}function H(e){return i.value?i.value.getDateTimeFormat(e):{}}function W(e,t){i.value&&(i.value.setDateTimeFormat(e,t),u.value[e]=t)}function U(e,t){i.value&&i.value.mergeDateTimeFormat(e,t)}function V(e){return i.value?i.value.getNumberFormat(e):{}}function q(e,t){i.value&&(i.value.setNumberFormat(e,t),d.value[e]=t)}function K(e,t){i.value&&i.value.mergeNumberFormat(e,t)}const G={get id(){return i.value?i.value.id:-1},locale:k,fallbackLocale:S,messages:_,datetimeFormats:P,numberFormats:T,get inheritLocale(){return i.value?i.value.inheritLocale:a},set inheritLocale(e){i.value&&(i.value.inheritLocale=e)},get availableLocales(){return i.value?i.value.availableLocales:Object.keys(c.value)},get modifiers(){return i.value?i.value.modifiers:x},get pluralRules(){return i.value?i.value.pluralRules:C},get isGlobal(){return!!i.value&&i.value.isGlobal},get missingWarn(){return i.value?i.value.missingWarn:p},set missingWarn(e){i.value&&(i.value.missingWarn=e)},get fallbackWarn(){return i.value?i.value.fallbackWarn:h},set fallbackWarn(e){i.value&&(i.value.missingWarn=e)},get fallbackRoot(){return i.value?i.value.fallbackRoot:f},set fallbackRoot(e){i.value&&(i.value.fallbackRoot=e)},get fallbackFormat(){return i.value?i.value.fallbackFormat:v},set fallbackFormat(e){i.value&&(i.value.fallbackFormat=e)},get warnHtmlMessage(){return i.value?i.value.warnHtmlMessage:b},set warnHtmlMessage(e){i.value&&(i.value.warnHtmlMessage=e)},get escapeParameter(){return i.value?i.value.escapeParameter:y},set escapeParameter(e){i.value&&(i.value.escapeParameter=e)},t:M,getPostTranslationHandler:A,setPostTranslationHandler:z,getMissingHandler:R,setMissingHandler:E,rt:F,d:I,n:L,tm:B,te:D,getLocaleMessage:$,setLocaleMessage:N,mergeLocaleMessage:j,getDateTimeFormat:H,setDateTimeFormat:W,mergeDateTimeFormat:U,getNumberFormat:V,setNumberFormat:q,mergeNumberFormat:K};function X(e){e.locale.value=l.value,e.fallbackLocale.value=s.value,Object.keys(c.value).forEach((t=>{e.mergeLocaleMessage(t,c.value[t])})),Object.keys(u.value).forEach((t=>{e.mergeDateTimeFormat(t,u.value[t])})),Object.keys(d.value).forEach((t=>{e.mergeNumberFormat(t,d.value[t])})),e.escapeParameter=y,e.fallbackFormat=v,e.fallbackRoot=f,e.fallbackWarn=h,e.missingWarn=p,e.warnHtmlMessage=b}return Dn((()=>{if(null==e.proxy||null==e.proxy.$i18n)throw of(nf.NOT_AVAILABLE_COMPOSITION_IN_LEGACY);const n=i.value=e.proxy.$i18n.__composer;"global"===t?(l.value=n.locale.value,s.value=n.fallbackLocale.value,c.value=n.messages.value,u.value=n.datetimeFormats.value,d.value=n.numberFormats.value):r&&X(n)})),G}(t,i,o,e)}if("global"===i)return ff(o,e,r),o;if("parent"===i){let r=function(e,t,n=!1){let o=null;const r=t.root;let i=function(e,t=!1){return null==e?null:t&&e.vnode.ctx||e.parent}(t,n);for(;null!=i;){const t=e;if("composition"===e.mode)o=t.__getInstance(i);else if(__VUE_I18N_LEGACY_API__){const e=t.__getInstance(i);null!=e&&(o=e.__composer,n&&o&&!o[cf]&&(o=null))}if(null!=o)break;if(r===i)break;i=i.parent}return o}(n,t,e.__useComponent);return null==r&&(r=o),r}const a=n;let l=a.__getInstance(t);if(null==l){const n=Td({},e);"__i18n"in r&&(n.__i18n=r.__i18n),o&&(n.__root=o),l=xf(n),a.__composerExtend&&(l[uf]=a.__composerExtend(l)),function(e,t,n){$n((()=>{}),t),Wn((()=>{const o=n;e.__deleteInstance(t);const r=o[uf];r&&(r(),delete o[uf])}),t)}(a,t,l),a.__setInstance(t,l)}return l}const Ff=["locale","fallbackLocale","availableLocales"],If=["t","rt","d","n","tm","te"];var Lf;if("boolean"!=typeof __VUE_I18N_FULL_INSTALL__&&(zd().__VUE_I18N_FULL_INSTALL__=!0),"boolean"!=typeof __VUE_I18N_LEGACY_API__&&(zd().__VUE_I18N_LEGACY_API__=!0),"boolean"!=typeof __INTLIFY_JIT_COMPILATION__&&(zd().__INTLIFY_JIT_COMPILATION__=!1),"boolean"!=typeof __INTLIFY_DROP_MESSAGE_COMPILER__&&(zd().__INTLIFY_DROP_MESSAGE_COMPILER__=!1),"boolean"!=typeof __INTLIFY_PROD_DEVTOOLS__&&(zd().__INTLIFY_PROD_DEVTOOLS__=!1),__INTLIFY_JIT_COMPILATION__?xh((function(e,t){if(__INTLIFY_JIT_COMPILATION__&&!__INTLIFY_DROP_MESSAGE_COMPILER__&&Id(e)){!Ld(t.warnHtmlMessage)||t.warnHtmlMessage;const n=(t.onCacheKey||Mh)(e),o=Fh[n];if(o)return o;const{ast:r,detectError:i}=Lh(e,p(d({},t),{location:!1,jit:!0})),a=Rh(r);return i?a:Fh[n]=a}{const t=e.cacheKey;return t?Fh[t]||(Fh[t]=Rh(e)):Rh(e)}})):xh(Bh),bh=function(e,t){if(!Bd(e))return null;let n=Wp.get(t);if(n||(n=function(e){const t=[];let n,o,r,i,a,l,s,c=-1,u=0,d=0;const p=[];function h(){const t=e[c+1];if(5===u&&"'"===t||6===u&&'"'===t)return c++,r="\\"+t,p[0](),!0}for(p[0]=()=>{void 0===o?o=r:o+=r},p[1]=()=>{void 0!==o&&(t.push(o),o=void 0)},p[2]=()=>{p[0](),d++},p[3]=()=>{if(d>0)d--,u=4,p[0]();else{if(d=0,void 0===o)return!1;if(o=Hp(o),!1===o)return!1;p[1]()}};null!==u;)if(c++,n=e[c],"\\"!==n||!h()){if(i=jp(n),s=$p[u],a=s[i]||s.l||8,8===a)return;if(u=a[0],void 0!==a[1]&&(l=p[a[1]],l&&(r=n,!1===l())))return;if(7===u)return t}}(t),n&&Wp.set(t,n)),!n)return null;const o=n.length;let r=e,i=0;for(;iZl((()=>Promise.resolve().then((()=>IQ))),void 0),"./lang/fa-IR.json":()=>Zl((()=>Promise.resolve().then((()=>LQ))),void 0),"./lang/ja-JP.json":()=>Zl((()=>Promise.resolve().then((()=>BQ))),void 0),"./lang/ko-KR.json":()=>Zl((()=>Promise.resolve().then((()=>DQ))),void 0),"./lang/ru-RU.json":()=>Zl((()=>Promise.resolve().then((()=>$Q))),void 0),"./lang/vi-VN.json":()=>Zl((()=>Promise.resolve().then((()=>NQ))),void 0),"./lang/zh-CN.json":()=>Zl((()=>Promise.resolve().then((()=>jQ))),void 0),"./lang/zh-TW.json":()=>Zl((()=>Promise.resolve().then((()=>HQ))),void 0)})).map((e=>e.slice(7,-5))),jf=function(e={},t){const n=__VUE_I18N_LEGACY_API__&&Ld(e.legacy)?e.legacy:__VUE_I18N_LEGACY_API__,o=!Ld(e.globalInjection)||e.globalInjection,r=!__VUE_I18N_LEGACY_API__||!n||!!e.allowComposition,i=new Map,[a,l]=function(e,t,n){const o=ce();{const n=__VUE_I18N_LEGACY_API__&&t?o.run((()=>Cf(e))):o.run((()=>xf(e)));if(null==n)throw of(nf.UNEXPECTED_ERROR);return[o,n]}}(e,n),s=wd("");{const e={get mode(){return __VUE_I18N_LEGACY_API__&&n?"legacy":"composition"},get allowComposition(){return r},install(t,...r){return v(this,null,(function*(){if(t.__VUE_I18N_SYMBOL__=s,t.provide(t.__VUE_I18N_SYMBOL__,e),Nd(r[0])){const t=r[0];e.__composerExtend=t.__composerExtend,e.__vueI18nExtend=t.__vueI18nExtend}let i=null;!n&&o&&(i=function(e,t){const n=Object.create(null);Ff.forEach((e=>{const o=Object.getOwnPropertyDescriptor(t,e);if(!o)throw of(nf.UNEXPECTED_ERROR);const r=Rt(o.value)?{get:()=>o.value.value,set(e){o.value.value=e}}:{get:()=>o.get&&o.get()};Object.defineProperty(n,e,r)})),e.config.globalProperties.$i18n=n,If.forEach((n=>{const o=Object.getOwnPropertyDescriptor(t,n);if(!o||!o.value)throw of(nf.UNEXPECTED_ERROR);Object.defineProperty(e.config.globalProperties,`$${n}`,o)}));const o=()=>{delete e.config.globalProperties.$i18n,If.forEach((t=>{delete e.config.globalProperties[`$${t}`]}))};return o}(t,e.global)),__VUE_I18N_FULL_INSTALL__&&Rf(t,e,...r),__VUE_I18N_LEGACY_API__&&n&&t.mixin(function(e,t,n){return{beforeCreate(){const o=Gr();if(!o)throw of(nf.UNEXPECTED_ERROR);const r=this.$options;if(r.i18n){const o=r.i18n;if(r.__i18n&&(o.__i18n=r.__i18n),o.__root=t,this===this.$root)this.$i18n=Ef(e,o);else{o.__injectWithOption=!0,o.__extender=n.__vueI18nExtend,this.$i18n=Cf(o);const e=this.$i18n;e.__extender&&(e.__disposer=e.__extender(this.$i18n))}}else if(r.__i18n)if(this===this.$root)this.$i18n=Ef(e,r);else{this.$i18n=Cf({__i18n:r.__i18n,__injectWithOption:!0,__extender:n.__vueI18nExtend,__root:t});const e=this.$i18n;e.__extender&&(e.__disposer=e.__extender(this.$i18n))}else this.$i18n=e;r.__i18nGlobal&&ff(t,r,r),this.$t=(...e)=>this.$i18n.t(...e),this.$rt=(...e)=>this.$i18n.rt(...e),this.$tc=(...e)=>this.$i18n.tc(...e),this.$te=(e,t)=>this.$i18n.te(e,t),this.$d=(...e)=>this.$i18n.d(...e),this.$n=(...e)=>this.$i18n.n(...e),this.$tm=e=>this.$i18n.tm(e),n.__setInstance(o,this.$i18n)},mounted(){},unmounted(){const e=Gr();if(!e)throw of(nf.UNEXPECTED_ERROR);const t=this.$i18n;delete this.$t,delete this.$rt,delete this.$tc,delete this.$te,delete this.$d,delete this.$n,delete this.$tm,t.__disposer&&(t.__disposer(),delete t.__disposer,delete t.__extender),n.__deleteInstance(e),delete this.$i18n}}}(l,l.__composer,e));const a=t.unmount;t.unmount=()=>{i&&i(),e.dispose(),a()}}))},get global(){return l},dispose(){a.stop()},__instances:i,__getInstance:function(e){return i.get(e)||null},__setInstance:function(e,t){i.set(e,t)},__deleteInstance:function(e){i.delete(e)}};return e}}({locale:Df().value||function(){const e=navigator.language,t=Nf.includes(e)?e:"zh-CN";return Df().value||$f(t),t}(),fallbackLocale:"en-US",messages:{}});function Hf(){return v(this,null,(function*(){yield Promise.all(Nf.map((e=>v(this,null,(function*(){const t=yield((e,t)=>{const n=e[t];return n?"function"==typeof n?n():Promise.resolve(n):new Promise(((e,n)=>{("function"==typeof queueMicrotask?queueMicrotask:setTimeout)(n.bind(null,new Error("Unknown variable dynamic import: "+t)))}))})(Object.assign({"./lang/en-US.json":()=>Zl((()=>Promise.resolve().then((()=>IQ))),void 0),"./lang/fa-IR.json":()=>Zl((()=>Promise.resolve().then((()=>LQ))),void 0),"./lang/ja-JP.json":()=>Zl((()=>Promise.resolve().then((()=>BQ))),void 0),"./lang/ko-KR.json":()=>Zl((()=>Promise.resolve().then((()=>DQ))),void 0),"./lang/ru-RU.json":()=>Zl((()=>Promise.resolve().then((()=>$Q))),void 0),"./lang/vi-VN.json":()=>Zl((()=>Promise.resolve().then((()=>NQ))),void 0),"./lang/zh-CN.json":()=>Zl((()=>Promise.resolve().then((()=>jQ))),void 0),"./lang/zh-TW.json":()=>Zl((()=>Promise.resolve().then((()=>HQ))),void 0)}),`./lang/${e}.json`).then((e=>e.default||e));jf.global.setLocaleMessage(e,t)})))))}))}const Wf={"zh-CN":"简体中文","zh-TW":"繁體中文","en-US":"English","fa-IR":"Iran","ja-JP":"日本語","vi-VN":"Tiếng Việt","ko-KR":"한국어","ru-RU":"Русский"},Uf=e=>jf.global.t(e);function Vf(e=void 0,t="YYYY-MM-DD HH:mm:ss"){return null==e?"":(10===e.toString().length&&(e*=1e3),bd(e).format(t))}function qf(e=void 0,t="YYYY-MM-DD"){return Vf(e,t)}function Kf(e){const t="string"==typeof e?parseFloat(e):e;return isNaN(t)?"0.00":t.toFixed(2)}function Gf(e){const t="string"==typeof e?parseFloat(e):e;return isNaN(t)?"0.00":(t/100).toFixed(2)}function Xf(e){navigator.clipboard?navigator.clipboard.writeText(e).then((()=>{window.$message.success(Uf("复制成功"))})).catch((t=>{Yf(e)})):Yf(e)}function Yf(e){const t=document.createElement("button"),n=new xd(t,{text:()=>e});n.on("success",(()=>{window.$message.success(Uf("复制成功")),n.destroy()})),n.on("error",(()=>{window.$message.error(Uf("复制失败")),n.destroy()})),t.click()}function Qf(e){const t=e/1024,n=t/1024,o=n/1024,r=o/1024;return r>=1?Kf(r)+" TB":o>=1?Kf(o)+" GB":n>=1?Kf(n)+" MB":Kf(t)+" KB"}function Zf(e){return e&&Array.isArray(e)}function Jf(e){return/^(https?:|mailto:|tel:)/.test(e)}const ev=/^[a-z0-9]+(-[a-z0-9]+)*$/,tv=(e,t,n,o="")=>{const r=e.split(":");if("@"===e.slice(0,1)){if(r.length<2||r.length>3)return null;o=r.shift().slice(1)}if(r.length>3||!r.length)return null;if(r.length>1){const e=r.pop(),n=r.pop(),i={provider:r.length>0?r[0]:o,prefix:n,name:e};return t&&!nv(i)?null:i}const i=r[0],a=i.split("-");if(a.length>1){const e={provider:o,prefix:a.shift(),name:a.join("-")};return t&&!nv(e)?null:e}if(n&&""===o){const e={provider:o,prefix:"",name:i};return t&&!nv(e,n)?null:e}return null},nv=(e,t)=>!!e&&!(""!==e.provider&&!e.provider.match(ev)||!(t&&""===e.prefix||e.prefix.match(ev))||!e.name.match(ev)),ov=Object.freeze({left:0,top:0,width:16,height:16}),rv=Object.freeze({rotate:0,vFlip:!1,hFlip:!1}),iv=Object.freeze(d(d({},ov),rv)),av=Object.freeze(p(d({},iv),{body:"",hidden:!1}));function lv(e,t){const n=function(e,t){const n={};!e.hFlip!=!t.hFlip&&(n.hFlip=!0),!e.vFlip!=!t.vFlip&&(n.vFlip=!0);const o=((e.rotate||0)+(t.rotate||0))%4;return o&&(n.rotate=o),n}(e,t);for(const o in av)o in rv?o in e&&!(o in n)&&(n[o]=rv[o]):o in t?n[o]=t[o]:o in e&&(n[o]=e[o]);return n}function sv(e,t,n){const o=e.icons,r=e.aliases||Object.create(null);let i={};function a(e){i=lv(o[e]||r[e],i)}return a(t),n.forEach(a),lv(e,i)}function cv(e,t){const n=[];if("object"!=typeof e||"object"!=typeof e.icons)return n;e.not_found instanceof Array&&e.not_found.forEach((e=>{t(e,null),n.push(e)}));const o=function(e,t){const n=e.icons,o=e.aliases||Object.create(null),r=Object.create(null);return(t||Object.keys(n).concat(Object.keys(o))).forEach((function e(t){if(n[t])return r[t]=[];if(!(t in r)){r[t]=null;const n=o[t]&&o[t].parent,i=n&&e(n);i&&(r[t]=[n].concat(i))}return r[t]})),r}(e);for(const r in o){const i=o[r];i&&(t(r,sv(e,r,i)),n.push(r))}return n}const uv=d({provider:"",aliases:{},not_found:{}},ov);function dv(e,t){for(const n in t)if(n in e&&typeof e[n]!=typeof t[n])return!1;return!0}function pv(e){if("object"!=typeof e||null===e)return null;const t=e;if("string"!=typeof t.prefix||!e.icons||"object"!=typeof e.icons)return null;if(!dv(e,uv))return null;const n=t.icons;for(const r in n){const e=n[r];if(!r.match(ev)||"string"!=typeof e.body||!dv(e,av))return null}const o=t.aliases||Object.create(null);for(const r in o){const e=o[r],t=e.parent;if(!r.match(ev)||"string"!=typeof t||!n[t]&&!o[t]||!dv(e,av))return null}return t}const hv=Object.create(null);function fv(e,t){const n=hv[e]||(hv[e]=Object.create(null));return n[t]||(n[t]=function(e,t){return{provider:e,prefix:t,icons:Object.create(null),missing:new Set}}(e,t))}function vv(e,t){return pv(t)?cv(t,((t,n)=>{n?e.icons[t]=n:e.missing.add(t)})):[]}let mv=!1;function gv(e){return"boolean"==typeof e&&(mv=e),mv}function bv(e,t){const n=tv(e,!0,mv);return!!n&&function(e,t,n){try{if("string"==typeof n.body)return e.icons[t]=d({},n),!0}catch(o){}return!1}(fv(n.provider,n.prefix),n.name,t)}const yv=Object.freeze({width:null,height:null}),xv=Object.freeze(d(d({},yv),rv)),Cv=/(-?[0-9.]*[0-9]+[0-9.]*)/g,wv=/^-?[0-9.]*[0-9]+[0-9.]*$/g;function kv(e,t,n){if(1===t)return e;if(n=n||100,"number"==typeof e)return Math.ceil(e*t*n)/n;if("string"!=typeof e)return e;const o=e.split(Cv);if(null===o||!o.length)return e;const r=[];let i=o.shift(),a=wv.test(i);for(;;){if(a){const e=parseFloat(i);isNaN(e)?r.push(i):r.push(Math.ceil(e*t*n)/n)}else r.push(i);if(i=o.shift(),void 0===i)return r.join("");a=!a}}const Sv=/\sid="(\S+)"/g,_v="IconifyId"+Date.now().toString(16)+(16777216*Math.random()|0).toString(16);let Pv=0;const Tv=Object.create(null);function Av(e){return Tv[e]||Tv[""]}function zv(e){let t;if("string"==typeof e.resources)t=[e.resources];else if(t=e.resources,!(t instanceof Array&&t.length))return null;return{resources:t,path:e.path||"/",maxURL:e.maxURL||500,rotate:e.rotate||750,timeout:e.timeout||5e3,random:!0===e.random,index:e.index||0,dataAfterTimeout:!1!==e.dataAfterTimeout}}const Rv=Object.create(null),Ev=["https://api.simplesvg.com","https://api.unisvg.com"],Ov=[];for(;Ev.length>0;)1===Ev.length||Math.random()>.5?Ov.push(Ev.shift()):Ov.push(Ev.pop());function Mv(e,t){const n=zv(t);return null!==n&&(Rv[e]=n,!0)}function Fv(e){return Rv[e]}Rv[""]=zv({resources:["https://api.iconify.design"].concat(Ov)});let Iv=(()=>{let e;try{if(e=fetch,"function"==typeof e)return e}catch(t){}})();const Lv={prepare:(e,t,n)=>{const o=[],r=function(e,t){const n=Fv(e);if(!n)return 0;let o;if(n.maxURL){let e=0;n.resources.forEach((t=>{const n=t;e=Math.max(e,n.length)}));const r=t+".json?icons=";o=n.maxURL-e-n.path.length-r.length}else o=0;return o}(e,t),i="icons";let a={type:i,provider:e,prefix:t,icons:[]},l=0;return n.forEach(((n,s)=>{l+=n.length+1,l>=r&&s>0&&(o.push(a),a={type:i,provider:e,prefix:t,icons:[]},l=n.length),a.icons.push(n)})),o.push(a),o},send:(e,t,n)=>{if(!Iv)return void n("abort",424);let o=function(e){if("string"==typeof e){const t=Fv(e);if(t)return t.path}return"/"}(t.provider);switch(t.type){case"icons":{const e=t.prefix,n=t.icons.join(",");o+=e+".json?"+new URLSearchParams({icons:n}).toString();break}case"custom":{const e=t.uri;o+="/"===e.slice(0,1)?e.slice(1):e;break}default:return void n("abort",400)}let r=503;Iv(e+o).then((e=>{const t=e.status;if(200===t)return r=501,e.json();setTimeout((()=>{n(function(e){return 404===e}(t)?"abort":"next",t)}))})).then((e=>{"object"==typeof e&&null!==e?setTimeout((()=>{n("success",e)})):setTimeout((()=>{404===e?n("abort",e):n("next",r)}))})).catch((()=>{n("next",r)}))}};function Bv(e,t){e.forEach((e=>{const n=e.loaderCallbacks;n&&(e.loaderCallbacks=n.filter((e=>e.id!==t)))}))}let Dv=0;var $v={resources:[],index:0,timeout:2e3,rotate:750,random:!1,dataAfterTimeout:!1};function Nv(e,t,n,o){const r=e.resources.length,i=e.random?Math.floor(Math.random()*r):e.index;let a;if(e.random){let t=e.resources.slice(0);for(a=[];t.length>1;){const e=Math.floor(Math.random()*t.length);a.push(t[e]),t=t.slice(0,e).concat(t.slice(e+1))}a=a.concat(t)}else a=e.resources.slice(i).concat(e.resources.slice(0,i));const l=Date.now();let s,c="pending",u=0,d=null,p=[],h=[];function f(){d&&(clearTimeout(d),d=null)}function v(){"pending"===c&&(c="aborted"),f(),p.forEach((e=>{"pending"===e.status&&(e.status="aborted")})),p=[]}function m(e,t){t&&(h=[]),"function"==typeof e&&h.push(e)}function g(){c="failed",h.forEach((e=>{e(void 0,s)}))}function b(){p.forEach((e=>{"pending"===e.status&&(e.status="aborted")})),p=[]}function y(){if("pending"!==c)return;f();const o=a.shift();if(void 0===o)return p.length?void(d=setTimeout((()=>{f(),"pending"===c&&(b(),g())}),e.timeout)):void g();const r={status:"pending",resource:o,callback:(t,n)=>{!function(t,n,o){const r="success"!==n;switch(p=p.filter((e=>e!==t)),c){case"pending":break;case"failed":if(r||!e.dataAfterTimeout)return;break;default:return}if("abort"===n)return s=o,void g();if(r)return s=o,void(p.length||(a.length?y():g()));if(f(),b(),!e.random){const n=e.resources.indexOf(t.resource);-1!==n&&n!==e.index&&(e.index=n)}c="completed",h.forEach((e=>{e(o)}))}(r,t,n)}};p.push(r),u++,d=setTimeout(y,e.rotate),n(o,t,r.callback)}return"function"==typeof o&&h.push(o),setTimeout(y),function(){return{startTime:l,payload:t,status:c,queriesSent:u,queriesPending:p.length,subscribe:m,abort:v}}}function jv(e){const t=d(d({},$v),e);let n=[];function o(){n=n.filter((e=>"pending"===e().status))}return{query:function(e,r,i){const a=Nv(t,e,r,((e,t)=>{o(),i&&i(e,t)}));return n.push(a),a},find:function(e){return n.find((t=>e(t)))||null},setIndex:e=>{t.index=e},getIndex:()=>t.index,cleanup:o}}function Hv(){}const Wv=Object.create(null);function Uv(e,t,n){let o,r;if("string"==typeof e){const t=Av(e);if(!t)return n(void 0,424),Hv;r=t.send;const i=function(e){if(!Wv[e]){const t=Fv(e);if(!t)return;const n={config:t,redundancy:jv(t)};Wv[e]=n}return Wv[e]}(e);i&&(o=i.redundancy)}else{const t=zv(e);if(t){o=jv(t);const n=Av(e.resources?e.resources[0]:"");n&&(r=n.send)}}return o&&r?o.query(t,r,n)().abort:(n(void 0,424),Hv)}const Vv="iconify2",qv="iconify",Kv=qv+"-count",Gv=qv+"-version",Xv=36e5;function Yv(e,t){try{return e.getItem(t)}catch(n){}}function Qv(e,t,n){try{return e.setItem(t,n),!0}catch(o){}}function Zv(e,t){try{e.removeItem(t)}catch(n){}}function Jv(e,t){return Qv(e,Kv,t.toString())}function em(e){return parseInt(Yv(e,Kv))||0}const tm={local:!0,session:!0},nm={local:new Set,session:new Set};let om=!1,rm="undefined"==typeof window?{}:window;function im(e){const t=e+"Storage";try{if(rm&&rm[t]&&"number"==typeof rm[t].length)return rm[t]}catch(n){}tm[e]=!1}function am(e,t){const n=im(e);if(!n)return;const o=Yv(n,Gv);if(o!==Vv){if(o){const e=em(n);for(let t=0;t{const o=qv+e.toString(),i=Yv(n,o);if("string"==typeof i){try{const n=JSON.parse(i);if("object"==typeof n&&"number"==typeof n.cached&&n.cached>r&&"string"==typeof n.provider&&"object"==typeof n.data&&"string"==typeof n.data.prefix&&t(n,e))return!0}catch(a){}Zv(n,o)}};let a=em(n);for(let l=a-1;l>=0;l--)i(l)||(l===a-1?(a--,Jv(n,a)):nm[e].add(l))}function lm(){if(!om){om=!0;for(const e in tm)am(e,(e=>{const t=e.data,n=fv(e.provider,t.prefix);if(!vv(n,t).length)return!1;const o=t.lastModified||-1;return n.lastModifiedCached=n.lastModifiedCached?Math.min(n.lastModifiedCached,o):o,!0}))}}function sm(e,t){function n(n){let o;if(!tm[n]||!(o=im(n)))return;const r=nm[n];let i;if(r.size)r.delete(i=Array.from(r).shift());else if(i=em(o),i>=50||!Jv(o,i+1))return;const a={cached:Math.floor(Date.now()/Xv),provider:e.provider,data:t};return Qv(o,qv+i.toString(),JSON.stringify(a))}om||lm(),t.lastModified&&!function(e,t){const n=e.lastModifiedCached;if(n&&n>=t)return n===t;if(e.lastModifiedCached=t,n)for(const o in tm)am(o,(n=>{const o=n.data;return n.provider!==e.provider||o.prefix!==e.prefix||o.lastModified===t}));return!0}(e,t.lastModified)||Object.keys(t.icons).length&&(t.not_found&&delete(t=Object.assign({},t)).not_found,n("local")||n("session"))}function cm(){}function um(e){e.iconsLoaderFlag||(e.iconsLoaderFlag=!0,setTimeout((()=>{e.iconsLoaderFlag=!1,function(e){e.pendingCallbacksFlag||(e.pendingCallbacksFlag=!0,setTimeout((()=>{e.pendingCallbacksFlag=!1;const t=e.loaderCallbacks?e.loaderCallbacks.slice(0):[];if(!t.length)return;let n=!1;const o=e.provider,r=e.prefix;t.forEach((t=>{const i=t.icons,a=i.pending.length;i.pending=i.pending.filter((t=>{if(t.prefix!==r)return!0;const a=t.name;if(e.icons[a])i.loaded.push({provider:o,prefix:r,name:a});else{if(!e.missing.has(a))return n=!0,!0;i.missing.push({provider:o,prefix:r,name:a})}return!1})),i.pending.length!==a&&(n||Bv([e],t.id),t.callback(i.loaded.slice(0),i.missing.slice(0),i.pending.slice(0),t.abort))}))})))}(e)})))}const dm=(e,t)=>{const n=function(e){const t={loaded:[],missing:[],pending:[]},n=Object.create(null);e.sort(((e,t)=>e.provider!==t.provider?e.provider.localeCompare(t.provider):e.prefix!==t.prefix?e.prefix.localeCompare(t.prefix):e.name.localeCompare(t.name)));let o={provider:"",prefix:"",name:""};return e.forEach((e=>{if(o.name===e.name&&o.prefix===e.prefix&&o.provider===e.provider)return;o=e;const r=e.provider,i=e.prefix,a=e.name,l=n[r]||(n[r]=Object.create(null)),s=l[i]||(l[i]=fv(r,i));let c;c=a in s.icons?t.loaded:""===i||s.missing.has(a)?t.missing:t.pending;const u={provider:r,prefix:i,name:a};c.push(u)})),t}(function(e,t=!0,n=!1){const o=[];return e.forEach((e=>{const r="string"==typeof e?tv(e,t,n):e;r&&o.push(r)})),o}(e,!0,gv()));if(!n.pending.length){let e=!0;return t&&setTimeout((()=>{e&&t(n.loaded,n.missing,n.pending,cm)})),()=>{e=!1}}const o=Object.create(null),r=[];let i,a;return n.pending.forEach((e=>{const{provider:t,prefix:n}=e;if(n===a&&t===i)return;i=t,a=n,r.push(fv(t,n));const l=o[t]||(o[t]=Object.create(null));l[n]||(l[n]=[])})),n.pending.forEach((e=>{const{provider:t,prefix:n,name:r}=e,i=fv(t,n),a=i.pendingIcons||(i.pendingIcons=new Set);a.has(r)||(a.add(r),o[t][n].push(r))})),r.forEach((e=>{const{provider:t,prefix:n}=e;o[t][n].length&&function(e,t){e.iconsToLoad?e.iconsToLoad=e.iconsToLoad.concat(t).sort():e.iconsToLoad=t,e.iconsQueueFlag||(e.iconsQueueFlag=!0,setTimeout((()=>{e.iconsQueueFlag=!1;const{provider:t,prefix:n}=e,o=e.iconsToLoad;let r;delete e.iconsToLoad,o&&(r=Av(t))&&r.prepare(t,n,o).forEach((n=>{Uv(t,n,(t=>{if("object"!=typeof t)n.icons.forEach((t=>{e.missing.add(t)}));else try{const n=vv(e,t);if(!n.length)return;const o=e.pendingIcons;o&&n.forEach((e=>{o.delete(e)})),sm(e,t)}catch(o){}um(e)}))}))})))}(e,o[t][n])})),t?function(e,t,n){const o=Dv++,r=Bv.bind(null,n,o);if(!t.pending.length)return r;const i={id:o,icons:t,callback:e,abort:r};return n.forEach((e=>{(e.loaderCallbacks||(e.loaderCallbacks=[])).push(i)})),r}(t,n,r):cm},pm=/[\s,]+/;function hm(e,t){t.split(pm).forEach((t=>{switch(t.trim()){case"horizontal":e.hFlip=!0;break;case"vertical":e.vFlip=!0}}))}function fm(e,t=0){const n=e.replace(/^-?[0-9.]*/,"");function o(e){for(;e<0;)e+=4;return e%4}if(""===n){const t=parseInt(e);return isNaN(t)?0:o(t)}if(n!==e){let t=0;switch(n){case"%":t=25;break;case"deg":t=90}if(t){let r=parseFloat(e.slice(0,e.length-n.length));return isNaN(r)?0:(r/=t,r%1==0?o(r):0)}}return t}const vm=p(d({},xv),{inline:!1}),mm={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink","aria-hidden":!0,role:"img"},gm={display:"inline-block"},bm={backgroundColor:"currentColor"},ym={backgroundColor:"transparent"},xm={Image:"var(--svg)",Repeat:"no-repeat",Size:"100% 100%"},Cm={webkitMask:bm,mask:bm,background:ym};for(const c in Cm){const e=Cm[c];for(const t in xm)e[c+t]=xm[t]}const wm={};function km(e){return e+(e.match(/^[-0-9.]+$/)?"px":"")}["horizontal","vertical"].forEach((e=>{const t=e.slice(0,1)+"Flip";wm[e+"-flip"]=t,wm[e.slice(0,1)+"-flip"]=t,wm[e+"Flip"]=t}));const Sm=(e,t)=>{const n=function(e,t){const n=d({},e);for(const o in t){const e=t[o],r=typeof e;o in yv?(null===e||e&&("string"===r||"number"===r))&&(n[o]=e):r===typeof n[o]&&(n[o]="rotate"===o?e%4:e)}return n}(vm,t),o=d({},mm),r=t.mode||"svg",i={},a=t.style,l="object"!=typeof a||a instanceof Array?{}:a;for(let d in t){const e=t[d];if(void 0!==e)switch(d){case"icon":case"style":case"onLoad":case"mode":break;case"inline":case"hFlip":case"vFlip":n[d]=!0===e||"true"===e||1===e;break;case"flip":"string"==typeof e&&hm(n,e);break;case"color":i.color=e;break;case"rotate":"string"==typeof e?n[d]=fm(e):"number"==typeof e&&(n[d]=e);break;case"ariaHidden":case"aria-hidden":!0!==e&&"true"!==e&&delete o["aria-hidden"];break;default:{const t=wm[d];t?!0!==e&&"true"!==e&&1!==e||(n[t]=!0):void 0===vm[d]&&(o[d]=e)}}}const s=function(e,t){const n=d(d({},iv),e),o=d(d({},xv),t),r={left:n.left,top:n.top,width:n.width,height:n.height};let i=n.body;[n,o].forEach((e=>{const t=[],n=e.hFlip,o=e.vFlip;let a,l=e.rotate;switch(n?o?l+=2:(t.push("translate("+(r.width+r.left).toString()+" "+(0-r.top).toString()+")"),t.push("scale(-1 1)"),r.top=r.left=0):o&&(t.push("translate("+(0-r.left).toString()+" "+(r.height+r.top).toString()+")"),t.push("scale(1 -1)"),r.top=r.left=0),l<0&&(l-=4*Math.floor(l/4)),l%=4,l){case 1:a=r.height/2+r.top,t.unshift("rotate(90 "+a.toString()+" "+a.toString()+")");break;case 2:t.unshift("rotate(180 "+(r.width/2+r.left).toString()+" "+(r.height/2+r.top).toString()+")");break;case 3:a=r.width/2+r.left,t.unshift("rotate(-90 "+a.toString()+" "+a.toString()+")")}l%2==1&&(r.left!==r.top&&(a=r.left,r.left=r.top,r.top=a),r.width!==r.height&&(a=r.width,r.width=r.height,r.height=a)),t.length&&(i=function(e,t,n){const o=function(e,t="defs"){let n="";const o=e.indexOf("<"+t);for(;o>=0;){const r=e.indexOf(">",o),i=e.indexOf("",i);if(-1===a)break;n+=e.slice(r+1,i).trim(),e=e.slice(0,o).trim()+e.slice(a+1)}return{defs:n,content:e}}(e);return r=o.defs,i=t+o.content+n,r?""+r+""+i:i;var r,i}(i,'',""))}));const a=o.width,l=o.height,s=r.width,c=r.height;let u,p;null===a?(p=null===l?"1em":"auto"===l?c:l,u=kv(p,s/c)):(u="auto"===a?s:a,p=null===l?kv(u,c/s):"auto"===l?c:l);const h={},f=(e,t)=>{(e=>"unset"===e||"undefined"===e||"none"===e)(t)||(h[e]=t.toString())};f("width",u),f("height",p);const v=[r.left,r.top,s,c];return h.viewBox=v.join(" "),{attributes:h,viewBox:v,body:i}}(e,n),c=s.attributes;if(n.inline&&(i.verticalAlign="-0.125em"),"svg"===r){o.style=d(d({},i),l),Object.assign(o,c);let e=0,n=t.id;return"string"==typeof n&&(n=n.replace(/-/g,"_")),o.innerHTML=function(e,t=_v){const n=[];let o;for(;o=Sv.exec(e);)n.push(o[1]);if(!n.length)return e;const r="suffix"+(16777216*Math.random()|Date.now()).toString(16);return n.forEach((n=>{const o="function"==typeof t?t(n):t+(Pv++).toString(),i=n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");e=e.replace(new RegExp('([#;"])('+i+')([")]|\\.[a-z])',"g"),"$1"+o+r+"$3")})),e=e.replace(new RegExp(r,"g"),"")}(s.body,n?()=>n+"ID"+e++:"iconifyVue"),li("svg",o)}const{body:u,width:h,height:f}=e,v="mask"===r||"bg"!==r&&-1!==u.indexOf("currentColor"),m=function(e,t){let n=-1===e.indexOf("xlink:")?"":' xmlns:xlink="http://www.w3.org/1999/xlink"';for(const o in t)n+=" "+o+'="'+t[o]+'"';return'"+e+""}(u,p(d({},c),{width:h+"",height:f+""}));var g;return o.style=d(d(d(p(d({},i),{"--svg":(g=m,'url("'+function(e){return"data:image/svg+xml,"+function(e){return e.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(//g,"%3E").replace(/\s+/g," ")}(e)}(g)+'")'),width:km(c.width),height:km(c.height)}),gm),v?bm:ym),l),li("span",o)};var _m;if(gv(!0),_m=Lv,Tv[""]=_m,"undefined"!=typeof document&&"undefined"!=typeof window){lm();const e=window;if(void 0!==e.IconifyPreload){const t=e.IconifyPreload;"object"==typeof t&&null!==t&&(t instanceof Array?t:[t]).forEach((e=>{try{"object"!=typeof e||null===e||e instanceof Array||"object"!=typeof e.icons||"string"!=typeof e.prefix||function(e,t){if("object"!=typeof e)return!1;if("string"!=typeof t&&(t=e.provider||""),mv&&!t&&!e.prefix){let t=!1;return pv(e)&&(e.prefix="",cv(e,((e,n)=>{n&&bv(e,n)&&(t=!0)}))),t}const n=e.prefix;nv({provider:t,prefix:n,name:"a"})&&vv(fv(t,n),e)}(e)}catch(t){}}))}if(void 0!==e.IconifyProviders){const t=e.IconifyProviders;if("object"==typeof t&&null!==t)for(let e in t)try{const n=t[e];if("object"!=typeof n||!n||void 0===n.resources)continue;Mv(e,n)}catch(WQ){}}}const Pm=p(d({},iv),{body:""}),Tm=zn({inheritAttrs:!1,data:()=>({_name:"",_loadingIcon:null,iconMounted:!1,counter:0}),mounted(){this.iconMounted=!0},unmounted(){this.abortLoading()},methods:{abortLoading(){this._loadingIcon&&(this._loadingIcon.abort(),this._loadingIcon=null)},getIcon(e,t){if("object"==typeof e&&null!==e&&"string"==typeof e.body)return this._name="",this.abortLoading(),{data:e};let n;if("string"!=typeof e||null===(n=tv(e,!1,!0)))return this.abortLoading(),null;const o=function(e){const t="string"==typeof e?tv(e,!0,mv):e;if(t){const e=fv(t.provider,t.prefix),n=t.name;return e.icons[n]||(e.missing.has(n)?null:void 0)}}(n);if(!o)return this._loadingIcon&&this._loadingIcon.name===e||(this.abortLoading(),this._name="",null!==o&&(this._loadingIcon={name:e,abort:dm([n],(()=>{this.counter++}))})),null;this.abortLoading(),this._name!==e&&(this._name=e,t&&t(e));const r=["iconify"];return""!==n.prefix&&r.push("iconify--"+n.prefix),""!==n.provider&&r.push("iconify--"+n.provider),{data:o,classes:r}}},render(){this.counter;const e=this.$attrs,t=this.iconMounted||e.ssr?this.getIcon(e.icon,e.onLoad):null;if(!t)return Sm(Pm,e);let n=e;return t.classes&&(n=p(d({},e),{class:("string"==typeof e.class?e.class+" ":"")+t.classes.join(" ")})),Sm(d(d({},iv),t.data),n)}});let Am=[];const zm=new WeakMap;function Rm(){Am.forEach((e=>e(...zm.get(e)))),Am=[]}function Em(e,...t){zm.set(e,t),Am.includes(e)||1===Am.push(e)&&requestAnimationFrame(Rm)}function Om(e){if(null===e)return null;const t=function(e){return 9===e.nodeType?null:e.parentNode}(e);if(null===t)return null;if(9===t.nodeType)return document.documentElement;if(1===t.nodeType){const{overflow:e,overflowX:n,overflowY:o}=getComputedStyle(t);if(/(auto|scroll|overlay)/.test(e+o+n))return t}return Om(t)}function Mm(e,t){let{target:n}=e;for(;n;){if(n.dataset&&void 0!==n.dataset[t])return!0;n=n.parentElement}return!1}function Fm(e){return e.composedPath()[0]||null}function Im(e){return"string"==typeof e?e.endsWith("px")?Number(e.slice(0,e.length-2)):Number(e):e}function Lm(e){if(null!=e)return"number"==typeof e?`${e}px`:e.endsWith("px")?e:`${e}px`}function Bm(e,t){const n=e.trim().split(/\s+/g),o={top:n[0]};switch(n.length){case 1:o.right=n[0],o.bottom=n[0],o.left=n[0];break;case 2:o.right=n[1],o.left=n[1],o.bottom=n[0];break;case 3:o.right=n[1],o.bottom=n[2],o.left=n[1];break;case 4:o.right=n[1],o.bottom=n[2],o.left=n[3];break;default:throw new Error("[seemly/getMargin]:"+e+" is not a valid value.")}return void 0===t?o:o[t]}const Dm={black:"#000",silver:"#C0C0C0",gray:"#808080",white:"#FFF",maroon:"#800000",red:"#F00",purple:"#800080",fuchsia:"#F0F",green:"#008000",lime:"#0F0",olive:"#808000",yellow:"#FF0",navy:"#000080",blue:"#00F",teal:"#008080",aqua:"#0FF",transparent:"#0000"},$m="^\\s*",Nm="\\s*$",jm="\\s*((\\.\\d+)|(\\d+(\\.\\d*)?))\\s*",Hm="([0-9A-Fa-f])",Wm="([0-9A-Fa-f]{2})",Um=new RegExp(`${$m}rgb\\s*\\(${jm},${jm},${jm}\\)${Nm}`),Vm=new RegExp(`${$m}rgba\\s*\\(${jm},${jm},${jm},${jm}\\)${Nm}`),qm=new RegExp(`${$m}#${Hm}${Hm}${Hm}${Nm}`),Km=new RegExp(`${$m}#${Wm}${Wm}${Wm}${Nm}`),Gm=new RegExp(`${$m}#${Hm}${Hm}${Hm}${Hm}${Nm}`),Xm=new RegExp(`${$m}#${Wm}${Wm}${Wm}${Wm}${Nm}`);function Ym(e){return parseInt(e,16)}function Qm(e){try{let t;if(t=Km.exec(e))return[Ym(t[1]),Ym(t[2]),Ym(t[3]),1];if(t=Um.exec(e))return[rg(t[1]),rg(t[5]),rg(t[9]),1];if(t=Vm.exec(e))return[rg(t[1]),rg(t[5]),rg(t[9]),og(t[13])];if(t=qm.exec(e))return[Ym(t[1]+t[1]),Ym(t[2]+t[2]),Ym(t[3]+t[3]),1];if(t=Xm.exec(e))return[Ym(t[1]),Ym(t[2]),Ym(t[3]),og(Ym(t[4])/255)];if(t=Gm.exec(e))return[Ym(t[1]+t[1]),Ym(t[2]+t[2]),Ym(t[3]+t[3]),og(Ym(t[4]+t[4])/255)];if(e in Dm)return Qm(Dm[e]);throw new Error(`[seemly/rgba]: Invalid color value ${e}.`)}catch(WQ){throw WQ}}function Zm(e,t,n,o){return`rgba(${rg(e)}, ${rg(t)}, ${rg(n)}, ${r=o,r>1?1:r<0?0:r})`;var r}function Jm(e,t,n,o,r){return rg((e*t*(1-o)+n*o)/r)}function eg(e,t){Array.isArray(e)||(e=Qm(e)),Array.isArray(t)||(t=Qm(t));const n=e[3],o=t[3],r=og(n+o-n*o);return Zm(Jm(e[0],n,t[0],o,r),Jm(e[1],n,t[1],o,r),Jm(e[2],n,t[2],o,r),r)}function tg(e,t){const[n,o,r,i=1]=Array.isArray(e)?e:Qm(e);return t.alpha?Zm(n,o,r,t.alpha):Zm(n,o,r,i)}function ng(e,t){const[n,o,r,i=1]=Array.isArray(e)?e:Qm(e),{lightness:a=1,alpha:l=1}=t;return function(e){const[t,n,o]=e;return 3 in e?`rgba(${rg(t)}, ${rg(n)}, ${rg(o)}, ${og(e[3])})`:`rgba(${rg(t)}, ${rg(n)}, ${rg(o)}, 1)`}([n*a,o*a,r*a,i*l])}function og(e){const t=Math.round(100*Number(e))/100;return t>1?1:t<0?0:t}function rg(e){const t=Math.round(Number(e));return t>255?255:t<0?0:t}function ig(e=8){return Math.random().toString(16).slice(2,2+e)}function ag(e,t){const n=[];for(let o=0;o{o[t]=e[t]})),Object.assign(o,n)}function cg(e,t=[],n){const o={};return Object.getOwnPropertyNames(e).forEach((n=>{t.includes(n)||(o[n]=e[n])})),Object.assign(o,n)}function ug(e,t=!0,n=[]){return e.forEach((e=>{if(null!==e)if("object"==typeof e)if(Array.isArray(e))ug(e,t,n);else if(e.type===yr){if(null===e.children)return;Array.isArray(e.children)&&ug(e.children,t,n)}else{if(e.type===Cr&&t)return;n.push(e)}else"string"!=typeof e&&"number"!=typeof e||n.push(Dr(String(e)))})),n}function dg(e,...t){if(!Array.isArray(e))return e(...t);e.forEach((e=>dg(e,...t)))}function pg(e){return Object.keys(e)}function hg(e,...t){return"function"==typeof e?e(...t):"string"==typeof e?Dr(e):"number"==typeof e?Dr(String(e)):null}function fg(e,t){throw new Error(`[naive/${e}]: ${t}`)}function vg(e){switch(e){case"tiny":return"mini";case"small":return"tiny";case"medium":return"small";case"large":return"medium";case"huge":return"large"}throw new Error(`${e} has no smaller size.`)}function mg(e){switch(typeof e){case"string":return e||void 0;case"number":return String(e);default:return}}function gg(e,t="default",n=void 0){const o=e[t];if(!o)return null;const r=ug(o(n));return 1===r.length?r[0]:null}function bg(e){return t=>{e.value=t?t.$el:null}}function yg(e){return e.some((e=>!Er(e)||e.type!==Cr&&!(e.type===yr&&!yg(e.children))))?e:null}function xg(e,t){return e&&yg(e())||t()}function Cg(e,t,n){return e&&yg(e(t))||n(t)}function wg(e,t){return t(e&&yg(e())||null)}function kg(e){return!(e&&yg(e()))}function Sg(e){const t=e.filter((e=>void 0!==e));if(0!==t.length)return 1===t.length?t[0]:t=>{e.forEach((e=>{e&&e(t)}))}}const _g=zn({render(){var e,t;return null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)}}),Pg=/^(\d|\.)+$/,Tg=/(\d|\.)+/;function Ag(e,{c:t=1,offset:n=0,attachPx:o=!0}={}){if("number"==typeof e){const o=(e+n)*t;return 0===o?"0":`${o}px`}if("string"==typeof e){if(Pg.test(e)){const r=(Number(e)+n)*t;return o?0===r?"0":`${r}px`:`${r}`}{const o=Tg.exec(e);return o?e.replace(Tg,String((Number(o[0])+n)*t)):e}}return e}function zg(e){return e.replace(/#|\(|\)|,|\s|\./g,"_")}function Rg(e){const{left:t,right:n,top:o,bottom:r}=Bm(e);return`${o} ${n} ${r} ${t}`}const Eg=/\s*,(?![^(]*\))\s*/g,Og=/\s+/g;function Mg(e){let t=[""];return e.forEach((e=>{(e=e&&e.trim())&&(t=e.includes("&")?function(e,t){const n=[];return t.split(Eg).forEach((t=>{let o=function(e){let t=0;for(let n=0;n{n.push((e&&e+" ")+t)}));if(1===o)return void e.forEach((e=>{n.push(t.replace("&",e))}));let r=[t];for(;o--;){const t=[];r.forEach((n=>{e.forEach((e=>{t.push(n.replace("&",e))}))})),r=t}r.forEach((e=>n.push(e)))})),n}(t,e):function(e,t){const n=[];return t.split(Eg).forEach((t=>{e.forEach((e=>{n.push((e&&e+" ")+t)}))})),n}(t,e))})),t.join(", ").replace(Og," ")}function Fg(e){if(!e)return;const t=e.parentElement;t&&t.removeChild(e)}function Ig(e){return document.querySelector(`style[cssr-id="${e}"]`)}function Lg(e){return!!e&&/^\s*@(s|m)/.test(e)}const Bg=/[A-Z]/g;function Dg(e){return e.replace(Bg,(e=>"-"+e.toLowerCase()))}function $g(e,t,n,o){if(!t)return"";const r=function(e,t,n){return"function"==typeof e?e({context:t.context,props:n}):e}(t,n,o);if(!r)return"";if("string"==typeof r)return`${e} {\n${r}\n}`;const i=Object.keys(r);if(0===i.length)return n.config.keepEmptyBlock?e+" {\n}":"";const a=e?[e+" {"]:[];return i.forEach((e=>{const t=r[e];"raw"!==e?(e=Dg(e),null!=t&&a.push(` ${e}${function(e,t=" "){return"object"==typeof e&&null!==e?" {\n"+Object.entries(e).map((e=>t+` ${Dg(e[0])}: ${e[1]};`)).join("\n")+"\n"+t+"}":`: ${e};`}(t)}`)):a.push("\n"+t+"\n")})),e&&a.push("}"),a.join("\n")}function Ng(e,t,n){e&&e.forEach((e=>{if(Array.isArray(e))Ng(e,t,n);else if("function"==typeof e){const o=e(t);Array.isArray(o)?Ng(o,t,n):o&&n(o)}else e&&n(e)}))}function jg(e,t,n,o,r,i){const a=e.$;let l="";if(a&&"string"!=typeof a)if("function"==typeof a){const e=a({context:o.context,props:r});Lg(e)?l=e:t.push(e)}else if(a.before&&a.before(o.context),a.$&&"string"!=typeof a.$){if(a.$){const e=a.$({context:o.context,props:r});Lg(e)?l=e:t.push(e)}}else Lg(a.$)?l=a.$:t.push(a.$);else Lg(a)?l=a:t.push(a);const s=Mg(t),c=$g(s,e.props,o,r);l?(n.push(`${l} {`),i&&c&&i.insertRule(`${l} {\n${c}\n}\n`)):(i&&c&&i.insertRule(c),!i&&c.length&&n.push(c)),e.children&&Ng(e.children,{context:o.context,props:r},(e=>{if("string"==typeof e){const t=$g(s,{raw:e},o,r);i?i.insertRule(t):n.push(t)}else jg(e,t,n,o,r,i)})),t.pop(),l&&n.push("}"),a&&a.after&&a.after(o.context)}function Hg(e,t,n,o=!1){const r=[];return jg(e,[],r,t,n,o?e.instance.__styleSheet:void 0),o?"":r.join("\n\n")}function Wg(e){for(var t,n=0,o=0,r=e.length;r>=4;++o,r-=4)t=1540483477*(65535&(t=255&e.charCodeAt(o)|(255&e.charCodeAt(++o))<<8|(255&e.charCodeAt(++o))<<16|(255&e.charCodeAt(++o))<<24))+(59797*(t>>>16)<<16),n=1540483477*(65535&(t^=t>>>24))+(59797*(t>>>16)<<16)^1540483477*(65535&n)+(59797*(n>>>16)<<16);switch(r){case 3:n^=(255&e.charCodeAt(o+2))<<16;case 2:n^=(255&e.charCodeAt(o+1))<<8;case 1:n=1540483477*(65535&(n^=255&e.charCodeAt(o)))+(59797*(n>>>16)<<16)}return(((n=1540483477*(65535&(n^=n>>>13))+(59797*(n>>>16)<<16))^n>>>15)>>>0).toString(36)}function Ug(e,t){e.push(t)}function Vg(e,t,n,o,r,i,a,l,s){if(i&&!s){if(void 0===n)return;const r=window.__cssrContext;return void(r[n]||(r[n]=!0,Hg(t,e,o,i)))}let c;if(void 0===n&&(c=t.render(o),n=Wg(c)),s)return void s.adapter(n,null!=c?c:t.render(o));const u=Ig(n);if(null!==u&&!a)return u;const d=null!=u?u:function(e){const t=document.createElement("style");return t.setAttribute("cssr-id",e),t}(n);if(void 0===c&&(c=t.render(o)),d.textContent=c,null!==u)return u;if(l){const e=document.head.querySelector(`meta[name="${l}"]`);if(e)return document.head.insertBefore(d,e),Ug(t.els,d),d}return r?document.head.insertBefore(d,document.head.querySelector("style, link")):document.head.appendChild(d),Ug(t.els,d),d}function qg(e){return Hg(this,this.instance,e)}function Kg(e={}){const{id:t,ssr:n,props:o,head:r=!1,silent:i=!1,force:a=!1,anchorMetaName:l}=e;return Vg(this.instance,this,t,o,r,i,a,l,n)}function Gg(e={}){const{id:t}=e;!function(e,t,n){const{els:o}=t;if(void 0===n)o.forEach(Fg),t.els=[];else{const e=Ig(n);e&&o.includes(e)&&(Fg(e),t.els=o.filter((t=>t!==e)))}}(this.instance,this,t)}"undefined"!=typeof window&&(window.__cssrContext={});const Xg=function(e,t,n,o){return{instance:e,$:t,props:n,children:o,els:[],render:qg,mount:Kg,unmount:Gg}};function Yg(e={}){let t=null;const n={c:(...e)=>function(e,t,n,o){return Array.isArray(t)?Xg(e,{$:null},null,t):Array.isArray(n)?Xg(e,t,null,n):Array.isArray(o)?Xg(e,t,n,o):Xg(e,t,n,null)}(n,...e),use:(e,...t)=>e.install(n,...t),find:Ig,context:{},config:e,get __styleSheet(){if(!t){const e=document.createElement("style");return document.head.appendChild(e),t=document.styleSheets[document.styleSheets.length-1],t}return t}};return n}const Qg=".n-",Zg=Yg(),Jg=function(e){let t,n=".",o="__",r="--";if(e){let t=e.blockPrefix;t&&(n=t),t=e.elementPrefix,t&&(o=t),t=e.modifierPrefix,t&&(r=t)}const i={install(e){t=e.c;const n=e.context;n.bem={},n.bem.b=null,n.bem.els=null}};return Object.assign(i,{cB:(...e)=>t(function(e){let t,o;return{before(e){t=e.bem.b,o=e.bem.els,e.bem.els=null},after(e){e.bem.b=t,e.bem.els=o},$:({context:t,props:o})=>(e="string"==typeof e?e:e({context:t,props:o}),t.bem.b=e,`${(null==o?void 0:o.bPrefix)||n}${t.bem.b}`)}}(e[0]),e[1],e[2]),cE:(...e)=>t(function(e){let t;return{before(e){t=e.bem.els},after(e){e.bem.els=t},$:({context:t,props:r})=>(e="string"==typeof e?e:e({context:t,props:r}),t.bem.els=e.split(",").map((e=>e.trim())),t.bem.els.map((e=>`${(null==r?void 0:r.bPrefix)||n}${t.bem.b}${o}${e}`)).join(", "))}}(e[0]),e[1],e[2]),cM:(...e)=>{return t((i=e[0],{$({context:e,props:t}){const a=(i="string"==typeof i?i:i({context:e,props:t})).split(",").map((e=>e.trim()));function l(i){return a.map((a=>`&${(null==t?void 0:t.bPrefix)||n}${e.bem.b}${void 0!==i?`${o}${i}`:""}${r}${a}`)).join(", ")}const s=e.bem.els;return null!==s?l(s[0]):l()}}),e[1],e[2]);var i},cNotM:(...e)=>{return t((i=e[0],{$({context:e,props:t}){i="string"==typeof i?i:i({context:e,props:t});const a=e.bem.els;return`&:not(${(null==t?void 0:t.bPrefix)||n}${e.bem.b}${null!==a&&a.length>0?`${o}${a[0]}`:""}${r}${i})`}}),e[1],e[2]);var i}}),i}({blockPrefix:Qg,elementPrefix:"__",modifierPrefix:"--"});Zg.use(Jg);const{c:eb,find:tb}=Zg,{cB:nb,cE:ob,cM:rb,cNotM:ib}=Jg;function ab(e){return eb((({props:{bPrefix:e}})=>`${e||Qg}modal, ${e||Qg}drawer`),[e])}function lb(e){return eb((({props:{bPrefix:e}})=>`${e||Qg}popover`),[e])}function sb(e){return eb((({props:{bPrefix:e}})=>`&${e||Qg}modal`),e)}const cb=(...e)=>eb(">",[nb(...e)]);function ub(e,t){return e+("default"===t?"":t.replace(/^[a-z]/,(e=>e.toUpperCase())))}let db;const pb="undefined"!=typeof document&&"undefined"!=typeof window,hb=new WeakSet;function fb(e){return!hb.has(e)}function vb(e){const t=Et(!!e.value);if(t.value)return gt(t);const n=lr(e,(e=>{e&&(t.value=!0,n())}));return gt(t)}function mb(e){const t=ai(e),n=Et(t.value);return lr(t,(e=>{n.value=e})),"function"==typeof e?n:{__v_isRef:!0,get value(){return n.value},set value(t){e.set(t)}}}function gb(){return null!==Gr()}const bb="undefined"!=typeof window;let yb,xb;var Cb,wb;function kb(e){return e.composedPath()[0]}yb=bb?null===(wb=null===(Cb=document)||void 0===Cb?void 0:Cb.fonts)||void 0===wb?void 0:wb.ready:void 0,xb=!1,void 0!==yb?yb.then((()=>{xb=!0})):xb=!0;const Sb={mousemoveoutside:new WeakMap,clickoutside:new WeakMap};function _b(e,t,n){const o=Sb[e];let r=o.get(t);void 0===r&&o.set(t,r=new WeakMap);let i=r.get(n);return void 0===i&&r.set(n,i=function(e,t,n){if("mousemoveoutside"===e){const e=e=>{t.contains(kb(e))||n(e)};return{mousemove:e,touchstart:e}}if("clickoutside"===e){let e=!1;const o=n=>{e=!t.contains(kb(n))},r=o=>{e&&(t.contains(kb(o))||n(o))};return{mousedown:o,mouseup:r,touchstart:o,touchend:r}}return{}}(e,t,n)),i}const{on:Pb,off:Tb}=function(){if("undefined"==typeof window)return{on:()=>{},off:()=>{}};const e=new WeakMap,t=new WeakMap;function n(){e.set(this,!0)}function o(){e.set(this,!0),t.set(this,!0)}function r(e,t,n){const o=e[t];return e[t]=function(){return n.apply(e,arguments),o.apply(e,arguments)},e}function i(e,t){e[t]=Event.prototype[t]}const a=new WeakMap,l=Object.getOwnPropertyDescriptor(Event.prototype,"currentTarget");function s(){var e;return null!==(e=a.get(this))&&void 0!==e?e:null}function c(e,t){void 0!==l&&Object.defineProperty(e,"currentTarget",{configurable:!0,enumerable:!0,get:null!=t?t:l.get})}const u={bubble:{},capture:{}},d={},p=function(){const l=function(l){const{type:d,eventPhase:p,bubbles:h}=l,f=kb(l);if(2===p)return;const v=1===p?"capture":"bubble";let m=f;const g=[];for(;null===m&&(m=window),g.push(m),m!==window;)m=m.parentNode||null;const b=u.capture[d],y=u.bubble[d];if(r(l,"stopPropagation",n),r(l,"stopImmediatePropagation",o),c(l,s),"capture"===v){if(void 0===b)return;for(let n=g.length-1;n>=0&&!e.has(l);--n){const e=g[n],o=b.get(e);if(void 0!==o){a.set(l,e);for(const e of o){if(t.has(l))break;e(l)}}if(0===n&&!h&&void 0!==y){const n=y.get(e);if(void 0!==n)for(const e of n){if(t.has(l))break;e(l)}}}}else if("bubble"===v){if(void 0===y)return;for(let n=0;nt(e)))};return e.displayName="evtdUnifiedWindowEventHandler",e}();function f(e,t){const n=u[e];return void 0===n[t]&&(n[t]=new Map,window.addEventListener(t,p,"capture"===e)),n[t]}function v(e,t){let n=e.get(t);return void 0===n&&e.set(t,n=new Set),n}function m(e,t,n,o){const r=function(e,t,n,o){if("mousemoveoutside"===e||"clickoutside"===e){const r=_b(e,t,n);return Object.keys(r).forEach((e=>{Tb(e,document,r[e],o)})),!0}return!1}(e,t,n,o);if(r)return;const i=!0===o||"object"==typeof o&&!0===o.capture,a=i?"capture":"bubble",l=f(a,e),s=v(l,t);if(t===window&&!function(e,t,n,o){const r=u[t][n];if(void 0!==r){const t=r.get(e);if(void 0!==t&&t.has(o))return!0}return!1}(t,i?"bubble":"capture",e,n)&&function(e,t){const n=d[e];return!(void 0===n||!n.has(t))}(e,n)){const t=d[e];t.delete(n),0===t.size&&(window.removeEventListener(e,h),d[e]=void 0)}s.has(n)&&s.delete(n),0===s.size&&l.delete(t),0===l.size&&(window.removeEventListener(e,p,"capture"===a),u[a][e]=void 0)}return{on:function(e,t,n,o){let r;if(r="object"==typeof o&&!0===o.once?i=>{m(e,t,r,o),n(i)}:n,function(e,t,n,o){if("mousemoveoutside"===e||"clickoutside"===e){const r=_b(e,t,n);return Object.keys(r).forEach((e=>{Pb(e,document,r[e],o)})),!0}return!1}(e,t,r,o))return;const i=v(f(!0===o||"object"==typeof o&&!0===o.capture?"capture":"bubble",e),t);if(i.has(r)||i.add(r),t===window){const t=function(e){return void 0===d[e]&&(d[e]=new Set,window.addEventListener(e,h)),d[e]}(e);t.has(r)||t.add(r)}},off:m}}(),Ab=Et(null);function zb(e){if(e.clientX>0||e.clientY>0)Ab.value={x:e.clientX,y:e.clientY};else{const{target:t}=e;if(t instanceof Element){const{left:e,top:n,width:o,height:r}=t.getBoundingClientRect();Ab.value=e>0||n>0?{x:e+o/2,y:n+r/2}:{x:0,y:0}}else Ab.value=null}}let Rb=0,Eb=!0;function Ob(){if(!bb)return gt(Et(null));0===Rb&&Pb("click",document,zb,!0);const e=()=>{Rb+=1};return Eb&&(Eb=gb())?(Dn(e),Hn((()=>{Rb-=1,0===Rb&&Tb("click",document,zb,!0)}))):e(),gt(Ab)}const Mb=Et(void 0);let Fb=0;function Ib(){Mb.value=Date.now()}let Lb=!0;function Bb(e){if(!bb)return gt(Et(!1));const t=Et(!1);let n=null;function o(){null!==n&&window.clearTimeout(n)}function r(){o(),t.value=!0,n=window.setTimeout((()=>{t.value=!1}),e)}0===Fb&&Pb("click",window,Ib,!0);const i=()=>{Fb+=1,Pb("click",window,r,!0)};return Lb&&(Lb=gb())?(Dn(i),Hn((()=>{Fb-=1,0===Fb&&Tb("click",window,Ib,!0),Tb("click",window,r,!0),o()}))):i(),gt(t)}function Db(e,t){return lr(e,(e=>{void 0!==e&&(t.value=e)})),ai((()=>void 0===e.value?t.value:e.value))}function $b(){const e=Et(!1);return $n((()=>{e.value=!0})),gt(e)}function Nb(e,t){return ai((()=>{for(const n of t)if(void 0!==e[n])return e[n];return e[t[t.length-1]]}))}const jb="undefined"!=typeof window&&(/iPad|iPhone|iPod/.test(navigator.platform)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1)&&!window.MSStream,Hb="n-internal-select-menu",Wb="n-internal-select-menu-body",Ub="n-modal-body",Vb="n-modal",qb="n-drawer-body",Kb="n-drawer",Gb="n-popover-body",Xb="__disabled__";function Yb(e){const t=Po(Ub,null),n=Po(qb,null),o=Po(Gb,null),r=Po(Wb,null),i=Et();if("undefined"!=typeof document){i.value=document.fullscreenElement;const e=()=>{i.value=document.fullscreenElement};$n((()=>{Pb("fullscreenchange",document,e)})),Hn((()=>{Tb("fullscreenchange",document,e)}))}return mb((()=>{var a;const{to:l}=e;return void 0!==l?!1===l?Xb:!0===l?i.value||"body":l:(null==t?void 0:t.value)?null!==(a=t.value.$el)&&void 0!==a?a:t.value:(null==n?void 0:n.value)?n.value:(null==o?void 0:o.value)?o.value:(null==r?void 0:r.value)?r.value:null!=l?l:i.value||"body"}))}Yb.tdkey=Xb,Yb.propTo={type:[String,Object,Boolean],default:void 0};let Qb=!1;function Zb(e,t,n="default"){const o=t[n];if(void 0===o)throw new Error(`[vueuc/${e}]: slot[${n}] is empty.`);return o()}function Jb(e,t=!0,n=[]){return e.forEach((e=>{if(null!==e)if("object"==typeof e)if(Array.isArray(e))Jb(e,t,n);else if(e.type===yr){if(null===e.children)return;Array.isArray(e.children)&&Jb(e.children,t,n)}else e.type!==Cr&&n.push(e);else"string"!=typeof e&&"number"!=typeof e||n.push(Dr(String(e)))})),n}function ey(e,t,n="default"){const o=t[n];if(void 0===o)throw new Error(`[vueuc/${e}]: slot[${n}] is empty.`);const r=Jb(o());if(1===r.length)return r[0];throw new Error(`[vueuc/${e}]: slot[${n}] should have exactly one child.`)}let ty=null;function ny(){if(null===ty&&(ty=document.getElementById("v-binder-view-measurer"),null===ty)){ty=document.createElement("div"),ty.id="v-binder-view-measurer";const{style:e}=ty;e.position="fixed",e.left="0",e.right="0",e.top="0",e.bottom="0",e.pointerEvents="none",e.visibility="hidden",document.body.appendChild(ty)}return ty.getBoundingClientRect()}function oy(e){const t=e.getBoundingClientRect(),n=ny();return{left:t.left-n.left,top:t.top-n.top,bottom:n.height+n.top-t.bottom,right:n.width+n.left-t.right,width:t.width,height:t.height}}function ry(e){if(null===e)return null;const t=function(e){return 9===e.nodeType?null:e.parentNode}(e);if(null===t)return null;if(9===t.nodeType)return document;if(1===t.nodeType){const{overflow:e,overflowX:n,overflowY:o}=getComputedStyle(t);if(/(auto|scroll|overlay)/.test(e+o+n))return t}return ry(t)}const iy=zn({name:"Binder",props:{syncTargetWithParent:Boolean,syncTarget:{type:Boolean,default:!0}},setup(e){var t;_o("VBinder",null===(t=Gr())||void 0===t?void 0:t.proxy);const n=Po("VBinder",null),o=Et(null);let r=[];const i=()=>{for(const e of r)Tb("scroll",e,l,!0);r=[]},a=new Set,l=()=>{Em(s)},s=()=>{a.forEach((e=>e()))},c=new Set,u=()=>{c.forEach((e=>e()))};return Hn((()=>{Tb("resize",window,u),i()})),{targetRef:o,setTargetRef:t=>{o.value=t,n&&e.syncTargetWithParent&&n.setTargetRef(t)},addScrollListener:e=>{0===a.size&&(()=>{let e=o.value;for(;e=ry(e),null!==e;)r.push(e);for(const t of r)Pb("scroll",t,l,!0)})(),a.has(e)||a.add(e)},removeScrollListener:e=>{a.has(e)&&a.delete(e),0===a.size&&i()},addResizeListener:e=>{0===c.size&&Pb("resize",window,u),c.has(e)||c.add(e)},removeResizeListener:e=>{c.has(e)&&c.delete(e),0===c.size&&Tb("resize",window,u)}}},render(){return Zb("binder",this.$slots)}}),ay=iy,ly=zn({name:"Target",setup(){const{setTargetRef:e,syncTarget:t}=Po("VBinder");return{syncTarget:t,setTargetDirective:{mounted:e,updated:e}}},render(){const{syncTarget:e,setTargetDirective:t}=this;return e?fn(ey("follower",this.$slots),[[t]]):ey("follower",this.$slots)}}),sy="@@mmoContext",cy={mounted(e,{value:t}){e[sy]={handler:void 0},"function"==typeof t&&(e[sy].handler=t,Pb("mousemoveoutside",e,t))},updated(e,{value:t}){const n=e[sy];"function"==typeof t?n.handler?n.handler!==t&&(Tb("mousemoveoutside",e,n.handler),n.handler=t,Pb("mousemoveoutside",e,t)):(e[sy].handler=t,Pb("mousemoveoutside",e,t)):n.handler&&(Tb("mousemoveoutside",e,n.handler),n.handler=void 0)},unmounted(e){const{handler:t}=e[sy];t&&Tb("mousemoveoutside",e,t),e[sy].handler=void 0}},uy="@@coContext",dy={mounted(e,{value:t,modifiers:n}){e[uy]={handler:void 0},"function"==typeof t&&(e[uy].handler=t,Pb("clickoutside",e,t,{capture:n.capture}))},updated(e,{value:t,modifiers:n}){const o=e[uy];"function"==typeof t?o.handler?o.handler!==t&&(Tb("clickoutside",e,o.handler,{capture:n.capture}),o.handler=t,Pb("clickoutside",e,t,{capture:n.capture})):(e[uy].handler=t,Pb("clickoutside",e,t,{capture:n.capture})):o.handler&&(Tb("clickoutside",e,o.handler,{capture:n.capture}),o.handler=void 0)},unmounted(e,{modifiers:t}){const{handler:n}=e[uy];n&&Tb("clickoutside",e,n,{capture:t.capture}),e[uy].handler=void 0}},py=new class{constructor(){this.elementZIndex=new Map,this.nextZIndex=2e3}get elementCount(){return this.elementZIndex.size}ensureZIndex(e,t){const{elementZIndex:n}=this;if(void 0!==t)return e.style.zIndex=`${t}`,void n.delete(e);const{nextZIndex:o}=this;n.has(e)&&n.get(e)+1===this.nextZIndex||(e.style.zIndex=`${o}`,n.set(e,o),this.nextZIndex=o+1,this.squashState())}unregister(e,t){const{elementZIndex:n}=this;n.has(e)&&n.delete(e),this.squashState()}squashState(){const{elementCount:e}=this;e||(this.nextZIndex=2e3),this.nextZIndex-e>2500&&this.rearrange()}rearrange(){const e=Array.from(this.elementZIndex.entries());e.sort(((e,t)=>e[1]-t[1])),this.nextZIndex=2e3,e.forEach((e=>{const t=e[0],n=this.nextZIndex++;`${n}`!==t.style.zIndex&&(t.style.zIndex=`${n}`)}))}},hy="@@ziContext",fy={mounted(e,t){const{value:n={}}=t,{zIndex:o,enabled:r}=n;e[hy]={enabled:!!r,initialized:!1},r&&(py.ensureZIndex(e,o),e[hy].initialized=!0)},updated(e,t){const{value:n={}}=t,{zIndex:o,enabled:r}=n,i=e[hy].enabled;r&&!i&&(py.ensureZIndex(e,o),e[hy].initialized=!0),e[hy].enabled=!!r},unmounted(e,t){if(!e[hy].initialized)return;const{value:n={}}=t,{zIndex:o}=n;py.unregister(e,o)}},vy=Symbol("@css-render/vue3-ssr");function my(e,t){const n=Po(vy,null);if(null===n)return;const{styles:o,ids:r}=n;r.has(e)||null!==o&&(r.add(e),o.push(function(e,t){return``}(e,t)))}const gy="undefined"!=typeof document;function by(){if(gy)return;const e=Po(vy,null);return null!==e?{adapter:my,context:e}:void 0}const{c:yy}=Yg(),xy="vueuc-style";function Cy(e){return e&-e}class wy{constructor(e,t){this.l=e,this.min=t;const n=new Array(e+1);for(let o=0;oo)throw new Error("[FinweckTree.sum]: `i` is larger than length.");let r=e*n;for(;e>0;)r+=t[e],e-=Cy(e);return r}getBound(e){let t=0,n=this.l;for(;n>t;){const o=Math.floor((t+n)/2),r=this.sum(o);if(r>e)n=o;else{if(!(r({showTeleport:vb(jt(e,"show")),mergedTo:ai((()=>{const{to:t}=e;return null!=t?t:"body"}))}),render(){return this.showTeleport?this.disabled?Zb("lazy-teleport",this.$slots):li(Go,{disabled:this.disabled,to:this.mergedTo},Zb("lazy-teleport",this.$slots)):null}}),_y={top:"bottom",bottom:"top",left:"right",right:"left"},Py={start:"end",center:"center",end:"start"},Ty={top:"height",bottom:"height",left:"width",right:"width"},Ay={"bottom-start":"top left",bottom:"top center","bottom-end":"top right","top-start":"bottom left",top:"bottom center","top-end":"bottom right","right-start":"top left",right:"center left","right-end":"bottom left","left-start":"top right",left:"center right","left-end":"bottom right"},zy={"bottom-start":"bottom left",bottom:"bottom center","bottom-end":"bottom right","top-start":"top left",top:"top center","top-end":"top right","right-start":"top right",right:"center right","right-end":"bottom right","left-start":"top left",left:"center left","left-end":"bottom left"},Ry={"bottom-start":"right","bottom-end":"left","top-start":"right","top-end":"left","right-start":"bottom","right-end":"top","left-start":"bottom","left-end":"top"},Ey={top:!0,bottom:!1,left:!0,right:!1},Oy={top:"end",bottom:"start",left:"end",right:"start"},My=yy([yy(".v-binder-follower-container",{position:"absolute",left:"0",right:"0",top:"0",height:"0",pointerEvents:"none",zIndex:"auto"}),yy(".v-binder-follower-content",{position:"absolute",zIndex:"auto"},[yy("> *",{pointerEvents:"all"})])]),Fy=zn({name:"Follower",inheritAttrs:!1,props:{show:Boolean,enabled:{type:Boolean,default:void 0},placement:{type:String,default:"bottom"},syncTrigger:{type:Array,default:["resize","scroll"]},to:[String,Object],flip:{type:Boolean,default:!0},internalShift:Boolean,x:Number,y:Number,width:String,minWidth:String,containerClass:String,teleportDisabled:Boolean,zindexable:{type:Boolean,default:!0},zIndex:Number,overlap:Boolean},setup(e){const t=Po("VBinder"),n=mb((()=>void 0!==e.enabled?e.enabled:e.show)),o=Et(null),r=Et(null),i=()=>{const{syncTrigger:n}=e;n.includes("scroll")&&t.addScrollListener(s),n.includes("resize")&&t.addResizeListener(s)},a=()=>{t.removeScrollListener(s),t.removeResizeListener(s)};$n((()=>{n.value&&(s(),i())}));const l=by();My.mount({id:"vueuc/binder",head:!0,anchorMetaName:xy,ssr:l}),Hn((()=>{a()})),function(e){if(xb)return;let t=!1;$n((()=>{xb||null==yb||yb.then((()=>{t||e()}))})),Hn((()=>{t=!0}))}((()=>{n.value&&s()}));const s=()=>{if(!n.value)return;const i=o.value;if(null===i)return;const a=t.targetRef,{x:l,y:s,overlap:c}=e,u=void 0!==l&&void 0!==s?function(e,t){const n=ny();return{top:t,left:e,height:0,width:0,right:n.width-e,bottom:n.height-t}}(l,s):oy(a);i.style.setProperty("--v-target-width",`${Math.round(u.width)}px`),i.style.setProperty("--v-target-height",`${Math.round(u.height)}px`);const{width:d,minWidth:p,placement:h,internalShift:f,flip:v}=e;i.setAttribute("v-placement",h),c?i.setAttribute("v-overlap",""):i.removeAttribute("v-overlap");const{style:m}=i;m.width="target"===d?`${u.width}px`:void 0!==d?d:"",m.minWidth="target"===p?`${u.width}px`:void 0!==p?p:"";const g=oy(i),b=oy(r.value),{left:y,top:x,placement:C}=function(e,t,n,o,r,i){if(!r||i)return{placement:e,top:0,left:0};const[a,l]=e.split("-");let s=null!=l?l:"center",c={top:0,left:0};const u=(e,r,i)=>{let a=0,l=0;const s=n[e]-t[r]-t[e];return s>0&&o&&(i?l=Ey[r]?s:-s:a=Ey[r]?s:-s),{left:a,top:l}},d="left"===a||"right"===a;if("center"!==s){const o=Ry[e],r=_y[o],i=Ty[o];if(n[i]>t[i]){if(t[o]+t[i]t[r]&&(s=Py[l])}else{const e="bottom"===a||"top"===a?"left":"top",o=_y[e],r=Ty[e],i=(n[r]-t[r])/2;(t[e]t[o]?(s=Oy[e],c=u(r,e,d)):(s=Oy[o],c=u(r,o,d)))}let p=a;return t[a]{e?(i(),c()):a()}));const c=()=>{tn().then(s).catch((e=>{}))};["placement","x","y","internalShift","flip","width","overlap","minWidth"].forEach((t=>{lr(jt(e,t),s)})),["teleportDisabled"].forEach((t=>{lr(jt(e,t),c)})),lr(jt(e,"syncTrigger"),(e=>{e.includes("resize")?t.addResizeListener(s):t.removeResizeListener(s),e.includes("scroll")?t.addScrollListener(s):t.removeScrollListener(s)}));const u=$b(),d=mb((()=>{const{to:t}=e;if(void 0!==t)return t;u.value}));return{VBinder:t,mergedEnabled:n,offsetContainerRef:r,followerRef:o,mergedTo:d,syncPosition:s}},render(){return li(Sy,{show:this.show,to:this.mergedTo,disabled:this.teleportDisabled},{default:()=>{var e,t;const n=li("div",{class:["v-binder-follower-container",this.containerClass],ref:"offsetContainerRef"},[li("div",{class:"v-binder-follower-content",ref:"followerRef"},null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e))]);return this.zindexable?fn(n,[[fy,{enabled:this.mergedEnabled,zIndex:this.zIndex}]]):n}})}});var Iy,Ly,By=[],Dy="ResizeObserver loop completed with undelivered notifications.";(Ly=Iy||(Iy={})).BORDER_BOX="border-box",Ly.CONTENT_BOX="content-box",Ly.DEVICE_PIXEL_CONTENT_BOX="device-pixel-content-box";var $y,Ny=function(e){return Object.freeze(e)},jy=function(e,t){this.inlineSize=e,this.blockSize=t,Ny(this)},Hy=function(){function e(e,t,n,o){return this.x=e,this.y=t,this.width=n,this.height=o,this.top=this.y,this.left=this.x,this.bottom=this.top+this.height,this.right=this.left+this.width,Ny(this)}return e.prototype.toJSON=function(){var e=this;return{x:e.x,y:e.y,top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.width,height:e.height}},e.fromRect=function(t){return new e(t.x,t.y,t.width,t.height)},e}(),Wy=function(e){return e instanceof SVGElement&&"getBBox"in e},Uy=function(e){if(Wy(e)){var t=e.getBBox(),n=t.width,o=t.height;return!n&&!o}var r=e,i=r.offsetWidth,a=r.offsetHeight;return!(i||a||e.getClientRects().length)},Vy=function(e){var t;if(e instanceof Element)return!0;var n=null===(t=null==e?void 0:e.ownerDocument)||void 0===t?void 0:t.defaultView;return!!(n&&e instanceof n.Element)},qy="undefined"!=typeof window?window:{},Ky=new WeakMap,Gy=/auto|scroll/,Xy=/^tb|vertical/,Yy=/msie|trident/i.test(qy.navigator&&qy.navigator.userAgent),Qy=function(e){return parseFloat(e||"0")},Zy=function(e,t,n){return void 0===e&&(e=0),void 0===t&&(t=0),void 0===n&&(n=!1),new jy((n?t:e)||0,(n?e:t)||0)},Jy=Ny({devicePixelContentBoxSize:Zy(),borderBoxSize:Zy(),contentBoxSize:Zy(),contentRect:new Hy(0,0,0,0)}),ex=function(e,t){if(void 0===t&&(t=!1),Ky.has(e)&&!t)return Ky.get(e);if(Uy(e))return Ky.set(e,Jy),Jy;var n=getComputedStyle(e),o=Wy(e)&&e.ownerSVGElement&&e.getBBox(),r=!Yy&&"border-box"===n.boxSizing,i=Xy.test(n.writingMode||""),a=!o&&Gy.test(n.overflowY||""),l=!o&&Gy.test(n.overflowX||""),s=o?0:Qy(n.paddingTop),c=o?0:Qy(n.paddingRight),u=o?0:Qy(n.paddingBottom),d=o?0:Qy(n.paddingLeft),p=o?0:Qy(n.borderTopWidth),h=o?0:Qy(n.borderRightWidth),f=o?0:Qy(n.borderBottomWidth),v=d+c,m=s+u,g=(o?0:Qy(n.borderLeftWidth))+h,b=p+f,y=l?e.offsetHeight-b-e.clientHeight:0,x=a?e.offsetWidth-g-e.clientWidth:0,C=r?v+g:0,w=r?m+b:0,k=o?o.width:Qy(n.width)-C-x,S=o?o.height:Qy(n.height)-w-y,_=k+v+x+g,P=S+m+y+b,T=Ny({devicePixelContentBoxSize:Zy(Math.round(k*devicePixelRatio),Math.round(S*devicePixelRatio),i),borderBoxSize:Zy(_,P,i),contentBoxSize:Zy(k,S,i),contentRect:new Hy(d,s,k,S)});return Ky.set(e,T),T},tx=function(e,t,n){var o=ex(e,n),r=o.borderBoxSize,i=o.contentBoxSize,a=o.devicePixelContentBoxSize;switch(t){case Iy.DEVICE_PIXEL_CONTENT_BOX:return a;case Iy.BORDER_BOX:return r;default:return i}},nx=function(e){var t=ex(e);this.target=e,this.contentRect=t.contentRect,this.borderBoxSize=Ny([t.borderBoxSize]),this.contentBoxSize=Ny([t.contentBoxSize]),this.devicePixelContentBoxSize=Ny([t.devicePixelContentBoxSize])},ox=function(e){if(Uy(e))return 1/0;for(var t=0,n=e.parentNode;n;)t+=1,n=n.parentNode;return t},rx=function(){var e=1/0,t=[];By.forEach((function(n){if(0!==n.activeTargets.length){var o=[];n.activeTargets.forEach((function(t){var n=new nx(t.target),r=ox(t.target);o.push(n),t.lastReportedSize=tx(t.target,t.observedBox),re?t.activeTargets.push(n):t.skippedTargets.push(n))}))}))},ax=function(){var e,t=0;for(ix(t);By.some((function(e){return e.activeTargets.length>0}));)t=rx(),ix(t);return By.some((function(e){return e.skippedTargets.length>0}))&&("function"==typeof ErrorEvent?e=new ErrorEvent("error",{message:Dy}):((e=document.createEvent("Event")).initEvent("error",!1,!1),e.message=Dy),window.dispatchEvent(e)),t>0},lx=[],sx=function(e){if(!$y){var t=0,n=document.createTextNode("");new MutationObserver((function(){return lx.splice(0).forEach((function(e){return e()}))})).observe(n,{characterData:!0}),$y=function(){n.textContent="".concat(t?t--:t++)}}lx.push(e),$y()},cx=0,ux={attributes:!0,characterData:!0,childList:!0,subtree:!0},dx=["resize","load","transitionend","animationend","animationstart","animationiteration","keyup","keydown","mouseup","mousedown","mouseover","mouseout","blur","focus"],px=function(e){return void 0===e&&(e=0),Date.now()+e},hx=!1,fx=new(function(){function e(){var e=this;this.stopped=!0,this.listener=function(){return e.schedule()}}return e.prototype.run=function(e){var t=this;if(void 0===e&&(e=250),!hx){hx=!0;var n,o=px(e);n=function(){var n=!1;try{n=ax()}finally{if(hx=!1,e=o-px(),!cx)return;n?t.run(1e3):e>0?t.run(e):t.start()}},sx((function(){requestAnimationFrame(n)}))}},e.prototype.schedule=function(){this.stop(),this.run()},e.prototype.observe=function(){var e=this,t=function(){return e.observer&&e.observer.observe(document.body,ux)};document.body?t():qy.addEventListener("DOMContentLoaded",t)},e.prototype.start=function(){var e=this;this.stopped&&(this.stopped=!1,this.observer=new MutationObserver(this.listener),this.observe(),dx.forEach((function(t){return qy.addEventListener(t,e.listener,!0)})))},e.prototype.stop=function(){var e=this;this.stopped||(this.observer&&this.observer.disconnect(),dx.forEach((function(t){return qy.removeEventListener(t,e.listener,!0)})),this.stopped=!0)},e}()),vx=function(e){!cx&&e>0&&fx.start(),!(cx+=e)&&fx.stop()},mx=function(){function e(e,t){this.target=e,this.observedBox=t||Iy.CONTENT_BOX,this.lastReportedSize={inlineSize:0,blockSize:0}}return e.prototype.isActive=function(){var e,t=tx(this.target,this.observedBox,!0);return e=this.target,Wy(e)||function(e){switch(e.tagName){case"INPUT":if("image"!==e.type)break;case"VIDEO":case"AUDIO":case"EMBED":case"OBJECT":case"CANVAS":case"IFRAME":case"IMG":return!0}return!1}(e)||"inline"!==getComputedStyle(e).display||(this.lastReportedSize=t),this.lastReportedSize.inlineSize!==t.inlineSize||this.lastReportedSize.blockSize!==t.blockSize},e}(),gx=function(e,t){this.activeTargets=[],this.skippedTargets=[],this.observationTargets=[],this.observer=e,this.callback=t},bx=new WeakMap,yx=function(e,t){for(var n=0;n=0&&(r&&By.splice(By.indexOf(n),1),n.observationTargets.splice(o,1),vx(-1))},e.disconnect=function(e){var t=this,n=bx.get(e);n.observationTargets.slice().forEach((function(n){return t.unobserve(e,n.target)})),n.activeTargets.splice(0,n.activeTargets.length)},e}(),Cx=function(){function e(e){if(0===arguments.length)throw new TypeError("Failed to construct 'ResizeObserver': 1 argument required, but only 0 present.");if("function"!=typeof e)throw new TypeError("Failed to construct 'ResizeObserver': The callback provided as parameter 1 is not a function.");xx.connect(this,e)}return e.prototype.observe=function(e,t){if(0===arguments.length)throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': 1 argument required, but only 0 present.");if(!Vy(e))throw new TypeError("Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element");xx.observe(this,e,t)},e.prototype.unobserve=function(e){if(0===arguments.length)throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': 1 argument required, but only 0 present.");if(!Vy(e))throw new TypeError("Failed to execute 'unobserve' on 'ResizeObserver': parameter 1 is not of type 'Element");xx.unobserve(this,e)},e.prototype.disconnect=function(){xx.disconnect(this)},e.toString=function(){return"function ResizeObserver () { [polyfill code] }"},e}();const wx=new class{constructor(){this.handleResize=this.handleResize.bind(this),this.observer=new("undefined"!=typeof window&&window.ResizeObserver||Cx)(this.handleResize),this.elHandlersMap=new Map}handleResize(e){for(const t of e){const e=this.elHandlersMap.get(t.target);void 0!==e&&e(t)}}registerHandler(e,t){this.elHandlersMap.set(e,t),this.observer.observe(e)}unregisterHandler(e){this.elHandlersMap.has(e)&&(this.elHandlersMap.delete(e),this.observer.unobserve(e))}},kx=zn({name:"ResizeObserver",props:{onResize:Function},setup(e){let t=!1;const n=Gr().proxy;function o(t){const{onResize:n}=e;void 0!==n&&n(t)}$n((()=>{const e=n.$el;void 0!==e&&(e.nextElementSibling!==e.nextSibling&&3===e.nodeType&&""!==e.nodeValue||null!==e.nextElementSibling&&(wx.registerHandler(e.nextElementSibling,o),t=!0))})),Hn((()=>{t&&wx.unregisterHandler(n.$el.nextElementSibling)}))},render(){return to(this.$slots,"default")}});let Sx,_x;function Px(){return"undefined"==typeof document?1:(void 0===_x&&(_x="chrome"in window?window.devicePixelRatio:1),_x)}const Tx=yy(".v-vl",{maxHeight:"inherit",height:"100%",overflow:"auto",minWidth:"1px"},[yy("&:not(.v-vl--show-scrollbar)",{scrollbarWidth:"none"},[yy("&::-webkit-scrollbar, &::-webkit-scrollbar-track-piece, &::-webkit-scrollbar-thumb",{width:0,height:0,display:"none"})])]),Ax=zn({name:"VirtualList",inheritAttrs:!1,props:{showScrollbar:{type:Boolean,default:!0},items:{type:Array,default:()=>[]},itemSize:{type:Number,required:!0},itemResizable:Boolean,itemsStyle:[String,Object],visibleItemsTag:{type:[String,Object],default:"div"},visibleItemsProps:Object,ignoreItemResize:Boolean,onScroll:Function,onWheel:Function,onResize:Function,defaultScrollKey:[Number,String],defaultScrollIndex:Number,keyField:{type:String,default:"key"},paddingTop:{type:[Number,String],default:0},paddingBottom:{type:[Number,String],default:0}},setup(e){const t=by();Tx.mount({id:"vueuc/virtual-list",head:!0,anchorMetaName:xy,ssr:t}),$n((()=>{const{defaultScrollIndex:t,defaultScrollKey:n}=e;null!=t?f({index:t}):null!=n&&f({key:n})}));let n=!1,o=!1;On((()=>{n=!1,o?f({top:d.value,left:u}):o=!0})),Mn((()=>{n=!0,o||(o=!0)}));const r=ai((()=>{const t=new Map,{keyField:n}=e;return e.items.forEach(((e,o)=>{t.set(e[n],o)})),t})),i=Et(null),a=Et(void 0),l=new Map,s=ai((()=>{const{items:t,itemSize:n,keyField:o}=e,r=new wy(t.length,n);return t.forEach(((e,t)=>{const n=e[o],i=l.get(n);void 0!==i&&r.add(t,i)})),r})),c=Et(0);let u=0;const d=Et(0),p=mb((()=>Math.max(s.value.getBound(d.value-Im(e.paddingTop))-1,0))),h=ai((()=>{const{value:t}=a;if(void 0===t)return[];const{items:n,itemSize:o}=e,r=p.value,i=Math.min(r+Math.ceil(t/o+1),n.length-1),l=[];for(let e=r;e<=i;++e)l.push(n[e]);return l})),f=(e,t)=>{if("number"==typeof e)return void b(e,t,"auto");const{left:n,top:o,index:i,key:a,position:l,behavior:s,debounce:c=!0}=e;if(void 0!==n||void 0!==o)b(n,o,s);else if(void 0!==i)g(i,s,c);else if(void 0!==a){const e=r.value.get(a);void 0!==e&&g(e,s,c)}else"bottom"===l?b(0,Number.MAX_SAFE_INTEGER,s):"top"===l&&b(0,0,s)};let v,m=null;function g(t,n,o){const{value:r}=s,a=r.sum(t)+Im(e.paddingTop);if(o){v=t,null!==m&&window.clearTimeout(m),m=window.setTimeout((()=>{v=void 0,m=null}),16);const{scrollTop:e,offsetHeight:o}=i.value;if(a>e){const l=r.get(t);a+l<=e+o||i.value.scrollTo({left:0,top:a+l-o,behavior:n})}else i.value.scrollTo({left:0,top:a,behavior:n})}else i.value.scrollTo({left:0,top:a,behavior:n})}function b(e,t,n){i.value.scrollTo({left:e,top:t,behavior:n})}const y=!("undefined"!=typeof document&&(void 0===Sx&&(Sx="matchMedia"in window&&window.matchMedia("(pointer:coarse)").matches),Sx));let x=!1;function C(){const{value:e}=i;null!=e&&(d.value=e.scrollTop,u=e.scrollLeft)}function w(e){let t=e;for(;null!==t;){if("none"===t.style.display)return!0;t=t.parentElement}return!1}return{listHeight:a,listStyle:{overflow:"auto"},keyToIndex:r,itemsStyle:ai((()=>{const{itemResizable:t}=e,n=Lm(s.value.sum());return c.value,[e.itemsStyle,{boxSizing:"content-box",height:t?"":n,minHeight:t?n:"",paddingTop:Lm(e.paddingTop),paddingBottom:Lm(e.paddingBottom)}]})),visibleItemsStyle:ai((()=>(c.value,{transform:`translateY(${Lm(s.value.sum(p.value))})`}))),viewportItems:h,listElRef:i,itemsElRef:Et(null),scrollTo:f,handleListResize:function(t){if(n)return;if(w(t.target))return;if(t.contentRect.height===a.value)return;a.value=t.contentRect.height;const{onResize:o}=e;void 0!==o&&o(t)},handleListScroll:function(t){var n;null===(n=e.onScroll)||void 0===n||n.call(e,t),y&&x||C()},handleListWheel:function(t){var n;if(null===(n=e.onWheel)||void 0===n||n.call(e,t),y){const e=i.value;if(null!=e){if(0===t.deltaX){if(0===e.scrollTop&&t.deltaY<=0)return;if(e.scrollTop+e.offsetHeight>=e.scrollHeight&&t.deltaY>=0)return}t.preventDefault(),e.scrollTop+=t.deltaY/Px(),e.scrollLeft+=t.deltaX/Px(),C(),x=!0,Em((()=>{x=!1}))}}},handleItemResize:function(t,o){var a,u,d;if(n)return;if(e.ignoreItemResize)return;if(w(o.target))return;const{value:p}=s,h=r.value.get(t),f=p.get(h),m=null!==(d=null===(u=null===(a=o.borderBoxSize)||void 0===a?void 0:a[0])||void 0===u?void 0:u.blockSize)&&void 0!==d?d:o.contentRect.height;if(m===f)return;0==m-e.itemSize?l.delete(t):l.set(t,m-e.itemSize);const g=m-f;if(0===g)return;p.add(h,g);const b=i.value;if(null!=b){if(void 0===v){const e=p.sum(h);b.scrollTop>e&&b.scrollBy(0,g)}else(hb.scrollTop+b.offsetHeight)&&b.scrollBy(0,g);C()}c.value++}}},render(){const{itemResizable:e,keyField:t,keyToIndex:n,visibleItemsTag:o}=this;return li(kx,{onResize:this.handleListResize},{default:()=>{var r,i;return li("div",Wr(this.$attrs,{class:["v-vl",this.showScrollbar&&"v-vl--show-scrollbar"],onScroll:this.handleListScroll,onWheel:this.handleListWheel,ref:"listElRef"}),[0!==this.items.length?li("div",{ref:"itemsElRef",class:"v-vl-items",style:this.itemsStyle},[li(o,Object.assign({class:"v-vl-visible-items",style:this.visibleItemsStyle},this.visibleItemsProps),{default:()=>this.viewportItems.map((o=>{const r=o[t],i=n.get(r),a=this.$slots.default({item:o,index:i})[0];return e?li(kx,{key:r,onResize:e=>this.handleItemResize(r,e)},{default:()=>a}):(a.key=r,a)}))})]):null===(i=(r=this.$slots).empty)||void 0===i?void 0:i.call(r)])}})}}),zx="v-hidden",Rx=yy("[v-hidden]",{display:"none!important"}),Ex=zn({name:"Overflow",props:{getCounter:Function,getTail:Function,updateCounter:Function,onUpdateCount:Function,onUpdateOverflow:Function},setup(e,{slots:t}){const n=Et(null),o=Et(null);function r(r){const{value:i}=n,{getCounter:a,getTail:l}=e;let s;if(s=void 0!==a?a():o.value,!i||!s)return;s.hasAttribute(zx)&&s.removeAttribute(zx);const{children:c}=i;if(r.showAllItemsBeforeCalculate)for(const e of c)e.hasAttribute(zx)&&e.removeAttribute(zx);const u=i.offsetWidth,d=[],p=t.tail?null==l?void 0:l():null;let h=p?p.offsetWidth:0,f=!1;const v=i.children.length-(t.tail?1:0);for(let t=0;tu){const{updateCounter:n}=e;for(let o=t;o>=0;--o){const r=v-1-o;void 0!==n?n(r):s.textContent=`${r}`;const i=s.offsetWidth;if(h-=d[o],h+i<=u||0===o){f=!0,t=o-1,p&&(-1===t?(p.style.maxWidth=u-i+"px",p.style.boxSizing="border-box"):p.style.maxWidth="");const{onUpdateCount:n}=e;n&&n(r);break}}}}const{onUpdateOverflow:m}=e;f?void 0!==m&&m(!0):(void 0!==m&&m(!1),s.setAttribute(zx,""))}const i=by();return Rx.mount({id:"vueuc/overflow",head:!0,anchorMetaName:xy,ssr:i}),$n((()=>r({showAllItemsBeforeCalculate:!1}))),{selfRef:n,counterRef:o,sync:r}},render(){const{$slots:e}=this;return tn((()=>this.sync({showAllItemsBeforeCalculate:!1}))),li("div",{class:"v-overflow",ref:"selfRef"},[to(e,"default"),e.counter?e.counter():li("span",{style:{display:"inline-block"},ref:"counterRef"}),e.tail?e.tail():null])}});function Ox(e){return e instanceof HTMLElement}function Mx(e){for(let t=0;t=0;t--){const n=e.childNodes[t];if(Ox(n)&&(Ix(n)||Fx(n)))return!0}return!1}function Ix(e){if(!function(e){if(e.tabIndex>0||0===e.tabIndex&&null!==e.getAttribute("tabIndex"))return!0;if(e.getAttribute("disabled"))return!1;switch(e.nodeName){case"A":return!!e.href&&"ignore"!==e.rel;case"INPUT":return"hidden"!==e.type&&"file"!==e.type;case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}}(e))return!1;try{e.focus({preventScroll:!0})}catch(WQ){}return document.activeElement===e}let Lx=[];const Bx=zn({name:"FocusTrap",props:{disabled:Boolean,active:Boolean,autoFocus:{type:Boolean,default:!0},onEsc:Function,initialFocusTo:String,finalFocusTo:String,returnFocusOnDeactivated:{type:Boolean,default:!0}},setup(e){const t=ig(),n=Et(null),o=Et(null);let r=!1,i=!1;const a="undefined"==typeof document?null:document.activeElement;function l(){return Lx[Lx.length-1]===t}function s(t){var n;"Escape"===t.code&&l()&&(null===(n=e.onEsc)||void 0===n||n.call(e,t))}function c(e){if(!i&&l()){const t=u();if(null===t)return;if(t.contains(Fm(e)))return;p("first")}}function u(){const e=n.value;if(null===e)return null;let t=e;for(;t=t.nextSibling,!(null===t||t instanceof Element&&"DIV"===t.tagName););return t}function d(){var n;if(e.disabled)return;if(document.removeEventListener("focus",c,!0),Lx=Lx.filter((e=>e!==t)),l())return;const{finalFocusTo:o}=e;void 0!==o?null===(n=ky(o))||void 0===n||n.focus({preventScroll:!0}):e.returnFocusOnDeactivated&&a instanceof HTMLElement&&(i=!0,a.focus({preventScroll:!0}),i=!1)}function p(t){if(l()&&e.active){const e=n.value,r=o.value;if(null!==e&&null!==r){const n=u();if(null==n||n===r)return i=!0,e.focus({preventScroll:!0}),void(i=!1);i=!0;const o="first"===t?Mx(n):Fx(n);i=!1,o||(i=!0,e.focus({preventScroll:!0}),i=!1)}}}return $n((()=>{lr((()=>e.active),(n=>{n?(function(){var n;if(!e.disabled){if(Lx.push(t),e.autoFocus){const{initialFocusTo:t}=e;void 0===t?p("first"):null===(n=ky(t))||void 0===n||n.focus({preventScroll:!0})}r=!0,document.addEventListener("focus",c,!0)}}(),Pb("keydown",document,s)):(Tb("keydown",document,s),r&&d())}),{immediate:!0})})),Hn((()=>{Tb("keydown",document,s),r&&d()})),{focusableStartRef:n,focusableEndRef:o,focusableStyle:"position: absolute; height: 0; width: 0;",handleStartFocus:function(e){if(i)return;const t=u();null!==t&&(null!==e.relatedTarget&&t.contains(e.relatedTarget)?p("last"):p("first"))},handleEndFocus:function(e){i||(null!==e.relatedTarget&&e.relatedTarget===n.value?p("last"):p("first"))}}},render(){const{default:e}=this.$slots;if(void 0===e)return null;if(this.disabled)return e();const{active:t,focusableStyle:n}=this;return li(yr,null,[li("div",{"aria-hidden":"true",tabindex:t?"0":"-1",ref:"focusableStartRef",style:n,onFocus:this.handleStartFocus}),e(),li("div",{"aria-hidden":"true",style:n,ref:"focusableEndRef",tabindex:t?"0":"-1",onFocus:this.handleEndFocus})])}});function Dx(e,t){t&&($n((()=>{const{value:n}=e;n&&wx.registerHandler(n,t)})),Hn((()=>{const{value:t}=e;t&&wx.unregisterHandler(t)})))}let $x=0,Nx="",jx="",Hx="",Wx="";const Ux=Et("0px");function Vx(e){if("undefined"==typeof document)return;const t=document.documentElement;let n,o=!1;const r=()=>{t.style.marginRight=Nx,t.style.overflow=jx,t.style.overflowX=Hx,t.style.overflowY=Wx,Ux.value="0px"};$n((()=>{n=lr(e,(e=>{if(e){if(!$x){const e=window.innerWidth-t.offsetWidth;e>0&&(Nx=t.style.marginRight,t.style.marginRight=`${e}px`,Ux.value=`${e}px`),jx=t.style.overflow,Hx=t.style.overflowX,Wx=t.style.overflowY,t.style.overflow="hidden",t.style.overflowX="hidden",t.style.overflowY="hidden"}o=!0,$x++}else $x--,$x||r(),o=!1}),{immediate:!0})})),Hn((()=>{null==n||n(),o&&($x--,$x||r(),o=!1)}))}const qx=Et(!1);function Kx(){qx.value=!0}function Gx(){qx.value=!1}let Xx=0;function Yx(){return pb&&(Dn((()=>{Xx||(window.addEventListener("compositionstart",Kx),window.addEventListener("compositionend",Gx)),Xx++})),Hn((()=>{Xx<=1?(window.removeEventListener("compositionstart",Kx),window.removeEventListener("compositionend",Gx),Xx=0):Xx--}))),qx}function Qx(e){const t={isDeactivated:!1};let n=!1;return On((()=>{t.isDeactivated=!1,n?e():n=!0})),Mn((()=>{t.isDeactivated=!0,n||(n=!0)})),t}function Zx(e){return"#document"===e.nodeName}const Jx="n-form-item";function eC(e,{defaultSize:t="medium",mergedSize:n,mergedDisabled:o}={}){const r=Po(Jx,null);_o(Jx,null);const i=ai(n?()=>n(r):()=>{const{size:n}=e;if(n)return n;if(r){const{mergedSize:e}=r;if(void 0!==e.value)return e.value}return t}),a=ai(o?()=>o(r):()=>{const{disabled:t}=e;return void 0!==t?t:!!r&&r.disabled.value}),l=ai((()=>{const{status:t}=e;return t||(null==r?void 0:r.mergedValidationStatus.value)}));return Hn((()=>{r&&r.restoreValidation()})),{mergedSizeRef:i,mergedDisabledRef:a,mergedStatusRef:l,nTriggerFormBlur(){r&&r.handleContentBlur()},nTriggerFormChange(){r&&r.handleContentChange()},nTriggerFormFocus(){r&&r.handleContentFocus()},nTriggerFormInput(){r&&r.handleContentInput()}}}const tC="object"==typeof global&&global&&global.Object===Object&&global;var nC="object"==typeof self&&self&&self.Object===Object&&self;const oC=tC||nC||Function("return this")(),rC=oC.Symbol;var iC=Object.prototype,aC=iC.hasOwnProperty,lC=iC.toString,sC=rC?rC.toStringTag:void 0,cC=Object.prototype.toString,uC=rC?rC.toStringTag:void 0;function dC(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":uC&&uC in Object(e)?function(e){var t=aC.call(e,sC),n=e[sC];try{e[sC]=void 0;var o=!0}catch(WQ){}var r=lC.call(e);return o&&(t?e[sC]=n:delete e[sC]),r}(e):function(e){return cC.call(e)}(e)}function pC(e){return null!=e&&"object"==typeof e}function hC(e){return"symbol"==typeof e||pC(e)&&"[object Symbol]"==dC(e)}function fC(e,t){for(var n=-1,o=null==e?0:e.length,r=Array(o);++n0){if(++JC>=800)return arguments[0]}else JC=0;return ZC.apply(void 0,arguments)});const ow=nw;var rw=/^(?:0|[1-9]\d*)$/;function iw(e,t){var n=typeof e;return!!(t=null==t?9007199254740991:t)&&("number"==n||"symbol"!=n&&rw.test(e))&&e>-1&&e%1==0&&e-1&&e%1==0&&e<=9007199254740991}function hw(e){return null!=e&&pw(e.length)&&!EC(e)}var fw=Object.prototype;function vw(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||fw)}function mw(e){return pC(e)&&"[object Arguments]"==dC(e)}var gw=Object.prototype,bw=gw.hasOwnProperty,yw=gw.propertyIsEnumerable,xw=mw(function(){return arguments}())?mw:function(e){return pC(e)&&bw.call(e,"callee")&&!yw.call(e,"callee")};const Cw=xw;var ww="object"==typeof e&&e&&!e.nodeType&&e,kw=ww&&"object"==typeof t&&t&&!t.nodeType&&t,Sw=kw&&kw.exports===ww?oC.Buffer:void 0;const _w=(Sw?Sw.isBuffer:void 0)||function(){return!1};var Pw={};Pw["[object Float32Array]"]=Pw["[object Float64Array]"]=Pw["[object Int8Array]"]=Pw["[object Int16Array]"]=Pw["[object Int32Array]"]=Pw["[object Uint8Array]"]=Pw["[object Uint8ClampedArray]"]=Pw["[object Uint16Array]"]=Pw["[object Uint32Array]"]=!0,Pw["[object Arguments]"]=Pw["[object Array]"]=Pw["[object ArrayBuffer]"]=Pw["[object Boolean]"]=Pw["[object DataView]"]=Pw["[object Date]"]=Pw["[object Error]"]=Pw["[object Function]"]=Pw["[object Map]"]=Pw["[object Number]"]=Pw["[object Object]"]=Pw["[object RegExp]"]=Pw["[object Set]"]=Pw["[object String]"]=Pw["[object WeakMap]"]=!1;var Tw="object"==typeof e&&e&&!e.nodeType&&e,Aw=Tw&&"object"==typeof t&&t&&!t.nodeType&&t,zw=Aw&&Aw.exports===Tw&&tC.process,Rw=function(){try{var e=Aw&&Aw.require&&Aw.require("util").types;return e||zw&&zw.binding&&zw.binding("util")}catch(WQ){}}(),Ew=Rw&&Rw.isTypedArray,Ow=Ew?function(e){return function(t){return e(t)}}(Ew):function(e){return pC(e)&&pw(e.length)&&!!Pw[dC(e)]};const Mw=Ow;var Fw=Object.prototype.hasOwnProperty;function Iw(e,t){var n=vC(e),o=!n&&Cw(e),r=!n&&!o&&_w(e),i=!n&&!o&&!r&&Mw(e),a=n||o||r||i,l=a?function(e,t){for(var n=-1,o=Array(e);++n-1},Zw.prototype.set=function(e,t){var n=this.__data__,o=Yw(n,e);return o<0?(++this.size,n.push([e,t])):n[o][1]=t,this};const Jw=UC(oC,"Map");function ek(e,t){var n,o,r=e.__data__;return("string"==(o=typeof(n=t))||"number"==o||"symbol"==o||"boolean"==o?"__proto__"!==n:null===n)?r["string"==typeof t?"string":"hash"]:r.map}function tk(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=o?e:function(e,t,n){var o=-1,r=e.length;t<0&&(t=-t>r?0:r+t),(n=n>r?r:n)<0&&(n+=r),r=t>n?0:n-t>>>0,t>>>=0;for(var i=Array(r);++ol))return!1;var c=i.get(e),u=i.get(t);if(c&&u)return c==t&&u==e;var d=-1,p=!0,h=2&n?new WS:void 0;for(i.set(e,t),i.set(t,e);++d1?t[o-1]:void 0,i=o>2?t[2]:void 0;for(r=k_.length>3&&"function"==typeof r?(o--,r):void 0,i&&function(e,t,n){if(!wC(n))return!1;var o=typeof t;return!!("number"==o?hw(n)&&iw(t,n.length):"string"==o&&t in n)&&lw(n[t],e)}(t[0],t[1],i)&&(r=o<3?void 0:r,o=1),e=Object(e);++n{const e=null==i?void 0:i.value;n.mount({id:void 0===e?t:e+t,head:!0,props:{bPrefix:e?`.${e}-`:void 0},anchorMetaName:F_,ssr:a}),(null==l?void 0:l.preflightStyleDisabled)||O_.mount({id:"n-global",head:!0,anchorMetaName:F_,ssr:a})};a?e():Dn(e)}const s=ai((()=>{var t;const{theme:{common:n,self:i,peers:a={}}={},themeOverrides:s={},builtinThemeOverrides:c={}}=r,{common:u,peers:d}=s,{common:p,[e]:{common:h,self:f,peers:v={}}={}}=(null==l?void 0:l.mergedThemeRef.value)||{},{common:m,[e]:g={}}=(null==l?void 0:l.mergedThemeOverridesRef.value)||{},{common:b,peers:y={}}=g,x=__({},n||h||p||o.common,m,b,u);return{common:x,self:__(null===(t=i||f||o.self)||void 0===t?void 0:t(x),c,g,s),peers:__({},o.peers,v,a),peerOverrides:__({},c.peers,y,d)}}));return s}I_.props={theme:Object,themeOverrides:Object,builtinThemeOverrides:Object};const L_="n";function B_(e={},t={defaultBordered:!0}){const n=Po(M_,null);return{inlineThemeDisabled:null==n?void 0:n.inlineThemeDisabled,mergedRtlRef:null==n?void 0:n.mergedRtlRef,mergedComponentPropsRef:null==n?void 0:n.mergedComponentPropsRef,mergedBreakpointsRef:null==n?void 0:n.mergedBreakpointsRef,mergedBorderedRef:ai((()=>{var o,r;const{bordered:i}=e;return void 0!==i?i:null===(r=null!==(o=null==n?void 0:n.mergedBorderedRef.value)&&void 0!==o?o:t.defaultBordered)||void 0===r||r})),mergedClsPrefixRef:n?n.mergedClsPrefixRef:Ot(L_),namespaceRef:ai((()=>null==n?void 0:n.mergedNamespaceRef.value))}}function D_(){const e=Po(M_,null);return e?e.mergedClsPrefixRef:Ot(L_)}const $_={name:"zh-CN",global:{undo:"撤销",redo:"重做",confirm:"确认",clear:"清除"},Popconfirm:{positiveText:"确认",negativeText:"取消"},Cascader:{placeholder:"请选择",loading:"加载中",loadingRequiredMessage:e=>`加载全部 ${e} 的子节点后才可选中`},Time:{dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss"},DatePicker:{yearFormat:"yyyy年",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"yyyy-MM",dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss",quarterFormat:"yyyy-qqq",weekFormat:"RRRR-w周",clear:"清除",now:"此刻",confirm:"确认",selectTime:"选择时间",selectDate:"选择日期",datePlaceholder:"选择日期",datetimePlaceholder:"选择日期时间",monthPlaceholder:"选择月份",yearPlaceholder:"选择年份",quarterPlaceholder:"选择季度",weekPlaceholder:"选择周",startDatePlaceholder:"开始日期",endDatePlaceholder:"结束日期",startDatetimePlaceholder:"开始日期时间",endDatetimePlaceholder:"结束日期时间",startMonthPlaceholder:"开始月份",endMonthPlaceholder:"结束月份",monthBeforeYear:!1,firstDayOfWeek:0,today:"今天"},DataTable:{checkTableAll:"选择全部表格数据",uncheckTableAll:"取消选择全部表格数据",confirm:"确认",clear:"重置"},LegacyTransfer:{sourceTitle:"源项",targetTitle:"目标项"},Transfer:{selectAll:"全选",clearAll:"清除",unselectAll:"取消全选",total:e=>`共 ${e} 项`,selected:e=>`已选 ${e} 项`},Empty:{description:"无数据"},Select:{placeholder:"请选择"},TimePicker:{placeholder:"请选择时间",positiveText:"确认",negativeText:"取消",now:"此刻",clear:"清除"},Pagination:{goto:"跳至",selectionSuffix:"页"},DynamicTags:{add:"添加"},Log:{loading:"加载中"},Input:{placeholder:"请输入"},InputNumber:{placeholder:"请输入"},DynamicInput:{create:"添加"},ThemeEditor:{title:"主题编辑器",clearAllVars:"清除全部变量",clearSearch:"清除搜索",filterCompName:"过滤组件名",filterVarName:"过滤变量名",import:"导入",export:"导出",restore:"恢复默认"},Image:{tipPrevious:"上一张(←)",tipNext:"下一张(→)",tipCounterclockwise:"向左旋转",tipClockwise:"向右旋转",tipZoomOut:"缩小",tipZoomIn:"放大",tipDownload:"下载",tipClose:"关闭(Esc)",tipOriginalSize:"缩放到原始尺寸"}},N_={name:"zh-TW",global:{undo:"復原",redo:"重做",confirm:"確定",clear:"清除"},Popconfirm:{positiveText:"確定",negativeText:"取消"},Cascader:{placeholder:"請選擇",loading:"載入中",loadingRequiredMessage:e=>`載入全部 ${e} 的子節點後才可選擇`},Time:{dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss"},DatePicker:{yearFormat:"yyyy 年",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"yyyy-MM",dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss",quarterFormat:"yyyy-qqq",weekFormat:"RRRR-w",clear:"清除",now:"現在",confirm:"確定",selectTime:"選擇時間",selectDate:"選擇日期",datePlaceholder:"選擇日期",datetimePlaceholder:"選擇日期時間",monthPlaceholder:"選擇月份",yearPlaceholder:"選擇年份",quarterPlaceholder:"選擇季度",weekPlaceholder:"Select Week",startDatePlaceholder:"開始日期",endDatePlaceholder:"結束日期",startDatetimePlaceholder:"開始日期時間",endDatetimePlaceholder:"結束日期時間",startMonthPlaceholder:"開始月份",endMonthPlaceholder:"結束月份",monthBeforeYear:!1,firstDayOfWeek:0,today:"今天"},DataTable:{checkTableAll:"選擇全部表格資料",uncheckTableAll:"取消選擇全部表格資料",confirm:"確定",clear:"重設"},LegacyTransfer:{sourceTitle:"來源",targetTitle:"目標"},Transfer:{selectAll:"全選",unselectAll:"取消全選",clearAll:"清除全部",total:e=>`共 ${e} 項`,selected:e=>`已選 ${e} 項`},Empty:{description:"無資料"},Select:{placeholder:"請選擇"},TimePicker:{placeholder:"請選擇時間",positiveText:"確定",negativeText:"取消",now:"現在",clear:"清除"},Pagination:{goto:"跳至",selectionSuffix:"頁"},DynamicTags:{add:"新增"},Log:{loading:"載入中"},Input:{placeholder:"請輸入"},InputNumber:{placeholder:"請輸入"},DynamicInput:{create:"新增"},ThemeEditor:{title:"主題編輯器",clearAllVars:"清除全部變數",clearSearch:"清除搜尋",filterCompName:"過濾組件名稱",filterVarName:"過濾變數名稱",import:"匯入",export:"匯出",restore:"恢復預設"},Image:{tipPrevious:"上一張(←)",tipNext:"下一張(→)",tipCounterclockwise:"向左旋轉",tipClockwise:"向右旋轉",tipZoomOut:"縮小",tipZoomIn:"放大",tipDownload:"下載",tipClose:"關閉(Esc)",tipOriginalSize:"縮放到原始尺寸"}},j_={name:"en-US",global:{undo:"Undo",redo:"Redo",confirm:"Confirm",clear:"Clear"},Popconfirm:{positiveText:"Confirm",negativeText:"Cancel"},Cascader:{placeholder:"Please Select",loading:"Loading",loadingRequiredMessage:e=>`Please load all ${e}'s descendants before checking it.`},Time:{dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss"},DatePicker:{yearFormat:"yyyy",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"yyyy-MM",dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss",quarterFormat:"yyyy-qqq",weekFormat:"RRRR-w",clear:"Clear",now:"Now",confirm:"Confirm",selectTime:"Select Time",selectDate:"Select Date",datePlaceholder:"Select Date",datetimePlaceholder:"Select Date and Time",monthPlaceholder:"Select Month",yearPlaceholder:"Select Year",quarterPlaceholder:"Select Quarter",weekPlaceholder:"Select Week",startDatePlaceholder:"Start Date",endDatePlaceholder:"End Date",startDatetimePlaceholder:"Start Date and Time",endDatetimePlaceholder:"End Date and Time",startMonthPlaceholder:"Start Month",endMonthPlaceholder:"End Month",monthBeforeYear:!0,firstDayOfWeek:6,today:"Today"},DataTable:{checkTableAll:"Select all in the table",uncheckTableAll:"Unselect all in the table",confirm:"Confirm",clear:"Clear"},LegacyTransfer:{sourceTitle:"Source",targetTitle:"Target"},Transfer:{selectAll:"Select all",unselectAll:"Unselect all",clearAll:"Clear",total:e=>`Total ${e} items`,selected:e=>`${e} items selected`},Empty:{description:"No Data"},Select:{placeholder:"Please Select"},TimePicker:{placeholder:"Select Time",positiveText:"OK",negativeText:"Cancel",now:"Now",clear:"Clear"},Pagination:{goto:"Goto",selectionSuffix:"page"},DynamicTags:{add:"Add"},Log:{loading:"Loading"},Input:{placeholder:"Please Input"},InputNumber:{placeholder:"Please Input"},DynamicInput:{create:"Create"},ThemeEditor:{title:"Theme Editor",clearAllVars:"Clear All Variables",clearSearch:"Clear Search",filterCompName:"Filter Component Name",filterVarName:"Filter Variable Name",import:"Import",export:"Export",restore:"Reset to Default"},Image:{tipPrevious:"Previous picture (←)",tipNext:"Next picture (→)",tipCounterclockwise:"Counterclockwise",tipClockwise:"Clockwise",tipZoomOut:"Zoom out",tipZoomIn:"Zoom in",tipDownload:"Download",tipClose:"Close (Esc)",tipOriginalSize:"Zoom to original size"}},H_={name:"ru-RU",global:{undo:"Отменить",redo:"Вернуть",confirm:"Подтвердить",clear:"Очистить"},Popconfirm:{positiveText:"Подтвердить",negativeText:"Отмена"},Cascader:{placeholder:"Выбрать",loading:"Загрузка",loadingRequiredMessage:e=>`Загрузите все дочерние узлы ${e} прежде чем они станут необязательными`},Time:{dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss"},DatePicker:{yearFormat:"yyyy",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"yyyy-MM",dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss",quarterFormat:"yyyy-qqq",weekFormat:"RRRR-w",clear:"Очистить",now:"Сейчас",confirm:"Подтвердить",selectTime:"Выбрать время",selectDate:"Выбрать дату",datePlaceholder:"Выбрать дату",datetimePlaceholder:"Выбрать дату и время",monthPlaceholder:"Выберите месяц",yearPlaceholder:"Выберите год",quarterPlaceholder:"Выберите квартал",weekPlaceholder:"Select Week",startDatePlaceholder:"Дата начала",endDatePlaceholder:"Дата окончания",startDatetimePlaceholder:"Дата и время начала",endDatetimePlaceholder:"Дата и время окончания",startMonthPlaceholder:"Начало месяца",endMonthPlaceholder:"Конец месяца",monthBeforeYear:!0,firstDayOfWeek:0,today:"Сегодня"},DataTable:{checkTableAll:"Выбрать все в таблице",uncheckTableAll:"Отменить все в таблице",confirm:"Подтвердить",clear:"Очистить"},LegacyTransfer:{sourceTitle:"Источник",targetTitle:"Назначение"},Transfer:{selectAll:"Выбрать все",unselectAll:"Снять все",clearAll:"Очистить",total:e=>`Всего ${e} элементов`,selected:e=>`${e} выбрано элементов`},Empty:{description:"Нет данных"},Select:{placeholder:"Выбрать"},TimePicker:{placeholder:"Выбрать время",positiveText:"OK",negativeText:"Отменить",now:"Сейчас",clear:"Очистить"},Pagination:{goto:"Перейти",selectionSuffix:"страница"},DynamicTags:{add:"Добавить"},Log:{loading:"Загрузка"},Input:{placeholder:"Ввести"},InputNumber:{placeholder:"Ввести"},DynamicInput:{create:"Создать"},ThemeEditor:{title:"Редактор темы",clearAllVars:"Очистить все",clearSearch:"Очистить поиск",filterCompName:"Фильтровать по имени компонента",filterVarName:"Фильтровать имена переменных",import:"Импорт",export:"Экспорт",restore:"Сбросить"},Image:{tipPrevious:"Предыдущее изображение (←)",tipNext:"Следующее изображение (→)",tipCounterclockwise:"Против часовой стрелки",tipClockwise:"По часовой стрелке",tipZoomOut:"Отдалить",tipZoomIn:"Приблизить",tipDownload:"Скачать",tipClose:"Закрыть (Esc)",tipOriginalSize:"Вернуть исходный размер"}},W_={name:"ja-JP",global:{undo:"元に戻す",redo:"やり直す",confirm:"OK",clear:"クリア"},Popconfirm:{positiveText:"OK",negativeText:"キャンセル"},Cascader:{placeholder:"選択してください",loading:"ロード中",loadingRequiredMessage:e=>`すべての ${e} サブノードをロードしてから選択できます。`},Time:{dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss"},DatePicker:{yearFormat:"yyyy年",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"yyyy-MM",dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss",quarterFormat:"yyyy-qqq",weekFormat:"RRRR-w",clear:"クリア",now:"現在",confirm:"OK",selectTime:"時間を選択",selectDate:"日付を選択",datePlaceholder:"日付を選択",datetimePlaceholder:"選択",monthPlaceholder:"月を選択",yearPlaceholder:"年を選択",quarterPlaceholder:"四半期を選択",weekPlaceholder:"Select Week",startDatePlaceholder:"開始日",endDatePlaceholder:"終了日",startDatetimePlaceholder:"開始時間",endDatetimePlaceholder:"終了時間",startMonthPlaceholder:"開始月",endMonthPlaceholder:"終了月",monthBeforeYear:!1,firstDayOfWeek:0,today:"今日"},DataTable:{checkTableAll:"全選択",uncheckTableAll:"全選択取消",confirm:"OK",clear:"リセット"},LegacyTransfer:{sourceTitle:"元",targetTitle:"先"},Transfer:{selectAll:"全選択",unselectAll:"全選択取消",clearAll:"リセット",total:e=>`合計 ${e} 項目`,selected:e=>`${e} 個の項目を選択`},Empty:{description:"データなし"},Select:{placeholder:"選択してください"},TimePicker:{placeholder:"選択してください",positiveText:"OK",negativeText:"キャンセル",now:"現在",clear:"クリア"},Pagination:{goto:"ページジャンプ",selectionSuffix:"ページ"},DynamicTags:{add:"追加"},Log:{loading:"ロード中"},Input:{placeholder:"入力してください"},InputNumber:{placeholder:"入力してください"},DynamicInput:{create:"追加"},ThemeEditor:{title:"テーマエディタ",clearAllVars:"全件変数クリア",clearSearch:"検索クリア",filterCompName:"コンポネント名をフィルタ",filterVarName:"変数をフィルタ",import:"インポート",export:"エクスポート",restore:"デフォルト"},Image:{tipPrevious:"前の画像 (←)",tipNext:"次の画像 (→)",tipCounterclockwise:"左に回転",tipClockwise:"右に回転",tipZoomOut:"縮小",tipZoomIn:"拡大",tipDownload:"ダウンロード",tipClose:"閉じる (Esc)",tipOriginalSize:"元のサイズに戻す"}},U_={name:"ko-KR",global:{undo:"실행 취소",redo:"다시 실행",confirm:"확인",clear:"지우기"},Popconfirm:{positiveText:"확인",negativeText:"취소"},Cascader:{placeholder:"선택해 주세요",loading:"불러오는 중",loadingRequiredMessage:e=>`${e}의 모든 하위 항목을 불러온 뒤에 선택할 수 있습니다.`},Time:{dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss"},DatePicker:{yearFormat:"yyyy년",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"yyyy-MM",dateFormat:"yyyy-MM-dd",dateTimeFormat:"yyyy-MM-dd HH:mm:ss",quarterFormat:"yyyy-qqq",weekFormat:"RRRR-w",clear:"지우기",now:"현재",confirm:"확인",selectTime:"시간 선택",selectDate:"날짜 선택",datePlaceholder:"날짜 선택",datetimePlaceholder:"날짜 및 시간 선택",monthPlaceholder:"월 선택",yearPlaceholder:"년 선택",quarterPlaceholder:"분기 선택",weekPlaceholder:"Select Week",startDatePlaceholder:"시작 날짜",endDatePlaceholder:"종료 날짜",startDatetimePlaceholder:"시작 날짜 및 시간",endDatetimePlaceholder:"종료 날짜 및 시간",startMonthPlaceholder:"시작 월",endMonthPlaceholder:"종료 월",monthBeforeYear:!1,firstDayOfWeek:6,today:"오늘"},DataTable:{checkTableAll:"모두 선택",uncheckTableAll:"모두 선택 해제",confirm:"확인",clear:"지우기"},LegacyTransfer:{sourceTitle:"원본",targetTitle:"타깃"},Transfer:{selectAll:"전체 선택",unselectAll:"전체 해제",clearAll:"전체 삭제",total:e=>`총 ${e} 개`,selected:e=>`${e} 개 선택`},Empty:{description:"데이터 없음"},Select:{placeholder:"선택해 주세요"},TimePicker:{placeholder:"시간 선택",positiveText:"확인",negativeText:"취소",now:"현재 시간",clear:"지우기"},Pagination:{goto:"이동",selectionSuffix:"페이지"},DynamicTags:{add:"추가"},Log:{loading:"불러오는 중"},Input:{placeholder:"입력해 주세요"},InputNumber:{placeholder:"입력해 주세요"},DynamicInput:{create:"추가"},ThemeEditor:{title:"테마 편집기",clearAllVars:"모든 변수 지우기",clearSearch:"검색 지우기",filterCompName:"구성 요소 이름 필터",filterVarName:"변수 이름 필터",import:"가져오기",export:"내보내기",restore:"기본으로 재설정"},Image:{tipPrevious:"이전 (←)",tipNext:"다음 (→)",tipCounterclockwise:"시계 반대 방향으로 회전",tipClockwise:"시계 방향으로 회전",tipZoomOut:"축소",tipZoomIn:"확대",tipDownload:"다운로드",tipClose:"닫기 (Esc)",tipOriginalSize:"원본 크기로 확대"}},V_={name:"vi-VN",global:{undo:"Hoàn tác",redo:"Làm lại",confirm:"Xác nhận",clear:"xóa"},Popconfirm:{positiveText:"Xác nhận",negativeText:"Hủy"},Cascader:{placeholder:"Vui lòng chọn",loading:"Đang tải",loadingRequiredMessage:e=>`Vui lòng tải tất cả thông tin con của ${e} trước.`},Time:{dateFormat:"",dateTimeFormat:"HH:mm:ss dd-MM-yyyy"},DatePicker:{yearFormat:"yyyy",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"MM-yyyy",dateFormat:"dd-MM-yyyy",dateTimeFormat:"HH:mm:ss dd-MM-yyyy",quarterFormat:"qqq-yyyy",weekFormat:"RRRR-w",clear:"Xóa",now:"Hôm nay",confirm:"Xác nhận",selectTime:"Chọn giờ",selectDate:"Chọn ngày",datePlaceholder:"Chọn ngày",datetimePlaceholder:"Chọn ngày giờ",monthPlaceholder:"Chọn tháng",yearPlaceholder:"Chọn năm",quarterPlaceholder:"Chọn quý",weekPlaceholder:"Select Week",startDatePlaceholder:"Ngày bắt đầu",endDatePlaceholder:"Ngày kết thúc",startDatetimePlaceholder:"Thời gian bắt đầu",endDatetimePlaceholder:"Thời gian kết thúc",startMonthPlaceholder:"Tháng bắt đầu",endMonthPlaceholder:"Tháng kết thúc",monthBeforeYear:!0,firstDayOfWeek:0,today:"Hôm nay"},DataTable:{checkTableAll:"Chọn tất cả có trong bảng",uncheckTableAll:"Bỏ chọn tất cả có trong bảng",confirm:"Xác nhận",clear:"Xóa"},LegacyTransfer:{sourceTitle:"Nguồn",targetTitle:"Đích"},Transfer:{selectAll:"Chọn tất cả",unselectAll:"Bỏ chọn tất cả",clearAll:"Xoá tất cả",total:e=>`Tổng cộng ${e} mục`,selected:e=>`${e} mục được chọn`},Empty:{description:"Không có dữ liệu"},Select:{placeholder:"Vui lòng chọn"},TimePicker:{placeholder:"Chọn thời gian",positiveText:"OK",negativeText:"Hủy",now:"Hiện tại",clear:"Xóa"},Pagination:{goto:"Đi đến trang",selectionSuffix:"trang"},DynamicTags:{add:"Thêm"},Log:{loading:"Đang tải"},Input:{placeholder:"Vui lòng nhập"},InputNumber:{placeholder:"Vui lòng nhập"},DynamicInput:{create:"Tạo"},ThemeEditor:{title:"Tùy chỉnh giao diện",clearAllVars:"Xóa tất cả các biến",clearSearch:"Xóa tìm kiếm",filterCompName:"Lọc tên component",filterVarName:"Lọc tên biến",import:"Nhập",export:"Xuất",restore:"Đặt lại mặc định"},Image:{tipPrevious:"Hình trước (←)",tipNext:"Hình tiếp (→)",tipCounterclockwise:"Counterclockwise",tipClockwise:"Chiều kim đồng hồ",tipZoomOut:"Thu nhỏ",tipZoomIn:"Phóng to",tipDownload:"Tải về",tipClose:"Đóng (Esc)",tipOriginalSize:"Xem kích thước gốc"}},q_={name:"fa-IR",global:{undo:"لغو انجام شده",redo:"انجام دوباره",confirm:"تأیید",clear:"پاک کردن"},Popconfirm:{positiveText:"تأیید",negativeText:"لغو"},Cascader:{placeholder:"لطفا انتخاب کنید",loading:"بارگذاری",loadingRequiredMessage:e=>`پس از بارگیری کامل زیرمجموعه های ${e} می توانید انتخاب کنید `},Time:{dateFormat:"yyyy/MM/dd",dateTimeFormat:"yyyy/MM/dd، H:mm:ss"},DatePicker:{yearFormat:"yyyy سال",monthFormat:"MMM",dayFormat:"eeeeee",yearTypeFormat:"yyyy",monthTypeFormat:"MM/yyyy",dateFormat:"yyyy/MM/dd",dateTimeFormat:"yyyy/MM/dd HH:mm:ss",quarterFormat:"سه ماهه yyyy",weekFormat:"RRRR-w",clear:"پاک کردن",now:"اکنون",confirm:"تأیید",selectTime:"انتخاب زمان",selectDate:"انتخاب تاریخ",datePlaceholder:"انتخاب تاریخ",datetimePlaceholder:"انتخاب تاریخ و زمان",monthPlaceholder:"انتخاب ماه",yearPlaceholder:"انتخاب سال",quarterPlaceholder:"انتخاب سه‌ماهه",weekPlaceholder:"Select Week",startDatePlaceholder:"تاریخ شروع",endDatePlaceholder:"تاریخ پایان",startDatetimePlaceholder:"زمان شروع",endDatetimePlaceholder:"زمان پایان",startMonthPlaceholder:"ماه شروع",endMonthPlaceholder:"ماه پایان",monthBeforeYear:!1,firstDayOfWeek:6,today:"امروز"},DataTable:{checkTableAll:"انتخاب همه داده‌های جدول",uncheckTableAll:"عدم انتخاب همه داده‌های جدول",confirm:"تأیید",clear:"تنظیم مجدد"},LegacyTransfer:{sourceTitle:"آیتم منبع",targetTitle:"آیتم مقصد"},Transfer:{selectAll:"انتخاب همه",clearAll:"حذف همه",unselectAll:"عدم انتخاب همه",total:e=>`کل ${e} مورد`,selected:e=>`انتخاب شده ${e} مورد`},Empty:{description:"اطلاعاتی وجود ندارد"},Select:{placeholder:"لطفاً انتخاب کنید"},TimePicker:{placeholder:"لطفاً زمان مورد نظر را انتخاب کنید",positiveText:"تأیید",negativeText:"لغو",now:"همین الان",clear:"پاک کردن"},Pagination:{goto:"رفتن به صفحه",selectionSuffix:"صفحه"},DynamicTags:{add:"افزودن"},Log:{loading:"در حال بارگذاری"},Input:{placeholder:"لطفاً وارد کنید"},InputNumber:{placeholder:"لطفاً وارد کنید"},DynamicInput:{create:"افزودن"},ThemeEditor:{title:"ویرایشگر پوسته",clearAllVars:"پاک کردن همه متغیرها",clearSearch:"پاک کردن جستجو",filterCompName:"فیلتر نام کامپوننت",filterVarName:"فیلتر نام متغیر",import:"ورود",export:"خروج",restore:"بازگردانی به حالت پیش‌فرض"},Image:{tipPrevious:"تصویر قبلی (←)",tipNext:"تصویر بعدی (→)",tipCounterclockwise:"چرخش به سمت چپ",tipClockwise:"چرخش به سمت راست",tipZoomOut:"کوچک نمایی تصویر",tipZoomIn:"بزرگ نمایی تصویر",tipDownload:"بارگیری",tipClose:"بستن (Esc)",tipOriginalSize:"اندازه اصلی تصویر"}};var K_={lessThanXSeconds:{one:"不到 1 秒",other:"不到 {{count}} 秒"},xSeconds:{one:"1 秒",other:"{{count}} 秒"},halfAMinute:"半分钟",lessThanXMinutes:{one:"不到 1 分钟",other:"不到 {{count}} 分钟"},xMinutes:{one:"1 分钟",other:"{{count}} 分钟"},xHours:{one:"1 小时",other:"{{count}} 小时"},aboutXHours:{one:"大约 1 小时",other:"大约 {{count}} 小时"},xDays:{one:"1 天",other:"{{count}} 天"},aboutXWeeks:{one:"大约 1 个星期",other:"大约 {{count}} 个星期"},xWeeks:{one:"1 个星期",other:"{{count}} 个星期"},aboutXMonths:{one:"大约 1 个月",other:"大约 {{count}} 个月"},xMonths:{one:"1 个月",other:"{{count}} 个月"},aboutXYears:{one:"大约 1 年",other:"大约 {{count}} 年"},xYears:{one:"1 年",other:"{{count}} 年"},overXYears:{one:"超过 1 年",other:"超过 {{count}} 年"},almostXYears:{one:"将近 1 年",other:"将近 {{count}} 年"}};const G_=function(e,t,n){var o,r=K_[e];return o="string"==typeof r?r:1===t?r.one:r.other.replace("{{count}}",String(t)),null!=n&&n.addSuffix?n.comparison&&n.comparison>0?o+"内":o+"前":o};function X_(e){return function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.width?String(t.width):e.defaultWidth;return e.formats[n]||e.formats[e.defaultWidth]}}var Y_={date:X_({formats:{full:"y'年'M'月'd'日' EEEE",long:"y'年'M'月'd'日'",medium:"yyyy-MM-dd",short:"yy-MM-dd"},defaultWidth:"full"}),time:X_({formats:{full:"zzzz a h:mm:ss",long:"z a h:mm:ss",medium:"a h:mm:ss",short:"a h:mm"},defaultWidth:"full"}),dateTime:X_({formats:{full:"{{date}} {{time}}",long:"{{date}} {{time}}",medium:"{{date}} {{time}}",short:"{{date}} {{time}}"},defaultWidth:"full"})};function Q_(e,t){if(t.length1?"s":"")+" required, but only "+t.length+" present")}function Z_(e){return(Z_="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var J_={};function eP(e,t){var n,o,r,i,a,l,s,c;Q_(1,arguments);var u=J_,d=function(e){if(null===e||!0===e||!1===e)return NaN;var t=Number(e);return isNaN(t)?t:t<0?Math.ceil(t):Math.floor(t)}(null!==(n=null!==(o=null!==(r=null!==(i=null==t?void 0:t.weekStartsOn)&&void 0!==i?i:null==t||null===(a=t.locale)||void 0===a||null===(l=a.options)||void 0===l?void 0:l.weekStartsOn)&&void 0!==r?r:u.weekStartsOn)&&void 0!==o?o:null===(s=u.locale)||void 0===s||null===(c=s.options)||void 0===c?void 0:c.weekStartsOn)&&void 0!==n?n:0);if(!(d>=0&&d<=6))throw new RangeError("weekStartsOn must be between 0 and 6 inclusively");var p=function(e){Q_(1,arguments);var t=Object.prototype.toString.call(e);return e instanceof Date||"object"===Z_(e)&&"[object Date]"===t?new Date(e.getTime()):"number"==typeof e||"[object Number]"===t?new Date(e):new Date(NaN)}(e),h=p.getUTCDay(),f=(ht.getTime()?"'下个'"+o:"'上个'"+o}var oP={lastWeek:nP,yesterday:"'昨天' p",today:"'今天' p",tomorrow:"'明天' p",nextWeek:nP,other:"PP p"};const rP=function(e,t,n,o){var r=oP[e];return"function"==typeof r?r(t,n,o):r};function iP(e){return function(t,n){var o;if("formatting"===(null!=n&&n.context?String(n.context):"standalone")&&e.formattingValues){var r=e.defaultFormattingWidth||e.defaultWidth,i=null!=n&&n.width?String(n.width):r;o=e.formattingValues[i]||e.formattingValues[r]}else{var a=e.defaultWidth,l=null!=n&&n.width?String(n.width):e.defaultWidth;o=e.values[l]||e.values[a]}return o[e.argumentCallback?e.argumentCallback(t):t]}}function aP(e){return function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=n.width,r=o&&e.matchPatterns[o]||e.matchPatterns[e.defaultMatchWidth],i=t.match(r);if(!i)return null;var a,l=i[0],s=o&&e.parsePatterns[o]||e.parsePatterns[e.defaultParseWidth],c=Array.isArray(s)?function(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},o=t.match(e.matchPattern);if(!o)return null;var r=o[0],i=t.match(e.parsePattern);if(!i)return null;var a=e.valueCallback?e.valueCallback(i[0]):i[0];return{value:a=n.valueCallback?n.valueCallback(a):a,rest:t.slice(r.length)}}}const sP={code:"zh-CN",formatDistance:G_,formatLong:Y_,formatRelative:rP,localize:{ordinalNumber:function(e,t){var n=Number(e);switch(null==t?void 0:t.unit){case"date":return n.toString()+"日";case"hour":return n.toString()+"时";case"minute":return n.toString()+"分";case"second":return n.toString()+"秒";default:return"第 "+n.toString()}},era:iP({values:{narrow:["前","公元"],abbreviated:["前","公元"],wide:["公元前","公元"]},defaultWidth:"wide"}),quarter:iP({values:{narrow:["1","2","3","4"],abbreviated:["第一季","第二季","第三季","第四季"],wide:["第一季度","第二季度","第三季度","第四季度"]},defaultWidth:"wide",argumentCallback:function(e){return e-1}}),month:iP({values:{narrow:["一","二","三","四","五","六","七","八","九","十","十一","十二"],abbreviated:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],wide:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"]},defaultWidth:"wide"}),day:iP({values:{narrow:["日","一","二","三","四","五","六"],short:["日","一","二","三","四","五","六"],abbreviated:["周日","周一","周二","周三","周四","周五","周六"],wide:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]},defaultWidth:"wide"}),dayPeriod:iP({values:{narrow:{am:"上",pm:"下",midnight:"凌晨",noon:"午",morning:"早",afternoon:"下午",evening:"晚",night:"夜"},abbreviated:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"},wide:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"}},defaultWidth:"wide",formattingValues:{narrow:{am:"上",pm:"下",midnight:"凌晨",noon:"午",morning:"早",afternoon:"下午",evening:"晚",night:"夜"},abbreviated:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"},wide:{am:"上午",pm:"下午",midnight:"凌晨",noon:"中午",morning:"早晨",afternoon:"中午",evening:"晚上",night:"夜间"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:lP({matchPattern:/^(第\s*)?\d+(日|时|分|秒)?/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}}),era:aP({matchPatterns:{narrow:/^(前)/i,abbreviated:/^(前)/i,wide:/^(公元前|公元)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^(前)/i,/^(公元)/i]},defaultParseWidth:"any"}),quarter:aP({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^第[一二三四]刻/i,wide:/^第[一二三四]刻钟/i},defaultMatchWidth:"wide",parsePatterns:{any:[/(1|一)/i,/(2|二)/i,/(3|三)/i,/(4|四)/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:aP({matchPatterns:{narrow:/^(一|二|三|四|五|六|七|八|九|十[二一])/i,abbreviated:/^(一|二|三|四|五|六|七|八|九|十[二一]|\d|1[12])月/i,wide:/^(一|二|三|四|五|六|七|八|九|十[二一])月/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^一/i,/^二/i,/^三/i,/^四/i,/^五/i,/^六/i,/^七/i,/^八/i,/^九/i,/^十(?!(一|二))/i,/^十一/i,/^十二/i],any:[/^一|1/i,/^二|2/i,/^三|3/i,/^四|4/i,/^五|5/i,/^六|6/i,/^七|7/i,/^八|8/i,/^九|9/i,/^十(?!(一|二))|10/i,/^十一|11/i,/^十二|12/i]},defaultParseWidth:"any"}),day:aP({matchPatterns:{narrow:/^[一二三四五六日]/i,short:/^[一二三四五六日]/i,abbreviated:/^周[一二三四五六日]/i,wide:/^星期[一二三四五六日]/i},defaultMatchWidth:"wide",parsePatterns:{any:[/日/i,/一/i,/二/i,/三/i,/四/i,/五/i,/六/i]},defaultParseWidth:"any"}),dayPeriod:aP({matchPatterns:{any:/^(上午?|下午?|午夜|[中正]午|早上?|下午|晚上?|凌晨|)/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^上午?/i,pm:/^下午?/i,midnight:/^午夜/i,noon:/^[中正]午/i,morning:/^早上/i,afternoon:/^下午/i,evening:/^晚上?/i,night:/^凌晨/i}},defaultParseWidth:"any"})},options:{weekStartsOn:1,firstWeekContainsDate:4}},cP={name:"zh-CN",locale:sP};var uP={lessThanXSeconds:{one:"less than a second",other:"less than {{count}} seconds"},xSeconds:{one:"1 second",other:"{{count}} seconds"},halfAMinute:"half a minute",lessThanXMinutes:{one:"less than a minute",other:"less than {{count}} minutes"},xMinutes:{one:"1 minute",other:"{{count}} minutes"},aboutXHours:{one:"about 1 hour",other:"about {{count}} hours"},xHours:{one:"1 hour",other:"{{count}} hours"},xDays:{one:"1 day",other:"{{count}} days"},aboutXWeeks:{one:"about 1 week",other:"about {{count}} weeks"},xWeeks:{one:"1 week",other:"{{count}} weeks"},aboutXMonths:{one:"about 1 month",other:"about {{count}} months"},xMonths:{one:"1 month",other:"{{count}} months"},aboutXYears:{one:"about 1 year",other:"about {{count}} years"},xYears:{one:"1 year",other:"{{count}} years"},overXYears:{one:"over 1 year",other:"over {{count}} years"},almostXYears:{one:"almost 1 year",other:"almost {{count}} years"}};const dP=function(e,t,n){var o,r=uP[e];return o="string"==typeof r?r:1===t?r.one:r.other.replace("{{count}}",t.toString()),null!=n&&n.addSuffix?n.comparison&&n.comparison>0?"in "+o:o+" ago":o};var pP={date:X_({formats:{full:"EEEE, MMMM do, y",long:"MMMM do, y",medium:"MMM d, y",short:"MM/dd/yyyy"},defaultWidth:"full"}),time:X_({formats:{full:"h:mm:ss a zzzz",long:"h:mm:ss a z",medium:"h:mm:ss a",short:"h:mm a"},defaultWidth:"full"}),dateTime:X_({formats:{full:"{{date}} 'at' {{time}}",long:"{{date}} 'at' {{time}}",medium:"{{date}}, {{time}}",short:"{{date}}, {{time}}"},defaultWidth:"full"})},hP={lastWeek:"'last' eeee 'at' p",yesterday:"'yesterday at' p",today:"'today at' p",tomorrow:"'tomorrow at' p",nextWeek:"eeee 'at' p",other:"P"};const fP=function(e,t,n,o){return hP[e]},vP={name:"en-US",locale:{code:"en-US",formatDistance:dP,formatLong:pP,formatRelative:fP,localize:{ordinalNumber:function(e,t){var n=Number(e),o=n%100;if(o>20||o<10)switch(o%10){case 1:return n+"st";case 2:return n+"nd";case 3:return n+"rd"}return n+"th"},era:iP({values:{narrow:["B","A"],abbreviated:["BC","AD"],wide:["Before Christ","Anno Domini"]},defaultWidth:"wide"}),quarter:iP({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["1st quarter","2nd quarter","3rd quarter","4th quarter"]},defaultWidth:"wide",argumentCallback:function(e){return e-1}}),month:iP({values:{narrow:["J","F","M","A","M","J","J","A","S","O","N","D"],abbreviated:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],wide:["January","February","March","April","May","June","July","August","September","October","November","December"]},defaultWidth:"wide"}),day:iP({values:{narrow:["S","M","T","W","T","F","S"],short:["Su","Mo","Tu","We","Th","Fr","Sa"],abbreviated:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],wide:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},defaultWidth:"wide"}),dayPeriod:iP({values:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"}},defaultWidth:"wide",formattingValues:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:lP({matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}}),era:aP({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:"any"}),quarter:aP({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:aP({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:"any"}),day:aP({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:"any"}),dayPeriod:aP({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}}};function mP(e,t){if(void 0!==e.one&&1===t)return e.one;var n=t%10,o=t%100;return 1===n&&11!==o?e.singularNominative.replace("{{count}}",String(t)):n>=2&&n<=4&&(o<10||o>20)?e.singularGenitive.replace("{{count}}",String(t)):e.pluralGenitive.replace("{{count}}",String(t))}function gP(e){return function(t,n){return null!=n&&n.addSuffix?n.comparison&&n.comparison>0?e.future?mP(e.future,t):"через "+mP(e.regular,t):e.past?mP(e.past,t):mP(e.regular,t)+" назад":mP(e.regular,t)}}var bP={lessThanXSeconds:gP({regular:{one:"меньше секунды",singularNominative:"меньше {{count}} секунды",singularGenitive:"меньше {{count}} секунд",pluralGenitive:"меньше {{count}} секунд"},future:{one:"меньше, чем через секунду",singularNominative:"меньше, чем через {{count}} секунду",singularGenitive:"меньше, чем через {{count}} секунды",pluralGenitive:"меньше, чем через {{count}} секунд"}}),xSeconds:gP({regular:{singularNominative:"{{count}} секунда",singularGenitive:"{{count}} секунды",pluralGenitive:"{{count}} секунд"},past:{singularNominative:"{{count}} секунду назад",singularGenitive:"{{count}} секунды назад",pluralGenitive:"{{count}} секунд назад"},future:{singularNominative:"через {{count}} секунду",singularGenitive:"через {{count}} секунды",pluralGenitive:"через {{count}} секунд"}}),halfAMinute:function(e,t){return null!=t&&t.addSuffix?t.comparison&&t.comparison>0?"через полминуты":"полминуты назад":"полминуты"},lessThanXMinutes:gP({regular:{one:"меньше минуты",singularNominative:"меньше {{count}} минуты",singularGenitive:"меньше {{count}} минут",pluralGenitive:"меньше {{count}} минут"},future:{one:"меньше, чем через минуту",singularNominative:"меньше, чем через {{count}} минуту",singularGenitive:"меньше, чем через {{count}} минуты",pluralGenitive:"меньше, чем через {{count}} минут"}}),xMinutes:gP({regular:{singularNominative:"{{count}} минута",singularGenitive:"{{count}} минуты",pluralGenitive:"{{count}} минут"},past:{singularNominative:"{{count}} минуту назад",singularGenitive:"{{count}} минуты назад",pluralGenitive:"{{count}} минут назад"},future:{singularNominative:"через {{count}} минуту",singularGenitive:"через {{count}} минуты",pluralGenitive:"через {{count}} минут"}}),aboutXHours:gP({regular:{singularNominative:"около {{count}} часа",singularGenitive:"около {{count}} часов",pluralGenitive:"около {{count}} часов"},future:{singularNominative:"приблизительно через {{count}} час",singularGenitive:"приблизительно через {{count}} часа",pluralGenitive:"приблизительно через {{count}} часов"}}),xHours:gP({regular:{singularNominative:"{{count}} час",singularGenitive:"{{count}} часа",pluralGenitive:"{{count}} часов"}}),xDays:gP({regular:{singularNominative:"{{count}} день",singularGenitive:"{{count}} дня",pluralGenitive:"{{count}} дней"}}),aboutXWeeks:gP({regular:{singularNominative:"около {{count}} недели",singularGenitive:"около {{count}} недель",pluralGenitive:"около {{count}} недель"},future:{singularNominative:"приблизительно через {{count}} неделю",singularGenitive:"приблизительно через {{count}} недели",pluralGenitive:"приблизительно через {{count}} недель"}}),xWeeks:gP({regular:{singularNominative:"{{count}} неделя",singularGenitive:"{{count}} недели",pluralGenitive:"{{count}} недель"}}),aboutXMonths:gP({regular:{singularNominative:"около {{count}} месяца",singularGenitive:"около {{count}} месяцев",pluralGenitive:"около {{count}} месяцев"},future:{singularNominative:"приблизительно через {{count}} месяц",singularGenitive:"приблизительно через {{count}} месяца",pluralGenitive:"приблизительно через {{count}} месяцев"}}),xMonths:gP({regular:{singularNominative:"{{count}} месяц",singularGenitive:"{{count}} месяца",pluralGenitive:"{{count}} месяцев"}}),aboutXYears:gP({regular:{singularNominative:"около {{count}} года",singularGenitive:"около {{count}} лет",pluralGenitive:"около {{count}} лет"},future:{singularNominative:"приблизительно через {{count}} год",singularGenitive:"приблизительно через {{count}} года",pluralGenitive:"приблизительно через {{count}} лет"}}),xYears:gP({regular:{singularNominative:"{{count}} год",singularGenitive:"{{count}} года",pluralGenitive:"{{count}} лет"}}),overXYears:gP({regular:{singularNominative:"больше {{count}} года",singularGenitive:"больше {{count}} лет",pluralGenitive:"больше {{count}} лет"},future:{singularNominative:"больше, чем через {{count}} год",singularGenitive:"больше, чем через {{count}} года",pluralGenitive:"больше, чем через {{count}} лет"}}),almostXYears:gP({regular:{singularNominative:"почти {{count}} год",singularGenitive:"почти {{count}} года",pluralGenitive:"почти {{count}} лет"},future:{singularNominative:"почти через {{count}} год",singularGenitive:"почти через {{count}} года",pluralGenitive:"почти через {{count}} лет"}})};const yP=function(e,t,n){return bP[e](t,n)};var xP={date:X_({formats:{full:"EEEE, d MMMM y 'г.'",long:"d MMMM y 'г.'",medium:"d MMM y 'г.'",short:"dd.MM.y"},defaultWidth:"full"}),time:X_({formats:{full:"H:mm:ss zzzz",long:"H:mm:ss z",medium:"H:mm:ss",short:"H:mm"},defaultWidth:"full"}),dateTime:X_({formats:{any:"{{date}}, {{time}}"},defaultWidth:"any"})},CP=["воскресенье","понедельник","вторник","среду","четверг","пятницу","субботу"];function wP(e){var t=CP[e];return 2===e?"'во "+t+" в' p":"'в "+t+" в' p"}var kP={lastWeek:function(e,t,n){var o=e.getUTCDay();return tP(e,t,n)?wP(o):function(e){var t=CP[e];switch(e){case 0:return"'в прошлое "+t+" в' p";case 1:case 2:case 4:return"'в прошлый "+t+" в' p";case 3:case 5:case 6:return"'в прошлую "+t+" в' p"}}(o)},yesterday:"'вчера в' p",today:"'сегодня в' p",tomorrow:"'завтра в' p",nextWeek:function(e,t,n){var o=e.getUTCDay();return tP(e,t,n)?wP(o):function(e){var t=CP[e];switch(e){case 0:return"'в следующее "+t+" в' p";case 1:case 2:case 4:return"'в следующий "+t+" в' p";case 3:case 5:case 6:return"'в следующую "+t+" в' p"}}(o)},other:"P"};const SP=function(e,t,n,o){var r=kP[e];return"function"==typeof r?r(t,n,o):r},_P={name:"ru-RU",locale:{code:"ru",formatDistance:yP,formatLong:xP,formatRelative:SP,localize:{ordinalNumber:function(e,t){var n=Number(e),o=null==t?void 0:t.unit;return n+("date"===o?"-е":"week"===o||"minute"===o||"second"===o?"-я":"-й")},era:iP({values:{narrow:["до н.э.","н.э."],abbreviated:["до н. э.","н. э."],wide:["до нашей эры","нашей эры"]},defaultWidth:"wide"}),quarter:iP({values:{narrow:["1","2","3","4"],abbreviated:["1-й кв.","2-й кв.","3-й кв.","4-й кв."],wide:["1-й квартал","2-й квартал","3-й квартал","4-й квартал"]},defaultWidth:"wide",argumentCallback:function(e){return e-1}}),month:iP({values:{narrow:["Я","Ф","М","А","М","И","И","А","С","О","Н","Д"],abbreviated:["янв.","фев.","март","апр.","май","июнь","июль","авг.","сент.","окт.","нояб.","дек."],wide:["январь","февраль","март","апрель","май","июнь","июль","август","сентябрь","октябрь","ноябрь","декабрь"]},defaultWidth:"wide",formattingValues:{narrow:["Я","Ф","М","А","М","И","И","А","С","О","Н","Д"],abbreviated:["янв.","фев.","мар.","апр.","мая","июн.","июл.","авг.","сент.","окт.","нояб.","дек."],wide:["января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"]},defaultFormattingWidth:"wide"}),day:iP({values:{narrow:["В","П","В","С","Ч","П","С"],short:["вс","пн","вт","ср","чт","пт","сб"],abbreviated:["вск","пнд","втр","срд","чтв","птн","суб"],wide:["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"]},defaultWidth:"wide"}),dayPeriod:iP({values:{narrow:{am:"ДП",pm:"ПП",midnight:"полн.",noon:"полд.",morning:"утро",afternoon:"день",evening:"веч.",night:"ночь"},abbreviated:{am:"ДП",pm:"ПП",midnight:"полн.",noon:"полд.",morning:"утро",afternoon:"день",evening:"веч.",night:"ночь"},wide:{am:"ДП",pm:"ПП",midnight:"полночь",noon:"полдень",morning:"утро",afternoon:"день",evening:"вечер",night:"ночь"}},defaultWidth:"any",formattingValues:{narrow:{am:"ДП",pm:"ПП",midnight:"полн.",noon:"полд.",morning:"утра",afternoon:"дня",evening:"веч.",night:"ночи"},abbreviated:{am:"ДП",pm:"ПП",midnight:"полн.",noon:"полд.",morning:"утра",afternoon:"дня",evening:"веч.",night:"ночи"},wide:{am:"ДП",pm:"ПП",midnight:"полночь",noon:"полдень",morning:"утра",afternoon:"дня",evening:"вечера",night:"ночи"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:lP({matchPattern:/^(\d+)(-?(е|я|й|ое|ье|ая|ья|ый|ой|ий|ый))?/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}}),era:aP({matchPatterns:{narrow:/^((до )?н\.?\s?э\.?)/i,abbreviated:/^((до )?н\.?\s?э\.?)/i,wide:/^(до нашей эры|нашей эры|наша эра)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^д/i,/^н/i]},defaultParseWidth:"any"}),quarter:aP({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^[1234](-?[ыои]?й?)? кв.?/i,wide:/^[1234](-?[ыои]?й?)? квартал/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:aP({matchPatterns:{narrow:/^[яфмаисонд]/i,abbreviated:/^(янв|фев|март?|апр|ма[йя]|июн[ья]?|июл[ья]?|авг|сент?|окт|нояб?|дек)\.?/i,wide:/^(январ[ья]|феврал[ья]|марта?|апрел[ья]|ма[йя]|июн[ья]|июл[ья]|августа?|сентябр[ья]|октябр[ья]|октябр[ья]|ноябр[ья]|декабр[ья])/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^я/i,/^ф/i,/^м/i,/^а/i,/^м/i,/^и/i,/^и/i,/^а/i,/^с/i,/^о/i,/^н/i,/^я/i],any:[/^я/i,/^ф/i,/^мар/i,/^ап/i,/^ма[йя]/i,/^июн/i,/^июл/i,/^ав/i,/^с/i,/^о/i,/^н/i,/^д/i]},defaultParseWidth:"any"}),day:aP({matchPatterns:{narrow:/^[впсч]/i,short:/^(вс|во|пн|по|вт|ср|чт|че|пт|пя|сб|су)\.?/i,abbreviated:/^(вск|вос|пнд|пон|втр|вто|срд|сре|чтв|чет|птн|пят|суб).?/i,wide:/^(воскресень[ея]|понедельника?|вторника?|сред[аы]|четверга?|пятниц[аы]|суббот[аы])/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^в/i,/^п/i,/^в/i,/^с/i,/^ч/i,/^п/i,/^с/i],any:[/^в[ос]/i,/^п[он]/i,/^в/i,/^ср/i,/^ч/i,/^п[ят]/i,/^с[уб]/i]},defaultParseWidth:"any"}),dayPeriod:aP({matchPatterns:{narrow:/^([дп]п|полн\.?|полд\.?|утр[оа]|день|дня|веч\.?|ноч[ьи])/i,abbreviated:/^([дп]п|полн\.?|полд\.?|утр[оа]|день|дня|веч\.?|ноч[ьи])/i,wide:/^([дп]п|полночь|полдень|утр[оа]|день|дня|вечера?|ноч[ьи])/i},defaultMatchWidth:"wide",parsePatterns:{any:{am:/^дп/i,pm:/^пп/i,midnight:/^полн/i,noon:/^полд/i,morning:/^у/i,afternoon:/^д[ен]/i,evening:/^в/i,night:/^н/i}},defaultParseWidth:"any"})},options:{weekStartsOn:1,firstWeekContainsDate:1}}};var PP={lessThanXSeconds:{one:"1秒未満",other:"{{count}}秒未満",oneWithSuffix:"約1秒",otherWithSuffix:"約{{count}}秒"},xSeconds:{one:"1秒",other:"{{count}}秒"},halfAMinute:"30秒",lessThanXMinutes:{one:"1分未満",other:"{{count}}分未満",oneWithSuffix:"約1分",otherWithSuffix:"約{{count}}分"},xMinutes:{one:"1分",other:"{{count}}分"},aboutXHours:{one:"約1時間",other:"約{{count}}時間"},xHours:{one:"1時間",other:"{{count}}時間"},xDays:{one:"1日",other:"{{count}}日"},aboutXWeeks:{one:"約1週間",other:"約{{count}}週間"},xWeeks:{one:"1週間",other:"{{count}}週間"},aboutXMonths:{one:"約1か月",other:"約{{count}}か月"},xMonths:{one:"1か月",other:"{{count}}か月"},aboutXYears:{one:"約1年",other:"約{{count}}年"},xYears:{one:"1年",other:"{{count}}年"},overXYears:{one:"1年以上",other:"{{count}}年以上"},almostXYears:{one:"1年近く",other:"{{count}}年近く"}};const TP=function(e,t,n){var o;n=n||{};var r=PP[e];return o="string"==typeof r?r:1===t?n.addSuffix&&r.oneWithSuffix?r.oneWithSuffix:r.one:n.addSuffix&&r.otherWithSuffix?r.otherWithSuffix.replace("{{count}}",String(t)):r.other.replace("{{count}}",String(t)),n.addSuffix?n.comparison&&n.comparison>0?o+"後":o+"前":o};var AP={date:X_({formats:{full:"y年M月d日EEEE",long:"y年M月d日",medium:"y/MM/dd",short:"y/MM/dd"},defaultWidth:"full"}),time:X_({formats:{full:"H時mm分ss秒 zzzz",long:"H:mm:ss z",medium:"H:mm:ss",short:"H:mm"},defaultWidth:"full"}),dateTime:X_({formats:{full:"{{date}} {{time}}",long:"{{date}} {{time}}",medium:"{{date}} {{time}}",short:"{{date}} {{time}}"},defaultWidth:"full"})},zP={lastWeek:"先週のeeeeのp",yesterday:"昨日のp",today:"今日のp",tomorrow:"明日のp",nextWeek:"翌週のeeeeのp",other:"P"};const RP=function(e,t,n,o){return zP[e]},EP={name:"ja-JP",locale:{code:"ja",formatDistance:TP,formatLong:AP,formatRelative:RP,localize:{ordinalNumber:function(e,t){var n=Number(e);switch(String(null==t?void 0:t.unit)){case"year":return"".concat(n,"年");case"quarter":return"第".concat(n,"四半期");case"month":return"".concat(n,"月");case"week":return"第".concat(n,"週");case"date":return"".concat(n,"日");case"hour":return"".concat(n,"時");case"minute":return"".concat(n,"分");case"second":return"".concat(n,"秒");default:return"".concat(n)}},era:iP({values:{narrow:["BC","AC"],abbreviated:["紀元前","西暦"],wide:["紀元前","西暦"]},defaultWidth:"wide"}),quarter:iP({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["第1四半期","第2四半期","第3四半期","第4四半期"]},defaultWidth:"wide",argumentCallback:function(e){return Number(e)-1}}),month:iP({values:{narrow:["1","2","3","4","5","6","7","8","9","10","11","12"],abbreviated:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],wide:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"]},defaultWidth:"wide"}),day:iP({values:{narrow:["日","月","火","水","木","金","土"],short:["日","月","火","水","木","金","土"],abbreviated:["日","月","火","水","木","金","土"],wide:["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"]},defaultWidth:"wide"}),dayPeriod:iP({values:{narrow:{am:"午前",pm:"午後",midnight:"深夜",noon:"正午",morning:"朝",afternoon:"午後",evening:"夜",night:"深夜"},abbreviated:{am:"午前",pm:"午後",midnight:"深夜",noon:"正午",morning:"朝",afternoon:"午後",evening:"夜",night:"深夜"},wide:{am:"午前",pm:"午後",midnight:"深夜",noon:"正午",morning:"朝",afternoon:"午後",evening:"夜",night:"深夜"}},defaultWidth:"wide",formattingValues:{narrow:{am:"午前",pm:"午後",midnight:"深夜",noon:"正午",morning:"朝",afternoon:"午後",evening:"夜",night:"深夜"},abbreviated:{am:"午前",pm:"午後",midnight:"深夜",noon:"正午",morning:"朝",afternoon:"午後",evening:"夜",night:"深夜"},wide:{am:"午前",pm:"午後",midnight:"深夜",noon:"正午",morning:"朝",afternoon:"午後",evening:"夜",night:"深夜"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:lP({matchPattern:/^第?\d+(年|四半期|月|週|日|時|分|秒)?/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}}),era:aP({matchPatterns:{narrow:/^(B\.?C\.?|A\.?D\.?)/i,abbreviated:/^(紀元[前後]|西暦)/i,wide:/^(紀元[前後]|西暦)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^B/i,/^A/i],any:[/^(紀元前)/i,/^(西暦|紀元後)/i]},defaultParseWidth:"any"}),quarter:aP({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^Q[1234]/i,wide:/^第[1234一二三四1234]四半期/i},defaultMatchWidth:"wide",parsePatterns:{any:[/(1|一|1)/i,/(2|二|2)/i,/(3|三|3)/i,/(4|四|4)/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:aP({matchPatterns:{narrow:/^([123456789]|1[012])/,abbreviated:/^([123456789]|1[012])月/i,wide:/^([123456789]|1[012])月/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^1\D/,/^2/,/^3/,/^4/,/^5/,/^6/,/^7/,/^8/,/^9/,/^10/,/^11/,/^12/]},defaultParseWidth:"any"}),day:aP({matchPatterns:{narrow:/^[日月火水木金土]/,short:/^[日月火水木金土]/,abbreviated:/^[日月火水木金土]/,wide:/^[日月火水木金土]曜日/},defaultMatchWidth:"wide",parsePatterns:{any:[/^日/,/^月/,/^火/,/^水/,/^木/,/^金/,/^土/]},defaultParseWidth:"any"}),dayPeriod:aP({matchPatterns:{any:/^(AM|PM|午前|午後|正午|深夜|真夜中|夜|朝)/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^(A|午前)/i,pm:/^(P|午後)/i,midnight:/^深夜|真夜中/i,noon:/^正午/i,morning:/^朝/i,afternoon:/^午後/i,evening:/^夜/i,night:/^深夜/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}}};var OP={lessThanXSeconds:{one:"1초 미만",other:"{{count}}초 미만"},xSeconds:{one:"1초",other:"{{count}}초"},halfAMinute:"30초",lessThanXMinutes:{one:"1분 미만",other:"{{count}}분 미만"},xMinutes:{one:"1분",other:"{{count}}분"},aboutXHours:{one:"약 1시간",other:"약 {{count}}시간"},xHours:{one:"1시간",other:"{{count}}시간"},xDays:{one:"1일",other:"{{count}}일"},aboutXWeeks:{one:"약 1주",other:"약 {{count}}주"},xWeeks:{one:"1주",other:"{{count}}주"},aboutXMonths:{one:"약 1개월",other:"약 {{count}}개월"},xMonths:{one:"1개월",other:"{{count}}개월"},aboutXYears:{one:"약 1년",other:"약 {{count}}년"},xYears:{one:"1년",other:"{{count}}년"},overXYears:{one:"1년 이상",other:"{{count}}년 이상"},almostXYears:{one:"거의 1년",other:"거의 {{count}}년"}};const MP=function(e,t,n){var o,r=OP[e];return o="string"==typeof r?r:1===t?r.one:r.other.replace("{{count}}",t.toString()),null!=n&&n.addSuffix?n.comparison&&n.comparison>0?o+" 후":o+" 전":o};var FP={date:X_({formats:{full:"y년 M월 d일 EEEE",long:"y년 M월 d일",medium:"y.MM.dd",short:"y.MM.dd"},defaultWidth:"full"}),time:X_({formats:{full:"a H시 mm분 ss초 zzzz",long:"a H:mm:ss z",medium:"HH:mm:ss",short:"HH:mm"},defaultWidth:"full"}),dateTime:X_({formats:{full:"{{date}} {{time}}",long:"{{date}} {{time}}",medium:"{{date}} {{time}}",short:"{{date}} {{time}}"},defaultWidth:"full"})},IP={lastWeek:"'지난' eeee p",yesterday:"'어제' p",today:"'오늘' p",tomorrow:"'내일' p",nextWeek:"'다음' eeee p",other:"P"};const LP=function(e,t,n,o){return IP[e]},BP={name:"ko-KR",locale:{code:"ko",formatDistance:MP,formatLong:FP,formatRelative:LP,localize:{ordinalNumber:function(e,t){var n=Number(e);switch(String(null==t?void 0:t.unit)){case"minute":case"second":return String(n);case"date":return n+"일";default:return n+"번째"}},era:iP({values:{narrow:["BC","AD"],abbreviated:["BC","AD"],wide:["기원전","서기"]},defaultWidth:"wide"}),quarter:iP({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["1분기","2분기","3분기","4분기"]},defaultWidth:"wide",argumentCallback:function(e){return e-1}}),month:iP({values:{narrow:["1","2","3","4","5","6","7","8","9","10","11","12"],abbreviated:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],wide:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"]},defaultWidth:"wide"}),day:iP({values:{narrow:["일","월","화","수","목","금","토"],short:["일","월","화","수","목","금","토"],abbreviated:["일","월","화","수","목","금","토"],wide:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"]},defaultWidth:"wide"}),dayPeriod:iP({values:{narrow:{am:"오전",pm:"오후",midnight:"자정",noon:"정오",morning:"아침",afternoon:"오후",evening:"저녁",night:"밤"},abbreviated:{am:"오전",pm:"오후",midnight:"자정",noon:"정오",morning:"아침",afternoon:"오후",evening:"저녁",night:"밤"},wide:{am:"오전",pm:"오후",midnight:"자정",noon:"정오",morning:"아침",afternoon:"오후",evening:"저녁",night:"밤"}},defaultWidth:"wide",formattingValues:{narrow:{am:"오전",pm:"오후",midnight:"자정",noon:"정오",morning:"아침",afternoon:"오후",evening:"저녁",night:"밤"},abbreviated:{am:"오전",pm:"오후",midnight:"자정",noon:"정오",morning:"아침",afternoon:"오후",evening:"저녁",night:"밤"},wide:{am:"오전",pm:"오후",midnight:"자정",noon:"정오",morning:"아침",afternoon:"오후",evening:"저녁",night:"밤"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:lP({matchPattern:/^(\d+)(일|번째)?/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}}),era:aP({matchPatterns:{narrow:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(기원전|서기)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^(bc|기원전)/i,/^(ad|서기)/i]},defaultParseWidth:"any"}),quarter:aP({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234]사?분기/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:aP({matchPatterns:{narrow:/^(1[012]|[123456789])/,abbreviated:/^(1[012]|[123456789])월/i,wide:/^(1[012]|[123456789])월/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^1월?$/,/^2/,/^3/,/^4/,/^5/,/^6/,/^7/,/^8/,/^9/,/^10/,/^11/,/^12/]},defaultParseWidth:"any"}),day:aP({matchPatterns:{narrow:/^[일월화수목금토]/,short:/^[일월화수목금토]/,abbreviated:/^[일월화수목금토]/,wide:/^[일월화수목금토]요일/},defaultMatchWidth:"wide",parsePatterns:{any:[/^일/,/^월/,/^화/,/^수/,/^목/,/^금/,/^토/]},defaultParseWidth:"any"}),dayPeriod:aP({matchPatterns:{any:/^(am|pm|오전|오후|자정|정오|아침|저녁|밤)/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^(am|오전)/i,pm:/^(pm|오후)/i,midnight:/^자정/i,noon:/^정오/i,morning:/^아침/i,afternoon:/^오후/i,evening:/^저녁/i,night:/^밤/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}}};var DP={lessThanXSeconds:{one:"dưới 1 giây",other:"dưới {{count}} giây"},xSeconds:{one:"1 giây",other:"{{count}} giây"},halfAMinute:"nửa phút",lessThanXMinutes:{one:"dưới 1 phút",other:"dưới {{count}} phút"},xMinutes:{one:"1 phút",other:"{{count}} phút"},aboutXHours:{one:"khoảng 1 giờ",other:"khoảng {{count}} giờ"},xHours:{one:"1 giờ",other:"{{count}} giờ"},xDays:{one:"1 ngày",other:"{{count}} ngày"},aboutXWeeks:{one:"khoảng 1 tuần",other:"khoảng {{count}} tuần"},xWeeks:{one:"1 tuần",other:"{{count}} tuần"},aboutXMonths:{one:"khoảng 1 tháng",other:"khoảng {{count}} tháng"},xMonths:{one:"1 tháng",other:"{{count}} tháng"},aboutXYears:{one:"khoảng 1 năm",other:"khoảng {{count}} năm"},xYears:{one:"1 năm",other:"{{count}} năm"},overXYears:{one:"hơn 1 năm",other:"hơn {{count}} năm"},almostXYears:{one:"gần 1 năm",other:"gần {{count}} năm"}};const $P=function(e,t,n){var o,r=DP[e];return o="string"==typeof r?r:1===t?r.one:r.other.replace("{{count}}",String(t)),null!=n&&n.addSuffix?n.comparison&&n.comparison>0?o+" nữa":o+" trước":o};var NP={date:X_({formats:{full:"EEEE, 'ngày' d MMMM 'năm' y",long:"'ngày' d MMMM 'năm' y",medium:"d MMM 'năm' y",short:"dd/MM/y"},defaultWidth:"full"}),time:X_({formats:{full:"HH:mm:ss zzzz",long:"HH:mm:ss z",medium:"HH:mm:ss",short:"HH:mm"},defaultWidth:"full"}),dateTime:X_({formats:{full:"{{date}} {{time}}",long:"{{date}} {{time}}",medium:"{{date}} {{time}}",short:"{{date}} {{time}}"},defaultWidth:"full"})},jP={lastWeek:"eeee 'tuần trước vào lúc' p",yesterday:"'hôm qua vào lúc' p",today:"'hôm nay vào lúc' p",tomorrow:"'ngày mai vào lúc' p",nextWeek:"eeee 'tới vào lúc' p",other:"P"};const HP=function(e,t,n,o){return jP[e]},WP={name:"vi-VN",locale:{code:"vi",formatDistance:$P,formatLong:NP,formatRelative:HP,localize:{ordinalNumber:function(e,t){var n=Number(e),o=null==t?void 0:t.unit;if("quarter"===o)switch(n){case 1:return"I";case 2:return"II";case 3:return"III";case 4:return"IV"}else if("day"===o)switch(n){case 1:return"thứ 2";case 2:return"thứ 3";case 3:return"thứ 4";case 4:return"thứ 5";case 5:return"thứ 6";case 6:return"thứ 7";case 7:return"chủ nhật"}else{if("week"===o)return 1===n?"thứ nhất":"thứ "+n;if("dayOfYear"===o)return 1===n?"đầu tiên":"thứ "+n}return String(n)},era:iP({values:{narrow:["TCN","SCN"],abbreviated:["trước CN","sau CN"],wide:["trước Công Nguyên","sau Công Nguyên"]},defaultWidth:"wide"}),quarter:iP({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["Quý 1","Quý 2","Quý 3","Quý 4"]},defaultWidth:"wide",formattingValues:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["quý I","quý II","quý III","quý IV"]},defaultFormattingWidth:"wide",argumentCallback:function(e){return e-1}}),month:iP({values:{narrow:["1","2","3","4","5","6","7","8","9","10","11","12"],abbreviated:["Thg 1","Thg 2","Thg 3","Thg 4","Thg 5","Thg 6","Thg 7","Thg 8","Thg 9","Thg 10","Thg 11","Thg 12"],wide:["Tháng Một","Tháng Hai","Tháng Ba","Tháng Tư","Tháng Năm","Tháng Sáu","Tháng Bảy","Tháng Tám","Tháng Chín","Tháng Mười","Tháng Mười Một","Tháng Mười Hai"]},defaultWidth:"wide",formattingValues:{narrow:["01","02","03","04","05","06","07","08","09","10","11","12"],abbreviated:["thg 1","thg 2","thg 3","thg 4","thg 5","thg 6","thg 7","thg 8","thg 9","thg 10","thg 11","thg 12"],wide:["tháng 01","tháng 02","tháng 03","tháng 04","tháng 05","tháng 06","tháng 07","tháng 08","tháng 09","tháng 10","tháng 11","tháng 12"]},defaultFormattingWidth:"wide"}),day:iP({values:{narrow:["CN","T2","T3","T4","T5","T6","T7"],short:["CN","Th 2","Th 3","Th 4","Th 5","Th 6","Th 7"],abbreviated:["CN","Thứ 2","Thứ 3","Thứ 4","Thứ 5","Thứ 6","Thứ 7"],wide:["Chủ Nhật","Thứ Hai","Thứ Ba","Thứ Tư","Thứ Năm","Thứ Sáu","Thứ Bảy"]},defaultWidth:"wide"}),dayPeriod:iP({values:{narrow:{am:"am",pm:"pm",midnight:"nửa đêm",noon:"tr",morning:"sg",afternoon:"ch",evening:"tối",night:"đêm"},abbreviated:{am:"AM",pm:"PM",midnight:"nửa đêm",noon:"trưa",morning:"sáng",afternoon:"chiều",evening:"tối",night:"đêm"},wide:{am:"SA",pm:"CH",midnight:"nửa đêm",noon:"trưa",morning:"sáng",afternoon:"chiều",evening:"tối",night:"đêm"}},defaultWidth:"wide",formattingValues:{narrow:{am:"am",pm:"pm",midnight:"nửa đêm",noon:"tr",morning:"sg",afternoon:"ch",evening:"tối",night:"đêm"},abbreviated:{am:"AM",pm:"PM",midnight:"nửa đêm",noon:"trưa",morning:"sáng",afternoon:"chiều",evening:"tối",night:"đêm"},wide:{am:"SA",pm:"CH",midnight:"nửa đêm",noon:"giữa trưa",morning:"vào buổi sáng",afternoon:"vào buổi chiều",evening:"vào buổi tối",night:"vào ban đêm"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:lP({matchPattern:/^(\d+)/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}}),era:aP({matchPatterns:{narrow:/^(tcn|scn)/i,abbreviated:/^(trước CN|sau CN)/i,wide:/^(trước Công Nguyên|sau Công Nguyên)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^t/i,/^s/i]},defaultParseWidth:"any"}),quarter:aP({matchPatterns:{narrow:/^([1234]|i{1,3}v?)/i,abbreviated:/^q([1234]|i{1,3}v?)/i,wide:/^quý ([1234]|i{1,3}v?)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/(1|i)$/i,/(2|ii)$/i,/(3|iii)$/i,/(4|iv)$/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:aP({matchPatterns:{narrow:/^(0?[2-9]|10|11|12|0?1)/i,abbreviated:/^thg[ _]?(0?[1-9](?!\d)|10|11|12)/i,wide:/^tháng ?(Một|Hai|Ba|Tư|Năm|Sáu|Bảy|Tám|Chín|Mười|Mười ?Một|Mười ?Hai|0?[1-9](?!\d)|10|11|12)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/0?1$/i,/0?2/i,/3/,/4/,/5/,/6/,/7/,/8/,/9/,/10/,/11/,/12/],abbreviated:[/^thg[ _]?0?1(?!\d)/i,/^thg[ _]?0?2/i,/^thg[ _]?0?3/i,/^thg[ _]?0?4/i,/^thg[ _]?0?5/i,/^thg[ _]?0?6/i,/^thg[ _]?0?7/i,/^thg[ _]?0?8/i,/^thg[ _]?0?9/i,/^thg[ _]?10/i,/^thg[ _]?11/i,/^thg[ _]?12/i],wide:[/^tháng ?(Một|0?1(?!\d))/i,/^tháng ?(Hai|0?2)/i,/^tháng ?(Ba|0?3)/i,/^tháng ?(Tư|0?4)/i,/^tháng ?(Năm|0?5)/i,/^tháng ?(Sáu|0?6)/i,/^tháng ?(Bảy|0?7)/i,/^tháng ?(Tám|0?8)/i,/^tháng ?(Chín|0?9)/i,/^tháng ?(Mười|10)/i,/^tháng ?(Mười ?Một|11)/i,/^tháng ?(Mười ?Hai|12)/i]},defaultParseWidth:"wide"}),day:aP({matchPatterns:{narrow:/^(CN|T2|T3|T4|T5|T6|T7)/i,short:/^(CN|Th ?2|Th ?3|Th ?4|Th ?5|Th ?6|Th ?7)/i,abbreviated:/^(CN|Th ?2|Th ?3|Th ?4|Th ?5|Th ?6|Th ?7)/i,wide:/^(Chủ ?Nhật|Chúa ?Nhật|thứ ?Hai|thứ ?Ba|thứ ?Tư|thứ ?Năm|thứ ?Sáu|thứ ?Bảy)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/CN/i,/2/i,/3/i,/4/i,/5/i,/6/i,/7/i],short:[/CN/i,/2/i,/3/i,/4/i,/5/i,/6/i,/7/i],abbreviated:[/CN/i,/2/i,/3/i,/4/i,/5/i,/6/i,/7/i],wide:[/(Chủ|Chúa) ?Nhật/i,/Hai/i,/Ba/i,/Tư/i,/Năm/i,/Sáu/i,/Bảy/i]},defaultParseWidth:"wide"}),dayPeriod:aP({matchPatterns:{narrow:/^(a|p|nửa đêm|trưa|(giờ) (sáng|chiều|tối|đêm))/i,abbreviated:/^(am|pm|nửa đêm|trưa|(giờ) (sáng|chiều|tối|đêm))/i,wide:/^(ch[^i]*|sa|nửa đêm|trưa|(giờ) (sáng|chiều|tối|đêm))/i},defaultMatchWidth:"wide",parsePatterns:{any:{am:/^(a|sa)/i,pm:/^(p|ch[^i]*)/i,midnight:/nửa đêm/i,noon:/trưa/i,morning:/sáng/i,afternoon:/chiều/i,evening:/tối/i,night:/^đêm/i}},defaultParseWidth:"any"})},options:{weekStartsOn:1,firstWeekContainsDate:1}}},UP={name:"fa-IR",locale:sP};function VP(e){const{mergedLocaleRef:t,mergedDateLocaleRef:n}=Po(M_,null)||{},o=ai((()=>{var n,o;return null!==(o=null===(n=null==t?void 0:t.value)||void 0===n?void 0:n[e])&&void 0!==o?o:j_[e]})),r=ai((()=>{var e;return null!==(e=null==n?void 0:n.value)&&void 0!==e?e:vP}));return{dateLocaleRef:r,localeRef:o}}function qP(e,t,n){if(!t)return;const o=by(),r=Po(M_,null),i=()=>{const i=n.value;t.mount({id:void 0===i?e:i+e,head:!0,anchorMetaName:F_,props:{bPrefix:i?`.${i}-`:void 0},ssr:o}),(null==r?void 0:r.preflightStyleDisabled)||O_.mount({id:"n-global",head:!0,anchorMetaName:F_,ssr:o})};o?i():Dn(i)}function KP(e,t,n,o){var r;n||fg("useThemeClass","cssVarsRef is not passed");const i=null===(r=Po(M_,null))||void 0===r?void 0:r.mergedThemeHashRef,a=Et(""),l=by();let s;const c=`__${e}`;return ir((()=>{(()=>{let e=c;const r=t?t.value:void 0,u=null==i?void 0:i.value;u&&(e+=`-${u}`),r&&(e+=`-${r}`);const{themeOverrides:d,builtinThemeOverrides:p}=o;d&&(e+=`-${Wg(JSON.stringify(d))}`),p&&(e+=`-${Wg(JSON.stringify(p))}`),a.value=e,s=()=>{const t=n.value;let o="";for(const e in t)o+=`${e}: ${t[e]};`;eb(`.${e}`,o).mount({id:e,ssr:l}),s=void 0}})()})),{themeClass:a,onRender:()=>{null==s||s()}}}function GP(e,t,n){if(!t)return;const o=by(),r=ai((()=>{const{value:n}=t;if(!n)return;const o=n[e];return o||void 0})),i=()=>{ir((()=>{const{value:t}=n,i=`${t}${e}Rtl`;if(function(e,t){if(void 0===e)return!1;if(t){const{context:{ids:n}}=t;return n.has(e)}return null!==Ig(e)}(i,o))return;const{value:a}=r;a&&a.style.mount({id:i,head:!0,anchorMetaName:F_,props:{bPrefix:t?`.${t}-`:void 0},ssr:o})}))};return o?i():Dn(i),r}const XP=zn({name:"Add",render:()=>li("svg",{width:"512",height:"512",viewBox:"0 0 512 512",fill:"none",xmlns:"http://www.w3.org/2000/svg"},li("path",{d:"M256 112V400M400 256H112",stroke:"currentColor","stroke-width":"32","stroke-linecap":"round","stroke-linejoin":"round"}))}),YP=zn({name:"ArrowDown",render:()=>li("svg",{viewBox:"0 0 28 28",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},li("g",{"fill-rule":"nonzero"},li("path",{d:"M23.7916,15.2664 C24.0788,14.9679 24.0696,14.4931 23.7711,14.206 C23.4726,13.9188 22.9978,13.928 22.7106,14.2265 L14.7511,22.5007 L14.7511,3.74792 C14.7511,3.33371 14.4153,2.99792 14.0011,2.99792 C13.5869,2.99792 13.2511,3.33371 13.2511,3.74793 L13.2511,22.4998 L5.29259,14.2265 C5.00543,13.928 4.53064,13.9188 4.23213,14.206 C3.93361,14.4931 3.9244,14.9679 4.21157,15.2664 L13.2809,24.6944 C13.6743,25.1034 14.3289,25.1034 14.7223,24.6944 L23.7916,15.2664 Z"}))))});function QP(e,t){return zn({name:Ik(e),setup(){var n;const o=null===(n=Po(M_,null))||void 0===n?void 0:n.mergedIconsRef;return()=>{var n;const r=null===(n=null==o?void 0:o.value)||void 0===n?void 0:n[e];return r?r():t}}})}const ZP=zn({name:"Backward",render:()=>li("svg",{viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},li("path",{d:"M12.2674 15.793C11.9675 16.0787 11.4927 16.0672 11.2071 15.7673L6.20572 10.5168C5.9298 10.2271 5.9298 9.7719 6.20572 9.48223L11.2071 4.23177C11.4927 3.93184 11.9675 3.92031 12.2674 4.206C12.5673 4.49169 12.5789 4.96642 12.2932 5.26634L7.78458 9.99952L12.2932 14.7327C12.5789 15.0326 12.5673 15.5074 12.2674 15.793Z",fill:"currentColor"}))}),JP=zn({name:"Checkmark",render:()=>li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 16 16"},li("g",{fill:"none"},li("path",{d:"M14.046 3.486a.75.75 0 0 1-.032 1.06l-7.93 7.474a.85.85 0 0 1-1.188-.022l-2.68-2.72a.75.75 0 1 1 1.068-1.053l2.234 2.267l7.468-7.038a.75.75 0 0 1 1.06.032z",fill:"currentColor"})))}),eT=zn({name:"ChevronRight",render:()=>li("svg",{viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},li("path",{d:"M5.64645 3.14645C5.45118 3.34171 5.45118 3.65829 5.64645 3.85355L9.79289 8L5.64645 12.1464C5.45118 12.3417 5.45118 12.6583 5.64645 12.8536C5.84171 13.0488 6.15829 13.0488 6.35355 12.8536L10.8536 8.35355C11.0488 8.15829 11.0488 7.84171 10.8536 7.64645L6.35355 3.14645C6.15829 2.95118 5.84171 2.95118 5.64645 3.14645Z",fill:"currentColor"}))}),tT=QP("close",li("svg",{viewBox:"0 0 12 12",version:"1.1",xmlns:"http://www.w3.org/2000/svg","aria-hidden":!0},li("g",{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"},li("g",{fill:"currentColor","fill-rule":"nonzero"},li("path",{d:"M2.08859116,2.2156945 L2.14644661,2.14644661 C2.32001296,1.97288026 2.58943736,1.95359511 2.7843055,2.08859116 L2.85355339,2.14644661 L6,5.293 L9.14644661,2.14644661 C9.34170876,1.95118446 9.65829124,1.95118446 9.85355339,2.14644661 C10.0488155,2.34170876 10.0488155,2.65829124 9.85355339,2.85355339 L6.707,6 L9.85355339,9.14644661 C10.0271197,9.32001296 10.0464049,9.58943736 9.91140884,9.7843055 L9.85355339,9.85355339 C9.67998704,10.0271197 9.41056264,10.0464049 9.2156945,9.91140884 L9.14644661,9.85355339 L6,6.707 L2.85355339,9.85355339 C2.65829124,10.0488155 2.34170876,10.0488155 2.14644661,9.85355339 C1.95118446,9.65829124 1.95118446,9.34170876 2.14644661,9.14644661 L5.293,6 L2.14644661,2.85355339 C1.97288026,2.67998704 1.95359511,2.41056264 2.08859116,2.2156945 L2.14644661,2.14644661 L2.08859116,2.2156945 Z"}))))),nT=zn({name:"Eye",render:()=>li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},li("path",{d:"M255.66 112c-77.94 0-157.89 45.11-220.83 135.33a16 16 0 0 0-.27 17.77C82.92 340.8 161.8 400 255.66 400c92.84 0 173.34-59.38 221.79-135.25a16.14 16.14 0 0 0 0-17.47C428.89 172.28 347.8 112 255.66 112z",fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"32"}),li("circle",{cx:"256",cy:"256",r:"80",fill:"none",stroke:"currentColor","stroke-miterlimit":"10","stroke-width":"32"}))}),oT=zn({name:"EyeOff",render:()=>li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},li("path",{d:"M432 448a15.92 15.92 0 0 1-11.31-4.69l-352-352a16 16 0 0 1 22.62-22.62l352 352A16 16 0 0 1 432 448z",fill:"currentColor"}),li("path",{d:"M255.66 384c-41.49 0-81.5-12.28-118.92-36.5c-34.07-22-64.74-53.51-88.7-91v-.08c19.94-28.57 41.78-52.73 65.24-72.21a2 2 0 0 0 .14-2.94L93.5 161.38a2 2 0 0 0-2.71-.12c-24.92 21-48.05 46.76-69.08 76.92a31.92 31.92 0 0 0-.64 35.54c26.41 41.33 60.4 76.14 98.28 100.65C162 402 207.9 416 255.66 416a239.13 239.13 0 0 0 75.8-12.58a2 2 0 0 0 .77-3.31l-21.58-21.58a4 4 0 0 0-3.83-1a204.8 204.8 0 0 1-51.16 6.47z",fill:"currentColor"}),li("path",{d:"M490.84 238.6c-26.46-40.92-60.79-75.68-99.27-100.53C349 110.55 302 96 255.66 96a227.34 227.34 0 0 0-74.89 12.83a2 2 0 0 0-.75 3.31l21.55 21.55a4 4 0 0 0 3.88 1a192.82 192.82 0 0 1 50.21-6.69c40.69 0 80.58 12.43 118.55 37c34.71 22.4 65.74 53.88 89.76 91a.13.13 0 0 1 0 .16a310.72 310.72 0 0 1-64.12 72.73a2 2 0 0 0-.15 2.95l19.9 19.89a2 2 0 0 0 2.7.13a343.49 343.49 0 0 0 68.64-78.48a32.2 32.2 0 0 0-.1-34.78z",fill:"currentColor"}),li("path",{d:"M256 160a95.88 95.88 0 0 0-21.37 2.4a2 2 0 0 0-1 3.38l112.59 112.56a2 2 0 0 0 3.38-1A96 96 0 0 0 256 160z",fill:"currentColor"}),li("path",{d:"M165.78 233.66a2 2 0 0 0-3.38 1a96 96 0 0 0 115 115a2 2 0 0 0 1-3.38z",fill:"currentColor"}))}),rT=zn({name:"Empty",render:()=>li("svg",{viewBox:"0 0 28 28",fill:"none",xmlns:"http://www.w3.org/2000/svg"},li("path",{d:"M26 7.5C26 11.0899 23.0899 14 19.5 14C15.9101 14 13 11.0899 13 7.5C13 3.91015 15.9101 1 19.5 1C23.0899 1 26 3.91015 26 7.5ZM16.8536 4.14645C16.6583 3.95118 16.3417 3.95118 16.1464 4.14645C15.9512 4.34171 15.9512 4.65829 16.1464 4.85355L18.7929 7.5L16.1464 10.1464C15.9512 10.3417 15.9512 10.6583 16.1464 10.8536C16.3417 11.0488 16.6583 11.0488 16.8536 10.8536L19.5 8.20711L22.1464 10.8536C22.3417 11.0488 22.6583 11.0488 22.8536 10.8536C23.0488 10.6583 23.0488 10.3417 22.8536 10.1464L20.2071 7.5L22.8536 4.85355C23.0488 4.65829 23.0488 4.34171 22.8536 4.14645C22.6583 3.95118 22.3417 3.95118 22.1464 4.14645L19.5 6.79289L16.8536 4.14645Z",fill:"currentColor"}),li("path",{d:"M25 22.75V12.5991C24.5572 13.0765 24.053 13.4961 23.5 13.8454V16H17.5L17.3982 16.0068C17.0322 16.0565 16.75 16.3703 16.75 16.75C16.75 18.2688 15.5188 19.5 14 19.5C12.4812 19.5 11.25 18.2688 11.25 16.75L11.2432 16.6482C11.1935 16.2822 10.8797 16 10.5 16H4.5V7.25C4.5 6.2835 5.2835 5.5 6.25 5.5H12.2696C12.4146 4.97463 12.6153 4.47237 12.865 4H6.25C4.45507 4 3 5.45507 3 7.25V22.75C3 24.5449 4.45507 26 6.25 26H21.75C23.5449 26 25 24.5449 25 22.75ZM4.5 22.75V17.5H9.81597L9.85751 17.7041C10.2905 19.5919 11.9808 21 14 21L14.215 20.9947C16.2095 20.8953 17.842 19.4209 18.184 17.5H23.5V22.75C23.5 23.7165 22.7165 24.5 21.75 24.5H6.25C5.2835 24.5 4.5 23.7165 4.5 22.75Z",fill:"currentColor"}))}),iT=QP("error",li("svg",{viewBox:"0 0 48 48",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},li("g",{"fill-rule":"nonzero"},li("path",{d:"M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M17.8838835,16.1161165 L17.7823881,16.0249942 C17.3266086,15.6583353 16.6733914,15.6583353 16.2176119,16.0249942 L16.1161165,16.1161165 L16.0249942,16.2176119 C15.6583353,16.6733914 15.6583353,17.3266086 16.0249942,17.7823881 L16.1161165,17.8838835 L22.233,24 L16.1161165,30.1161165 L16.0249942,30.2176119 C15.6583353,30.6733914 15.6583353,31.3266086 16.0249942,31.7823881 L16.1161165,31.8838835 L16.2176119,31.9750058 C16.6733914,32.3416647 17.3266086,32.3416647 17.7823881,31.9750058 L17.8838835,31.8838835 L24,25.767 L30.1161165,31.8838835 L30.2176119,31.9750058 C30.6733914,32.3416647 31.3266086,32.3416647 31.7823881,31.9750058 L31.8838835,31.8838835 L31.9750058,31.7823881 C32.3416647,31.3266086 32.3416647,30.6733914 31.9750058,30.2176119 L31.8838835,30.1161165 L25.767,24 L31.8838835,17.8838835 L31.9750058,17.7823881 C32.3416647,17.3266086 32.3416647,16.6733914 31.9750058,16.2176119 L31.8838835,16.1161165 L31.7823881,16.0249942 C31.3266086,15.6583353 30.6733914,15.6583353 30.2176119,16.0249942 L30.1161165,16.1161165 L24,22.233 L17.8838835,16.1161165 L17.7823881,16.0249942 L17.8838835,16.1161165 Z"}))))),aT=zn({name:"FastBackward",render:()=>li("svg",{viewBox:"0 0 20 20",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"},li("g",{fill:"currentColor","fill-rule":"nonzero"},li("path",{d:"M8.73171,16.7949 C9.03264,17.0795 9.50733,17.0663 9.79196,16.7654 C10.0766,16.4644 10.0634,15.9897 9.76243,15.7051 L4.52339,10.75 L17.2471,10.75 C17.6613,10.75 17.9971,10.4142 17.9971,10 C17.9971,9.58579 17.6613,9.25 17.2471,9.25 L4.52112,9.25 L9.76243,4.29275 C10.0634,4.00812 10.0766,3.53343 9.79196,3.2325 C9.50733,2.93156 9.03264,2.91834 8.73171,3.20297 L2.31449,9.27241 C2.14819,9.4297 2.04819,9.62981 2.01448,9.8386 C2.00308,9.89058 1.99707,9.94459 1.99707,10 C1.99707,10.0576 2.00356,10.1137 2.01585,10.1675 C2.05084,10.3733 2.15039,10.5702 2.31449,10.7254 L8.73171,16.7949 Z"}))))}),lT=zn({name:"FastForward",render:()=>li("svg",{viewBox:"0 0 20 20",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"},li("g",{fill:"currentColor","fill-rule":"nonzero"},li("path",{d:"M11.2654,3.20511 C10.9644,2.92049 10.4897,2.93371 10.2051,3.23464 C9.92049,3.53558 9.93371,4.01027 10.2346,4.29489 L15.4737,9.25 L2.75,9.25 C2.33579,9.25 2,9.58579 2,10.0000012 C2,10.4142 2.33579,10.75 2.75,10.75 L15.476,10.75 L10.2346,15.7073 C9.93371,15.9919 9.92049,16.4666 10.2051,16.7675 C10.4897,17.0684 10.9644,17.0817 11.2654,16.797 L17.6826,10.7276 C17.8489,10.5703 17.9489,10.3702 17.9826,10.1614 C17.994,10.1094 18,10.0554 18,10.0000012 C18,9.94241 17.9935,9.88633 17.9812,9.83246 C17.9462,9.62667 17.8467,9.42976 17.6826,9.27455 L11.2654,3.20511 Z"}))))}),sT=zn({name:"Filter",render:()=>li("svg",{viewBox:"0 0 28 28",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},li("g",{"fill-rule":"nonzero"},li("path",{d:"M17,19 C17.5522847,19 18,19.4477153 18,20 C18,20.5522847 17.5522847,21 17,21 L11,21 C10.4477153,21 10,20.5522847 10,20 C10,19.4477153 10.4477153,19 11,19 L17,19 Z M21,13 C21.5522847,13 22,13.4477153 22,14 C22,14.5522847 21.5522847,15 21,15 L7,15 C6.44771525,15 6,14.5522847 6,14 C6,13.4477153 6.44771525,13 7,13 L21,13 Z M24,7 C24.5522847,7 25,7.44771525 25,8 C25,8.55228475 24.5522847,9 24,9 L4,9 C3.44771525,9 3,8.55228475 3,8 C3,7.44771525 3.44771525,7 4,7 L24,7 Z"}))))}),cT=zn({name:"Forward",render:()=>li("svg",{viewBox:"0 0 20 20",fill:"none",xmlns:"http://www.w3.org/2000/svg"},li("path",{d:"M7.73271 4.20694C8.03263 3.92125 8.50737 3.93279 8.79306 4.23271L13.7944 9.48318C14.0703 9.77285 14.0703 10.2281 13.7944 10.5178L8.79306 15.7682C8.50737 16.0681 8.03263 16.0797 7.73271 15.794C7.43279 15.5083 7.42125 15.0336 7.70694 14.7336L12.2155 10.0005L7.70694 5.26729C7.42125 4.96737 7.43279 4.49264 7.73271 4.20694Z",fill:"currentColor"}))}),uT=QP("info",li("svg",{viewBox:"0 0 28 28",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},li("g",{"fill-rule":"nonzero"},li("path",{d:"M14,2 C20.6274,2 26,7.37258 26,14 C26,20.6274 20.6274,26 14,26 C7.37258,26 2,20.6274 2,14 C2,7.37258 7.37258,2 14,2 Z M14,11 C13.4477,11 13,11.4477 13,12 L13,12 L13,20 C13,20.5523 13.4477,21 14,21 C14.5523,21 15,20.5523 15,20 L15,20 L15,12 C15,11.4477 14.5523,11 14,11 Z M14,6.75 C13.3096,6.75 12.75,7.30964 12.75,8 C12.75,8.69036 13.3096,9.25 14,9.25 C14.6904,9.25 15.25,8.69036 15.25,8 C15.25,7.30964 14.6904,6.75 14,6.75 Z"}))))),dT=zn({name:"More",render:()=>li("svg",{viewBox:"0 0 16 16",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"},li("g",{fill:"currentColor","fill-rule":"nonzero"},li("path",{d:"M4,7 C4.55228,7 5,7.44772 5,8 C5,8.55229 4.55228,9 4,9 C3.44772,9 3,8.55229 3,8 C3,7.44772 3.44772,7 4,7 Z M8,7 C8.55229,7 9,7.44772 9,8 C9,8.55229 8.55229,9 8,9 C7.44772,9 7,8.55229 7,8 C7,7.44772 7.44772,7 8,7 Z M12,7 C12.5523,7 13,7.44772 13,8 C13,8.55229 12.5523,9 12,9 C11.4477,9 11,8.55229 11,8 C11,7.44772 11.4477,7 12,7 Z"}))))}),pT=zn({name:"Remove",render:()=>li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 512 512"},li("line",{x1:"400",y1:"256",x2:"112",y2:"256",style:"\n fill: none;\n stroke: currentColor;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: 32px;\n "}))}),hT=QP("success",li("svg",{viewBox:"0 0 48 48",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},li("g",{"fill-rule":"nonzero"},li("path",{d:"M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M32.6338835,17.6161165 C32.1782718,17.1605048 31.4584514,17.1301307 30.9676119,17.5249942 L30.8661165,17.6161165 L20.75,27.732233 L17.1338835,24.1161165 C16.6457281,23.6279612 15.8542719,23.6279612 15.3661165,24.1161165 C14.9105048,24.5717282 14.8801307,25.2915486 15.2749942,25.7823881 L15.3661165,25.8838835 L19.8661165,30.3838835 C20.3217282,30.8394952 21.0415486,30.8698693 21.5323881,30.4750058 L21.6338835,30.3838835 L32.6338835,19.3838835 C33.1220388,18.8957281 33.1220388,18.1042719 32.6338835,17.6161165 Z"}))))),fT=QP("warning",li("svg",{viewBox:"0 0 24 24",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},li("g",{"fill-rule":"nonzero"},li("path",{d:"M12,2 C17.523,2 22,6.478 22,12 C22,17.522 17.523,22 12,22 C6.477,22 2,17.522 2,12 C2,6.478 6.477,2 12,2 Z M12.0018002,15.0037242 C11.450254,15.0037242 11.0031376,15.4508407 11.0031376,16.0023869 C11.0031376,16.553933 11.450254,17.0010495 12.0018002,17.0010495 C12.5533463,17.0010495 13.0004628,16.553933 13.0004628,16.0023869 C13.0004628,15.4508407 12.5533463,15.0037242 12.0018002,15.0037242 Z M11.99964,7 C11.4868042,7.00018474 11.0642719,7.38637706 11.0066858,7.8837365 L11,8.00036004 L11.0018003,13.0012393 L11.00857,13.117858 C11.0665141,13.6151758 11.4893244,14.0010638 12.0021602,14.0008793 C12.514996,14.0006946 12.9375283,13.6145023 12.9951144,13.1171428 L13.0018002,13.0005193 L13,7.99964009 L12.9932303,7.8830214 C12.9352861,7.38570354 12.5124758,6.99981552 11.99964,7 Z"}))))),vT=zn({name:"ChevronDown",render:()=>li("svg",{viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},li("path",{d:"M3.14645 5.64645C3.34171 5.45118 3.65829 5.45118 3.85355 5.64645L8 9.79289L12.1464 5.64645C12.3417 5.45118 12.6583 5.45118 12.8536 5.64645C13.0488 5.84171 13.0488 6.15829 12.8536 6.35355L8.35355 10.8536C8.15829 11.0488 7.84171 11.0488 7.64645 10.8536L3.14645 6.35355C2.95118 6.15829 2.95118 5.84171 3.14645 5.64645Z",fill:"currentColor"}))}),mT=QP("clear",li("svg",{viewBox:"0 0 16 16",version:"1.1",xmlns:"http://www.w3.org/2000/svg"},li("g",{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"},li("g",{fill:"currentColor","fill-rule":"nonzero"},li("path",{d:"M8,2 C11.3137085,2 14,4.6862915 14,8 C14,11.3137085 11.3137085,14 8,14 C4.6862915,14 2,11.3137085 2,8 C2,4.6862915 4.6862915,2 8,2 Z M6.5343055,5.83859116 C6.33943736,5.70359511 6.07001296,5.72288026 5.89644661,5.89644661 L5.89644661,5.89644661 L5.83859116,5.9656945 C5.70359511,6.16056264 5.72288026,6.42998704 5.89644661,6.60355339 L5.89644661,6.60355339 L7.293,8 L5.89644661,9.39644661 L5.83859116,9.4656945 C5.70359511,9.66056264 5.72288026,9.92998704 5.89644661,10.1035534 L5.89644661,10.1035534 L5.9656945,10.1614088 C6.16056264,10.2964049 6.42998704,10.2771197 6.60355339,10.1035534 L6.60355339,10.1035534 L8,8.707 L9.39644661,10.1035534 L9.4656945,10.1614088 C9.66056264,10.2964049 9.92998704,10.2771197 10.1035534,10.1035534 L10.1035534,10.1035534 L10.1614088,10.0343055 C10.2964049,9.83943736 10.2771197,9.57001296 10.1035534,9.39644661 L10.1035534,9.39644661 L8.707,8 L10.1035534,6.60355339 L10.1614088,6.5343055 C10.2964049,6.33943736 10.2771197,6.07001296 10.1035534,5.89644661 L10.1035534,5.89644661 L10.0343055,5.83859116 C9.83943736,5.70359511 9.57001296,5.72288026 9.39644661,5.89644661 L9.39644661,5.89644661 L8,7.293 L6.60355339,5.89644661 Z"}))))),gT=zn({name:"ChevronDownFilled",render:()=>li("svg",{viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg"},li("path",{d:"M3.20041 5.73966C3.48226 5.43613 3.95681 5.41856 4.26034 5.70041L8 9.22652L11.7397 5.70041C12.0432 5.41856 12.5177 5.43613 12.7996 5.73966C13.0815 6.0432 13.0639 6.51775 12.7603 6.7996L8.51034 10.7996C8.22258 11.0668 7.77743 11.0668 7.48967 10.7996L3.23966 6.7996C2.93613 6.51775 2.91856 6.0432 3.20041 5.73966Z",fill:"currentColor"}))}),bT=zn({name:"BaseIconSwitchTransition",setup(e,{slots:t}){const n=$b();return()=>li(vi,{name:"icon-switch-transition",appear:n.value},t)}}),yT=zn({name:"FadeInExpandTransition",props:{appear:Boolean,group:Boolean,mode:String,onLeave:Function,onAfterLeave:Function,onAfterEnter:Function,width:Boolean,reverse:Boolean},setup(e,{slots:t}){function n(t){e.width?t.style.maxWidth=`${t.offsetWidth}px`:t.style.maxHeight=`${t.offsetHeight}px`,t.offsetWidth}function o(t){e.width?t.style.maxWidth="0":t.style.maxHeight="0",t.offsetWidth;const{onLeave:n}=e;n&&n()}function r(t){e.width?t.style.maxWidth="":t.style.maxHeight="";const{onAfterLeave:n}=e;n&&n()}function i(t){if(t.style.transition="none",e.width){const e=t.offsetWidth;t.style.maxWidth="0",t.offsetWidth,t.style.transition="",t.style.maxWidth=`${e}px`}else if(e.reverse)t.style.maxHeight=`${t.offsetHeight}px`,t.offsetHeight,t.style.transition="",t.style.maxHeight="0";else{const e=t.offsetHeight;t.style.maxHeight="0",t.offsetWidth,t.style.transition="",t.style.maxHeight=`${e}px`}t.offsetWidth}function a(t){var n;e.width?t.style.maxWidth="":e.reverse||(t.style.maxHeight=""),null===(n=e.onAfterEnter)||void 0===n||n.call(e)}return()=>{const{group:l,width:s,appear:c,mode:u}=e,d=l?ta:vi,p={name:s?"fade-in-width-expand-transition":"fade-in-height-expand-transition",appear:c,onEnter:i,onAfterEnter:a,onBeforeLeave:n,onLeave:o,onAfterLeave:r};return l||(p.mode=u),li(d,p,t)}}}),xT=nb("base-icon","\n height: 1em;\n width: 1em;\n line-height: 1em;\n text-align: center;\n display: inline-block;\n position: relative;\n fill: currentColor;\n transform: translateZ(0);\n",[eb("svg","\n height: 1em;\n width: 1em;\n ")]),CT=zn({name:"BaseIcon",props:{role:String,ariaLabel:String,ariaDisabled:{type:Boolean,default:void 0},ariaHidden:{type:Boolean,default:void 0},clsPrefix:{type:String,required:!0},onClick:Function,onMousedown:Function,onMouseup:Function},setup(e){qP("-base-icon",xT,jt(e,"clsPrefix"))},render(){return li("i",{class:`${this.clsPrefix}-base-icon`,onClick:this.onClick,onMousedown:this.onMousedown,onMouseup:this.onMouseup,role:this.role,"aria-label":this.ariaLabel,"aria-hidden":this.ariaHidden,"aria-disabled":this.ariaDisabled},this.$slots)}}),wT=nb("base-close","\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n background-color: transparent;\n color: var(--n-close-icon-color);\n border-radius: var(--n-close-border-radius);\n height: var(--n-close-size);\n width: var(--n-close-size);\n font-size: var(--n-close-icon-size);\n outline: none;\n border: none;\n position: relative;\n padding: 0;\n",[rb("absolute","\n height: var(--n-close-icon-size);\n width: var(--n-close-icon-size);\n "),eb("&::before",'\n content: "";\n position: absolute;\n width: var(--n-close-size);\n height: var(--n-close-size);\n left: 50%;\n top: 50%;\n transform: translateY(-50%) translateX(-50%);\n transition: inherit;\n border-radius: inherit;\n '),ib("disabled",[eb("&:hover","\n color: var(--n-close-icon-color-hover);\n "),eb("&:hover::before","\n background-color: var(--n-close-color-hover);\n "),eb("&:focus::before","\n background-color: var(--n-close-color-hover);\n "),eb("&:active","\n color: var(--n-close-icon-color-pressed);\n "),eb("&:active::before","\n background-color: var(--n-close-color-pressed);\n ")]),rb("disabled","\n cursor: not-allowed;\n color: var(--n-close-icon-color-disabled);\n background-color: transparent;\n "),rb("round",[eb("&::before","\n border-radius: 50%;\n ")])]),kT=zn({name:"BaseClose",props:{isButtonTag:{type:Boolean,default:!0},clsPrefix:{type:String,required:!0},disabled:{type:Boolean,default:void 0},focusable:{type:Boolean,default:!0},round:Boolean,onClick:Function,absolute:Boolean},setup:e=>(qP("-base-close",wT,jt(e,"clsPrefix")),()=>{const{clsPrefix:t,disabled:n,absolute:o,round:r,isButtonTag:i}=e;return li(i?"button":"div",{type:i?"button":void 0,tabindex:n||!e.focusable?-1:0,"aria-disabled":n,"aria-label":"close",role:i?void 0:"button",disabled:n,class:[`${t}-base-close`,o&&`${t}-base-close--absolute`,n&&`${t}-base-close--disabled`,r&&`${t}-base-close--round`],onMousedown:t=>{e.focusable||t.preventDefault()},onClick:e.onClick},li(CT,{clsPrefix:t},{default:()=>li(tT,null)}))})}),ST=zn({props:{onFocus:Function,onBlur:Function},setup:e=>()=>li("div",{style:"width: 0; height: 0",tabindex:0,onFocus:e.onFocus,onBlur:e.onBlur})}),{cubicBezierEaseInOut:_T}=A_;function PT({originalTransform:e="",left:t=0,top:n=0,transition:o=`all .3s ${_T} !important`}={}){return[eb("&.icon-switch-transition-enter-from, &.icon-switch-transition-leave-to",{transform:`${e} scale(0.75)`,left:t,top:n,opacity:0}),eb("&.icon-switch-transition-enter-to, &.icon-switch-transition-leave-from",{transform:`scale(1) ${e}`,left:t,top:n,opacity:1}),eb("&.icon-switch-transition-enter-active, &.icon-switch-transition-leave-active",{transformOrigin:"center",position:"absolute",left:t,top:n,transition:o})]}const TT=eb([eb("@keyframes rotator","\n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg);\n }\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }"),nb("base-loading","\n position: relative;\n line-height: 0;\n width: 1em;\n height: 1em;\n ",[ob("transition-wrapper","\n position: absolute;\n width: 100%;\n height: 100%;\n ",[PT()]),ob("placeholder","\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translateX(-50%) translateY(-50%);\n ",[PT({left:"50%",top:"50%",originalTransform:"translateX(-50%) translateY(-50%)"})]),ob("container","\n animation: rotator 3s linear infinite both;\n ",[ob("icon","\n height: 1em;\n width: 1em;\n ")])])]),AT="1.6s",zT={strokeWidth:{type:Number,default:28},stroke:{type:String,default:void 0}},RT=zn({name:"BaseLoading",props:Object.assign({clsPrefix:{type:String,required:!0},show:{type:Boolean,default:!0},scale:{type:Number,default:1},radius:{type:Number,default:100}},zT),setup(e){qP("-base-loading",TT,jt(e,"clsPrefix"))},render(){const{clsPrefix:e,radius:t,strokeWidth:n,stroke:o,scale:r}=this,i=t/r;return li("div",{class:`${e}-base-loading`,role:"img","aria-label":"loading"},li(bT,null,{default:()=>this.show?li("div",{key:"icon",class:`${e}-base-loading__transition-wrapper`},li("div",{class:`${e}-base-loading__container`},li("svg",{class:`${e}-base-loading__icon`,viewBox:`0 0 ${2*i} ${2*i}`,xmlns:"http://www.w3.org/2000/svg",style:{color:o}},li("g",null,li("animateTransform",{attributeName:"transform",type:"rotate",values:`0 ${i} ${i};270 ${i} ${i}`,begin:"0s",dur:AT,fill:"freeze",repeatCount:"indefinite"}),li("circle",{class:`${e}-base-loading__icon`,fill:"none",stroke:"currentColor","stroke-width":n,"stroke-linecap":"round",cx:i,cy:i,r:t-n/2,"stroke-dasharray":5.67*t,"stroke-dashoffset":18.48*t},li("animateTransform",{attributeName:"transform",type:"rotate",values:`0 ${i} ${i};135 ${i} ${i};450 ${i} ${i}`,begin:"0s",dur:AT,fill:"freeze",repeatCount:"indefinite"}),li("animate",{attributeName:"stroke-dashoffset",values:`${5.67*t};${1.42*t};${5.67*t}`,begin:"0s",dur:AT,fill:"freeze",repeatCount:"indefinite"})))))):li("div",{key:"placeholder",class:`${e}-base-loading__placeholder`},this.$slots)}))}});function ET(e){return Array.isArray(e)?e:[e]}const OT="STOP";function MT(e,t){const n=t(e);void 0!==e.children&&n!==OT&&e.children.forEach((e=>MT(e,t)))}function FT(e){return e.children}function IT(e){return e.key}function LT(){return!1}function BT(e){return!0===e.disabled}function DT(e){var t;return null==e?[]:Array.isArray(e)?e:null!==(t=e.checkedKeys)&&void 0!==t?t:[]}function $T(e){var t;return null==e||Array.isArray(e)?[]:null!==(t=e.indeterminateKeys)&&void 0!==t?t:[]}function NT(e,t){const n=new Set(e);return t.forEach((e=>{n.has(e)||n.add(e)})),Array.from(n)}function jT(e,t){const n=new Set(e);return t.forEach((e=>{n.has(e)&&n.delete(e)})),Array.from(n)}function HT(e){return"group"===(null==e?void 0:e.type)}class WT extends Error{constructor(){super(),this.message="SubtreeNotLoadedError: checking a subtree whose required nodes are not fully loaded."}}function UT(e,t,n,o){const r=qT(t,n,o,!1),i=qT(e,n,o,!0),a=function(e,t){const n=new Set;return e.forEach((e=>{const o=t.treeNodeMap.get(e);if(void 0!==o){let e=o.parent;for(;null!==e&&!e.disabled&&!n.has(e.key);)n.add(e.key),e=e.parent}})),n}(e,n),l=[];return r.forEach((e=>{(i.has(e)||a.has(e))&&l.push(e)})),l.forEach((e=>r.delete(e))),r}function VT(e,t){const{checkedKeys:n,keysToCheck:o,keysToUncheck:r,indeterminateKeys:i,cascade:a,leafOnly:l,checkStrategy:s,allowNotLoaded:c}=e;if(!a)return void 0!==o?{checkedKeys:NT(n,o),indeterminateKeys:Array.from(i)}:void 0!==r?{checkedKeys:jT(n,r),indeterminateKeys:Array.from(i)}:{checkedKeys:Array.from(n),indeterminateKeys:Array.from(i)};const{levelTreeNodeMap:u}=t;let d;d=void 0!==r?UT(r,n,t,c):void 0!==o?function(e,t,n,o){return qT(t.concat(e),n,o,!1)}(o,n,t,c):qT(n,t,c,!1);const p="parent"===s,h="child"===s||l,f=d,v=new Set;for(let m=Math.max.apply(null,Array.from(u.keys()));m>=0;m-=1){const e=0===m,t=u.get(m);for(const n of t){if(n.isLeaf)continue;const{key:t,shallowLoaded:o}=n;if(h&&o&&n.children.forEach((e=>{!e.disabled&&!e.isLeaf&&e.shallowLoaded&&f.has(e.key)&&f.delete(e.key)})),n.disabled||!o)continue;let r=!0,i=!1,a=!0;for(const e of n.children){const t=e.key;if(!e.disabled)if(a&&(a=!1),f.has(t))i=!0;else{if(v.has(t)){i=!0,r=!1;break}if(r=!1,i)break}}r&&!a?(p&&n.children.forEach((e=>{!e.disabled&&f.has(e.key)&&f.delete(e.key)})),f.add(t)):i&&v.add(t),e&&h&&f.has(t)&&f.delete(t)}}return{checkedKeys:Array.from(f),indeterminateKeys:Array.from(v)}}function qT(e,t,n,o){const{treeNodeMap:r,getChildren:i}=t,a=new Set,l=new Set(e);return e.forEach((e=>{const t=r.get(e);void 0!==t&&MT(t,(e=>{if(e.disabled)return OT;const{key:t}=e;if(!a.has(t)&&(a.add(t),l.add(t),function(e,t){return!1===e.isLeaf&&!Array.isArray(t(e))}(e.rawNode,i))){if(o)return OT;if(!n)throw new WT}}))})),l}function KT(e,t){const n=e.siblings,o=n.length,{index:r}=e;return t?n[(r+1)%o]:r===n.length-1?null:n[r+1]}function GT(e,t,{loop:n=!1,includeDisabled:o=!1}={}){const r="prev"===t?XT:KT,i={reverse:"prev"===t};let a=!1,l=null;return function t(s){if(null!==s){if(s===e)if(a){if(!e.disabled&&!e.isGroup)return void(l=e)}else a=!0;else if((!s.disabled||o)&&!s.ignored&&!s.isGroup)return void(l=s);if(s.isGroup){const e=YT(s,i);null!==e?l=e:t(r(s,n))}else{const e=r(s,!1);if(null!==e)t(e);else{const e=function(e){return e.parent}(s);(null==e?void 0:e.isGroup)?t(r(e,n)):n&&t(r(s,!0))}}}}(e),l}function XT(e,t){const n=e.siblings,o=n.length,{index:r}=e;return t?n[(r-1+o)%o]:0===r?null:n[r-1]}function YT(e,t={}){const{reverse:n=!1}=t,{children:o}=e;if(o){const{length:e}=o,r=n?-1:e,i=n?-1:1;for(let a=n?e-1:0;a!==r;a+=i){const e=o[a];if(!e.disabled&&!e.ignored){if(!e.isGroup)return e;{const n=YT(e,t);if(null!==n)return n}}}}return null}const QT={getChild(){return this.ignored?null:YT(this)},getParent(){const{parent:e}=this;return(null==e?void 0:e.isGroup)?e.getParent():e},getNext(e={}){return GT(this,"next",e)},getPrev(e={}){return GT(this,"prev",e)}};function ZT(e,t,n,o,r,i=null,a=0){const l=[];return e.forEach(((s,c)=>{var u;const d=Object.create(o);if(d.rawNode=s,d.siblings=l,d.level=a,d.index=c,d.isFirstChild=0===c,d.isLastChild=c+1===e.length,d.parent=i,!d.ignored){const e=r(s);Array.isArray(e)&&(d.children=ZT(e,t,n,o,r,d,a+1))}l.push(d),t.set(d.key,d),n.has(a)||n.set(a,[]),null===(u=n.get(a))||void 0===u||u.push(d)})),l}function JT(e,t={}){var n;const o=new Map,r=new Map,{getDisabled:i=BT,getIgnored:a=LT,getIsGroup:l=HT,getKey:s=IT}=t,c=null!==(n=t.getChildren)&&void 0!==n?n:FT,u=t.ignoreEmptyChildren?e=>{const t=c(e);return Array.isArray(t)?t.length?t:null:t}:c,d=Object.assign({get key(){return s(this.rawNode)},get disabled(){return i(this.rawNode)},get isGroup(){return l(this.rawNode)},get isLeaf(){return function(e,t){const{isLeaf:n}=e;return void 0!==n?n:!t(e)}(this.rawNode,u)},get shallowLoaded(){return function(e,t){const{isLeaf:n}=e;return!(!1===n&&!Array.isArray(t(e)))}(this.rawNode,u)},get ignored(){return a(this.rawNode)},contains(e){return function(e,t){const n=e.key;for(;t;){if(t.key===n)return!0;t=t.parent}return!1}(this,e)}},QT),p=ZT(e,o,r,d,u);function h(e){if(null==e)return null;const t=o.get(e);return t&&!t.ignored?t:null}const f={treeNodes:p,treeNodeMap:o,levelTreeNodeMap:r,maxLevel:Math.max(...r.keys()),getChildren:u,getFlattenedNodes:e=>function(e,t){const n=t?new Set(t):void 0,o=[];return function e(t){t.forEach((t=>{o.push(t),t.isLeaf||!t.children||t.ignored||(t.isGroup||void 0===n||n.has(t.key))&&e(t.children)}))}(e),o}(p,e),getNode:function(e){if(null==e)return null;const t=o.get(e);return!t||t.isGroup||t.ignored?null:t},getPrev:function(e,t){const n=h(e);return n?n.getPrev(t):null},getNext:function(e,t){const n=h(e);return n?n.getNext(t):null},getParent:function(e){const t=h(e);return t?t.getParent():null},getChild:function(e){const t=h(e);return t?t.getChild():null},getFirstAvailableNode:()=>function(e){if(0===e.length)return null;const t=e[0];return t.isGroup||t.ignored||t.disabled?t.getNext():t}(p),getPath:(e,t={})=>function(e,{includeGroup:t=!1,includeSelf:n=!0},o){var r;const i=o.treeNodeMap;let a=null==e?null:null!==(r=i.get(e))&&void 0!==r?r:null;const l={keyPath:[],treeNodePath:[],treeNode:a};if(null==a?void 0:a.ignored)return l.treeNode=null,l;for(;a;)a.ignored||!t&&a.isGroup||l.treeNodePath.push(a),a=a.parent;return l.treeNodePath.reverse(),n||l.treeNodePath.pop(),l.keyPath=l.treeNodePath.map((e=>e.key)),l}(e,t,f),getCheckedKeys(e,t={}){const{cascade:n=!0,leafOnly:o=!1,checkStrategy:r="all",allowNotLoaded:i=!1}=t;return VT({checkedKeys:DT(e),indeterminateKeys:$T(e),cascade:n,leafOnly:o,checkStrategy:r,allowNotLoaded:i},f)},check(e,t,n={}){const{cascade:o=!0,leafOnly:r=!1,checkStrategy:i="all",allowNotLoaded:a=!1}=n;return VT({checkedKeys:DT(t),indeterminateKeys:$T(t),keysToCheck:null==e?[]:ET(e),cascade:o,leafOnly:r,checkStrategy:i,allowNotLoaded:a},f)},uncheck(e,t,n={}){const{cascade:o=!0,leafOnly:r=!1,checkStrategy:i="all",allowNotLoaded:a=!1}=n;return VT({checkedKeys:DT(t),indeterminateKeys:$T(t),keysToUncheck:null==e?[]:ET(e),cascade:o,leafOnly:r,checkStrategy:i,allowNotLoaded:a},f)},getNonLeafKeys:(e={})=>function(e,t={}){const{preserveGroup:n=!1}=t,o=[],r=n?e=>{e.isLeaf||(o.push(e.key),i(e.children))}:e=>{e.isLeaf||(e.isGroup||o.push(e.key),i(e.children))};function i(e){e.forEach(r)}return i(e),o}(p,e)};return f}const eA="#000",tA="#fff",nA="#fff",oA="rgb(72, 72, 78)",rA="rgb(24, 24, 28)",iA="rgb(44, 44, 50)",aA="rgb(16, 16, 20)",lA="0.9",sA="0.82",cA="0.52",uA="0.38",dA="0.28",pA="0.52",hA="0.38",fA="0.06",vA="0.09",mA="0.06",gA="0.05",bA="0.05",yA="0.18",xA="0.2",CA="0.12",wA="0.24",kA="0.09",SA="0.1",_A="0.06",PA="0.04",TA="0.2",AA="0.3",zA="0.12",RA="0.2",EA="#7fe7c4",OA="#63e2b7",MA="#5acea7",FA="rgb(42, 148, 125)",IA="#8acbec",LA="#70c0e8",BA="#66afd3",DA="rgb(56, 137, 197)",$A="#e98b8b",NA="#e88080",jA="#e57272",HA="rgb(208, 58, 82)",WA="#f5d599",UA="#f2c97d",VA="#e6c260",qA="rgb(240, 138, 0)",KA="#7fe7c4",GA="#63e2b7",XA="#5acea7",YA="rgb(42, 148, 125)",QA=Qm(eA),ZA=Qm(tA),JA=`rgba(${ZA.slice(0,3).join(", ")}, `;function ez(e){return`${JA+String(e)})`}const tz=Object.assign(Object.assign({name:"common"},A_),{baseColor:eA,primaryColor:OA,primaryColorHover:EA,primaryColorPressed:MA,primaryColorSuppl:FA,infoColor:LA,infoColorHover:IA,infoColorPressed:BA,infoColorSuppl:DA,successColor:GA,successColorHover:KA,successColorPressed:XA,successColorSuppl:YA,warningColor:UA,warningColorHover:WA,warningColorPressed:VA,warningColorSuppl:qA,errorColor:NA,errorColorHover:$A,errorColorPressed:jA,errorColorSuppl:HA,textColorBase:nA,textColor1:ez(lA),textColor2:ez(sA),textColor3:ez(cA),textColorDisabled:ez(uA),placeholderColor:ez(uA),placeholderColorDisabled:ez(dA),iconColor:ez(uA),iconColorDisabled:ez(dA),iconColorHover:ez(1.25*Number(uA)),iconColorPressed:ez(.8*Number(uA)),opacity1:lA,opacity2:sA,opacity3:cA,opacity4:uA,opacity5:dA,dividerColor:ez(kA),borderColor:ez(wA),closeIconColorHover:ez(Number(pA)),closeIconColor:ez(Number(pA)),closeIconColorPressed:ez(Number(pA)),closeColorHover:"rgba(255, 255, 255, .12)",closeColorPressed:"rgba(255, 255, 255, .08)",clearColor:ez(uA),clearColorHover:ng(ez(uA),{alpha:1.25}),clearColorPressed:ng(ez(uA),{alpha:.8}),scrollbarColor:ez(TA),scrollbarColorHover:ez(AA),scrollbarWidth:"5px",scrollbarHeight:"5px",scrollbarBorderRadius:"5px",progressRailColor:ez(CA),railColor:ez(xA),popoverColor:oA,tableColor:rA,cardColor:rA,modalColor:iA,bodyColor:aA,tagColor:function(e){const t=Array.from(ZA);return t[3]=Number(e),eg(QA,t)}(RA),avatarColor:ez(yA),invertedColor:eA,inputColor:ez(SA),codeColor:ez(zA),tabColor:ez(PA),actionColor:ez(_A),tableHeaderColor:ez(_A),hoverColor:ez(vA),tableColorHover:ez(mA),tableColorStriped:ez(gA),pressedColor:ez(bA),opacityDisabled:hA,inputColorDisabled:ez(fA),buttonColor2:"rgba(255, 255, 255, .08)",buttonColor2Hover:"rgba(255, 255, 255, .12)",buttonColor2Pressed:"rgba(255, 255, 255, .08)",boxShadow1:"0 1px 2px -2px rgba(0, 0, 0, .24), 0 3px 6px 0 rgba(0, 0, 0, .18), 0 5px 12px 4px rgba(0, 0, 0, .12)",boxShadow2:"0 3px 6px -4px rgba(0, 0, 0, .24), 0 6px 12px 0 rgba(0, 0, 0, .16), 0 9px 18px 8px rgba(0, 0, 0, .10)",boxShadow3:"0 6px 16px -9px rgba(0, 0, 0, .08), 0 9px 28px 0 rgba(0, 0, 0, .05), 0 12px 48px 16px rgba(0, 0, 0, .03)"}),nz="#FFF",oz="#000",rz="#000",iz="#fff",az="#fff",lz="#fff",sz="#fff",cz="0.82",uz="0.72",dz="0.38",pz="0.24",hz="0.18",fz="0.6",vz="0.5",mz="0.2",gz=".08",bz="0",yz="0.25",xz="0.4",Cz="#36ad6a",wz="#18a058",kz="#0c7a43",Sz="#36ad6a",_z="#4098fc",Pz="#2080f0",Tz="#1060c9",Az="#4098fc",zz="#de576d",Rz="#d03050",Ez="#ab1f3f",Oz="#de576d",Mz="#fcb040",Fz="#f0a020",Iz="#c97c10",Lz="#fcb040",Bz="#36ad6a",Dz="#18a058",$z="#0c7a43",Nz="#36ad6a",jz=Qm(nz),Hz=Qm(oz),Wz=`rgba(${Hz.slice(0,3).join(", ")}, `;function Uz(e){return`${Wz+String(e)})`}function Vz(e){const t=Array.from(Hz);return t[3]=Number(e),eg(jz,t)}const qz=Object.assign(Object.assign({name:"common"},A_),{baseColor:nz,primaryColor:wz,primaryColorHover:Cz,primaryColorPressed:kz,primaryColorSuppl:Sz,infoColor:Pz,infoColorHover:_z,infoColorPressed:Tz,infoColorSuppl:Az,successColor:Dz,successColorHover:Bz,successColorPressed:$z,successColorSuppl:Nz,warningColor:Fz,warningColorHover:Mz,warningColorPressed:Iz,warningColorSuppl:Lz,errorColor:Rz,errorColorHover:zz,errorColorPressed:Ez,errorColorSuppl:Oz,textColorBase:rz,textColor1:"rgb(31, 34, 37)",textColor2:"rgb(51, 54, 57)",textColor3:"rgb(118, 124, 130)",textColorDisabled:Vz(pz),placeholderColor:Vz(pz),placeholderColorDisabled:Vz(hz),iconColor:Vz(pz),iconColorHover:ng(Vz(pz),{lightness:.75}),iconColorPressed:ng(Vz(pz),{lightness:.9}),iconColorDisabled:Vz(hz),opacity1:cz,opacity2:uz,opacity3:dz,opacity4:pz,opacity5:hz,dividerColor:"rgb(239, 239, 245)",borderColor:"rgb(224, 224, 230)",closeIconColor:Vz(Number(fz)),closeIconColorHover:Vz(Number(fz)),closeIconColorPressed:Vz(Number(fz)),closeColorHover:"rgba(0, 0, 0, .09)",closeColorPressed:"rgba(0, 0, 0, .13)",clearColor:Vz(pz),clearColorHover:ng(Vz(pz),{lightness:.75}),clearColorPressed:ng(Vz(pz),{lightness:.9}),scrollbarColor:Uz(yz),scrollbarColorHover:Uz(xz),scrollbarWidth:"5px",scrollbarHeight:"5px",scrollbarBorderRadius:"5px",progressRailColor:Vz(gz),railColor:"rgb(219, 219, 223)",popoverColor:iz,tableColor:az,cardColor:az,modalColor:lz,bodyColor:sz,tagColor:"#eee",avatarColor:Vz(mz),invertedColor:"rgb(0, 20, 40)",inputColor:Vz(bz),codeColor:"rgb(244, 244, 248)",tabColor:"rgb(247, 247, 250)",actionColor:"rgb(250, 250, 252)",tableHeaderColor:"rgb(250, 250, 252)",hoverColor:"rgb(243, 243, 245)",tableColorHover:"rgba(0, 0, 100, 0.03)",tableColorStriped:"rgba(0, 0, 100, 0.02)",pressedColor:"rgb(237, 237, 239)",opacityDisabled:vz,inputColorDisabled:"rgb(250, 250, 252)",buttonColor2:"rgba(46, 51, 56, .05)",buttonColor2Hover:"rgba(46, 51, 56, .09)",buttonColor2Pressed:"rgba(46, 51, 56, .13)",boxShadow1:"0 1px 2px -2px rgba(0, 0, 0, .08), 0 3px 6px 0 rgba(0, 0, 0, .06), 0 5px 12px 4px rgba(0, 0, 0, .04)",boxShadow2:"0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05)",boxShadow3:"0 6px 16px -9px rgba(0, 0, 0, .08), 0 9px 28px 0 rgba(0, 0, 0, .05), 0 12px 48px 16px rgba(0, 0, 0, .03)"}),Kz={iconSizeSmall:"34px",iconSizeMedium:"40px",iconSizeLarge:"46px",iconSizeHuge:"52px"};function Gz(e){const{textColorDisabled:t,iconColor:n,textColor2:o,fontSizeSmall:r,fontSizeMedium:i,fontSizeLarge:a,fontSizeHuge:l}=e;return Object.assign(Object.assign({},Kz),{fontSizeSmall:r,fontSizeMedium:i,fontSizeLarge:a,fontSizeHuge:l,textColor:t,iconColor:n,extraTextColor:o})}const Xz={name:"Empty",common:qz,self:Gz},Yz={name:"Empty",common:tz,self:Gz},Qz=nb("empty","\n display: flex;\n flex-direction: column;\n align-items: center;\n font-size: var(--n-font-size);\n",[ob("icon","\n width: var(--n-icon-size);\n height: var(--n-icon-size);\n font-size: var(--n-icon-size);\n line-height: var(--n-icon-size);\n color: var(--n-icon-color);\n transition:\n color .3s var(--n-bezier);\n ",[eb("+",[ob("description","\n margin-top: 8px;\n ")])]),ob("description","\n transition: color .3s var(--n-bezier);\n color: var(--n-text-color);\n "),ob("extra","\n text-align: center;\n transition: color .3s var(--n-bezier);\n margin-top: 12px;\n color: var(--n-extra-text-color);\n ")]),Zz=zn({name:"Empty",props:Object.assign(Object.assign({},I_.props),{description:String,showDescription:{type:Boolean,default:!0},showIcon:{type:Boolean,default:!0},size:{type:String,default:"medium"},renderIcon:Function}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Empty","-empty",Qz,Xz,e,t),{localeRef:r}=VP("Empty"),i=Po(M_,null),a=ai((()=>{var t,n,o;return null!==(t=e.description)&&void 0!==t?t:null===(o=null===(n=null==i?void 0:i.mergedComponentPropsRef.value)||void 0===n?void 0:n.Empty)||void 0===o?void 0:o.description})),l=ai((()=>{var e,t;return(null===(t=null===(e=null==i?void 0:i.mergedComponentPropsRef.value)||void 0===e?void 0:e.Empty)||void 0===t?void 0:t.renderIcon)||(()=>li(rT,null))})),s=ai((()=>{const{size:t}=e,{common:{cubicBezierEaseInOut:n},self:{[ub("iconSize",t)]:r,[ub("fontSize",t)]:i,textColor:a,iconColor:l,extraTextColor:s}}=o.value;return{"--n-icon-size":r,"--n-font-size":i,"--n-bezier":n,"--n-text-color":a,"--n-icon-color":l,"--n-extra-text-color":s}})),c=n?KP("empty",ai((()=>{let t="";const{size:n}=e;return t+=n[0],t})),s,e):void 0;return{mergedClsPrefix:t,mergedRenderIcon:l,localizedDescription:ai((()=>a.value||r.value.description)),cssVars:n?void 0:s,themeClass:null==c?void 0:c.themeClass,onRender:null==c?void 0:c.onRender}},render(){const{$slots:e,mergedClsPrefix:t,onRender:n}=this;return null==n||n(),li("div",{class:[`${t}-empty`,this.themeClass],style:this.cssVars},this.showIcon?li("div",{class:`${t}-empty__icon`},e.icon?e.icon():li(CT,{clsPrefix:t},{default:this.mergedRenderIcon})):null,this.showDescription?li("div",{class:`${t}-empty__description`},e.default?e.default():this.localizedDescription):null,e.extra?li("div",{class:`${t}-empty__extra`},e.extra()):null)}}),Jz={railInsetHorizontal:"auto 2px 4px 2px",railInsetVertical:"2px 4px 2px auto",railColor:"transparent"};function eR(e){const{scrollbarColor:t,scrollbarColorHover:n,scrollbarHeight:o,scrollbarWidth:r,scrollbarBorderRadius:i}=e;return Object.assign(Object.assign({},Jz),{height:o,width:r,borderRadius:i,color:t,colorHover:n})}const tR={name:"Scrollbar",common:qz,self:eR},nR={name:"Scrollbar",common:tz,self:eR},{cubicBezierEaseInOut:oR}=A_;function rR({name:e="fade-in",enterDuration:t="0.2s",leaveDuration:n="0.2s",enterCubicBezier:o=oR,leaveCubicBezier:r=oR}={}){return[eb(`&.${e}-transition-enter-active`,{transition:`all ${t} ${o}!important`}),eb(`&.${e}-transition-leave-active`,{transition:`all ${n} ${r}!important`}),eb(`&.${e}-transition-enter-from, &.${e}-transition-leave-to`,{opacity:0}),eb(`&.${e}-transition-leave-from, &.${e}-transition-enter-to`,{opacity:1})]}const iR=nb("scrollbar","\n overflow: hidden;\n position: relative;\n z-index: auto;\n height: 100%;\n width: 100%;\n",[eb(">",[nb("scrollbar-container","\n width: 100%;\n overflow: scroll;\n height: 100%;\n min-height: inherit;\n max-height: inherit;\n scrollbar-width: none;\n ",[eb("&::-webkit-scrollbar, &::-webkit-scrollbar-track-piece, &::-webkit-scrollbar-thumb","\n width: 0;\n height: 0;\n display: none;\n "),eb(">",[nb("scrollbar-content","\n box-sizing: border-box;\n min-width: 100%;\n ")])])]),eb(">, +",[nb("scrollbar-rail","\n position: absolute;\n pointer-events: none;\n user-select: none;\n background: var(--n-scrollbar-rail-color);\n -webkit-user-select: none;\n ",[rb("horizontal","\n inset: var(--n-scrollbar-rail-inset-horizontal);\n height: var(--n-scrollbar-height);\n ",[eb(">",[ob("scrollbar","\n height: var(--n-scrollbar-height);\n border-radius: var(--n-scrollbar-border-radius);\n right: 0;\n ")])]),rb("vertical","\n inset: var(--n-scrollbar-rail-inset-vertical);\n width: var(--n-scrollbar-width);\n ",[eb(">",[ob("scrollbar","\n width: var(--n-scrollbar-width);\n border-radius: var(--n-scrollbar-border-radius);\n bottom: 0;\n ")])]),rb("disabled",[eb(">",[ob("scrollbar","pointer-events: none;")])]),eb(">",[ob("scrollbar","\n z-index: 1;\n position: absolute;\n cursor: pointer;\n pointer-events: all;\n background-color: var(--n-scrollbar-color);\n transition: background-color .2s var(--n-scrollbar-bezier);\n ",[rR(),eb("&:hover","background-color: var(--n-scrollbar-color-hover);")])])])])]),aR=zn({name:"Scrollbar",props:Object.assign(Object.assign({},I_.props),{duration:{type:Number,default:0},scrollable:{type:Boolean,default:!0},xScrollable:Boolean,trigger:{type:String,default:"hover"},useUnifiedContainer:Boolean,triggerDisplayManually:Boolean,container:Function,content:Function,containerClass:String,containerStyle:[String,Object],contentClass:[String,Array],contentStyle:[String,Object],horizontalRailStyle:[String,Object],verticalRailStyle:[String,Object],onScroll:Function,onWheel:Function,onResize:Function,internalOnUpdateScrollLeft:Function,internalHoistYRail:Boolean}),inheritAttrs:!1,setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n,mergedRtlRef:o}=B_(e),r=GP("Scrollbar",o,t),i=Et(null),a=Et(null),l=Et(null),s=Et(null),c=Et(null),u=Et(null),d=Et(null),p=Et(null),h=Et(null),f=Et(null),v=Et(null),m=Et(0),g=Et(0),b=Et(!1),y=Et(!1);let x,C,w=!1,k=!1,S=0,_=0,P=0,T=0;const A=jb,z=I_("Scrollbar","-scrollbar",iR,tR,e,t),R=ai((()=>{const{value:e}=p,{value:t}=u,{value:n}=f;return null===e||null===t||null===n?0:Math.min(e,n*e/t+1.5*Im(z.value.self.width))})),E=ai((()=>`${R.value}px`)),O=ai((()=>{const{value:e}=h,{value:t}=d,{value:n}=v;return null===e||null===t||null===n?0:n*e/t+1.5*Im(z.value.self.height)})),M=ai((()=>`${O.value}px`)),F=ai((()=>{const{value:e}=p,{value:t}=m,{value:n}=u,{value:o}=f;if(null===e||null===n||null===o)return 0;{const r=n-e;return r?t/r*(o-R.value):0}})),I=ai((()=>`${F.value}px`)),L=ai((()=>{const{value:e}=h,{value:t}=g,{value:n}=d,{value:o}=v;if(null===e||null===n||null===o)return 0;{const r=n-e;return r?t/r*(o-O.value):0}})),B=ai((()=>`${L.value}px`)),D=ai((()=>{const{value:e}=p,{value:t}=u;return null!==e&&null!==t&&t>e})),$=ai((()=>{const{value:e}=h,{value:t}=d;return null!==e&&null!==t&&t>e})),N=ai((()=>{const{trigger:t}=e;return"none"===t||b.value})),j=ai((()=>{const{trigger:t}=e;return"none"===t||y.value})),H=ai((()=>{const{container:t}=e;return t?t():a.value})),W=ai((()=>{const{content:t}=e;return t?t():l.value})),U=(t,n)=>{if(!e.scrollable)return;if("number"==typeof t)return void q(t,null!=n?n:0,0,!1,"auto");const{left:o,top:r,index:i,elSize:a,position:l,behavior:s,el:c,debounce:u=!0}=t;void 0===o&&void 0===r||q(null!=o?o:0,null!=r?r:0,0,!1,s),void 0!==c?q(0,c.offsetTop,c.offsetHeight,u,s):void 0!==i&&void 0!==a?q(0,i*a,a,u,s):"bottom"===l?q(0,Number.MAX_SAFE_INTEGER,0,!1,s):"top"===l&&q(0,0,0,!1,s)},V=Qx((()=>{e.container||U({top:m.value,left:g.value})}));function q(e,t,n,o,r){const{value:i}=H;if(i){if(o){const{scrollTop:o,offsetHeight:a}=i;if(t>o)return void(t+n<=o+a||i.scrollTo({left:e,top:t+n-a,behavior:r}))}i.scrollTo({left:e,top:t,behavior:r})}}function K(){void 0!==C&&window.clearTimeout(C),C=window.setTimeout((()=>{y.value=!1}),e.duration),void 0!==x&&window.clearTimeout(x),x=window.setTimeout((()=>{b.value=!1}),e.duration)}function G(){const{value:e}=H;e&&(m.value=e.scrollTop,g.value=e.scrollLeft*((null==r?void 0:r.value)?-1:1))}function X(){const{value:e}=H;e&&(m.value=e.scrollTop,g.value=e.scrollLeft*((null==r?void 0:r.value)?-1:1),p.value=e.offsetHeight,h.value=e.offsetWidth,u.value=e.scrollHeight,d.value=e.scrollWidth);const{value:t}=c,{value:n}=s;t&&(v.value=t.offsetWidth),n&&(f.value=n.offsetHeight)}function Y(){e.scrollable&&(e.useUnifiedContainer?X():(function(){const{value:e}=W;e&&(u.value=e.offsetHeight,d.value=e.offsetWidth);const{value:t}=H;t&&(p.value=t.offsetHeight,h.value=t.offsetWidth);const{value:n}=c,{value:o}=s;n&&(v.value=n.offsetWidth),o&&(f.value=o.offsetHeight)}(),G()))}function Q(e){var t;return!(null===(t=i.value)||void 0===t?void 0:t.contains(Fm(e)))}function Z(t){if(!k)return;void 0!==x&&window.clearTimeout(x),void 0!==C&&window.clearTimeout(C);const{value:n}=h,{value:o}=d,{value:i}=O;if(null===n||null===o)return;const a=(null==r?void 0:r.value)?window.innerWidth-t.clientX-P:t.clientX-P,l=o-n;let s=_+a*(o-n)/(n-i);s=Math.min(l,s),s=Math.max(s,0);const{value:c}=H;if(c){c.scrollLeft=s*((null==r?void 0:r.value)?-1:1);const{internalOnUpdateScrollLeft:t}=e;t&&t(s)}}function J(e){e.preventDefault(),e.stopPropagation(),Tb("mousemove",window,Z,!0),Tb("mouseup",window,J,!0),k=!1,Y(),Q(e)&&K()}function ee(e){if(!w)return;void 0!==x&&window.clearTimeout(x),void 0!==C&&window.clearTimeout(C);const{value:t}=p,{value:n}=u,{value:o}=R;if(null===t||null===n)return;const r=e.clientY-T,i=n-t;let a=S+r*(n-t)/(t-o);a=Math.min(i,a),a=Math.max(a,0);const{value:l}=H;l&&(l.scrollTop=a)}function te(e){e.preventDefault(),e.stopPropagation(),Tb("mousemove",window,ee,!0),Tb("mouseup",window,te,!0),w=!1,Y(),Q(e)&&K()}ir((()=>{const{value:e}=$,{value:n}=D,{value:o}=t,{value:r}=c,{value:i}=s;r&&(e?r.classList.remove(`${o}-scrollbar-rail--disabled`):r.classList.add(`${o}-scrollbar-rail--disabled`)),i&&(n?i.classList.remove(`${o}-scrollbar-rail--disabled`):i.classList.add(`${o}-scrollbar-rail--disabled`))})),$n((()=>{e.container||Y()})),Hn((()=>{void 0!==x&&window.clearTimeout(x),void 0!==C&&window.clearTimeout(C),Tb("mousemove",window,ee,!0),Tb("mouseup",window,te,!0)}));const ne=ai((()=>{const{common:{cubicBezierEaseInOut:e},self:{color:t,colorHover:n,height:o,width:i,borderRadius:a,railInsetHorizontal:l,railInsetVertical:s,railColor:c}}=z.value;return{"--n-scrollbar-bezier":e,"--n-scrollbar-color":t,"--n-scrollbar-color-hover":n,"--n-scrollbar-border-radius":a,"--n-scrollbar-width":i,"--n-scrollbar-height":o,"--n-scrollbar-rail-inset-horizontal":l,"--n-scrollbar-rail-inset-vertical":(null==r?void 0:r.value)?Rg(s):s,"--n-scrollbar-rail-color":c}})),oe=n?KP("scrollbar",void 0,ne,e):void 0,re={scrollTo:U,scrollBy:(t,n)=>{if(!e.scrollable)return;const{value:o}=H;o&&("object"==typeof t?o.scrollBy(t):o.scrollBy(t,n||0))},sync:Y,syncUnifiedContainer:X,handleMouseEnterWrapper:function(){void 0!==x&&window.clearTimeout(x),b.value=!0,void 0!==C&&window.clearTimeout(C),y.value=!0,Y()},handleMouseLeaveWrapper:function(){K()}};return Object.assign(Object.assign({},re),{mergedClsPrefix:t,rtlEnabled:r,containerScrollTop:m,wrapperRef:i,containerRef:a,contentRef:l,yRailRef:s,xRailRef:c,needYBar:D,needXBar:$,yBarSizePx:E,xBarSizePx:M,yBarTopPx:I,xBarLeftPx:B,isShowXBar:N,isShowYBar:j,isIos:A,handleScroll:function(t){const{onScroll:n}=e;n&&n(t),G()},handleContentResize:()=>{V.isDeactivated||Y()},handleContainerResize:t=>{if(V.isDeactivated)return;const{onResize:n}=e;n&&n(t),Y()},handleYScrollMouseDown:function(e){e.preventDefault(),e.stopPropagation(),w=!0,Pb("mousemove",window,ee,!0),Pb("mouseup",window,te,!0),S=m.value,T=e.clientY},handleXScrollMouseDown:function(e){e.preventDefault(),e.stopPropagation(),k=!0,Pb("mousemove",window,Z,!0),Pb("mouseup",window,J,!0),_=g.value,P=(null==r?void 0:r.value)?window.innerWidth-e.clientX:e.clientX},cssVars:n?void 0:ne,themeClass:null==oe?void 0:oe.themeClass,onRender:null==oe?void 0:oe.onRender})},render(){var e;const{$slots:t,mergedClsPrefix:n,triggerDisplayManually:o,rtlEnabled:r,internalHoistYRail:i}=this;if(!this.scrollable)return null===(e=t.default)||void 0===e?void 0:e.call(t);const a="none"===this.trigger,l=(e,t)=>li("div",{ref:"yRailRef",class:[`${n}-scrollbar-rail`,`${n}-scrollbar-rail--vertical`,e],"data-scrollbar-rail":!0,style:[t||"",this.verticalRailStyle],"aria-hidden":!0},li(a?_g:vi,a?null:{name:"fade-in-transition"},{default:()=>this.needYBar&&this.isShowYBar&&!this.isIos?li("div",{class:`${n}-scrollbar-rail__scrollbar`,style:{height:this.yBarSizePx,top:this.yBarTopPx},onMousedown:this.handleYScrollMouseDown}):null})),s=()=>{var e,s;return null===(e=this.onRender)||void 0===e||e.call(this),li("div",Wr(this.$attrs,{role:"none",ref:"wrapperRef",class:[`${n}-scrollbar`,this.themeClass,r&&`${n}-scrollbar--rtl`],style:this.cssVars,onMouseenter:o?void 0:this.handleMouseEnterWrapper,onMouseleave:o?void 0:this.handleMouseLeaveWrapper}),[this.container?null===(s=t.default)||void 0===s?void 0:s.call(t):li("div",{role:"none",ref:"containerRef",class:[`${n}-scrollbar-container`,this.containerClass],style:this.containerStyle,onScroll:this.handleScroll,onWheel:this.onWheel},li(kx,{onResize:this.handleContentResize},{default:()=>li("div",{ref:"contentRef",role:"none",style:[{width:this.xScrollable?"fit-content":null},this.contentStyle],class:[`${n}-scrollbar-content`,this.contentClass]},t)})),i?null:l(void 0,void 0),this.xScrollable&&li("div",{ref:"xRailRef",class:[`${n}-scrollbar-rail`,`${n}-scrollbar-rail--horizontal`],style:this.horizontalRailStyle,"data-scrollbar-rail":!0,"aria-hidden":!0},li(a?_g:vi,a?null:{name:"fade-in-transition"},{default:()=>this.needXBar&&this.isShowXBar&&!this.isIos?li("div",{class:`${n}-scrollbar-rail__scrollbar`,style:{width:this.xBarSizePx,right:r?this.xBarLeftPx:void 0,left:r?void 0:this.xBarLeftPx},onMousedown:this.handleXScrollMouseDown}):null}))])},c=this.container?s():li(kx,{onResize:this.handleContainerResize},{default:s});return i?li(yr,null,c,l(this.themeClass,this.cssVars)):c}}),lR=aR,sR=aR,cR={height:"calc(var(--n-option-height) * 7.6)",paddingSmall:"4px 0",paddingMedium:"4px 0",paddingLarge:"4px 0",paddingHuge:"4px 0",optionPaddingSmall:"0 12px",optionPaddingMedium:"0 12px",optionPaddingLarge:"0 12px",optionPaddingHuge:"0 12px",loadingSize:"18px"};function uR(e){const{borderRadius:t,popoverColor:n,textColor3:o,dividerColor:r,textColor2:i,primaryColorPressed:a,textColorDisabled:l,primaryColor:s,opacityDisabled:c,hoverColor:u,fontSizeSmall:d,fontSizeMedium:p,fontSizeLarge:h,fontSizeHuge:f,heightSmall:v,heightMedium:m,heightLarge:g,heightHuge:b}=e;return Object.assign(Object.assign({},cR),{optionFontSizeSmall:d,optionFontSizeMedium:p,optionFontSizeLarge:h,optionFontSizeHuge:f,optionHeightSmall:v,optionHeightMedium:m,optionHeightLarge:g,optionHeightHuge:b,borderRadius:t,color:n,groupHeaderTextColor:o,actionDividerColor:r,optionTextColor:i,optionTextColorPressed:a,optionTextColorDisabled:l,optionTextColorActive:s,optionOpacityDisabled:c,optionCheckColor:s,optionColorPending:u,optionColorActive:"rgba(0, 0, 0, 0)",optionColorActivePending:u,actionTextColor:i,loadingColor:s})}const dR={name:"InternalSelectMenu",common:qz,peers:{Scrollbar:tR,Empty:Xz},self:uR},pR={name:"InternalSelectMenu",common:tz,peers:{Scrollbar:nR,Empty:Yz},self:uR},hR=zn({name:"NBaseSelectOption",props:{clsPrefix:{type:String,required:!0},tmNode:{type:Object,required:!0}},setup(e){const{valueRef:t,pendingTmNodeRef:n,multipleRef:o,valueSetRef:r,renderLabelRef:i,renderOptionRef:a,labelFieldRef:l,valueFieldRef:s,showCheckmarkRef:c,nodePropsRef:u,handleOptionClick:d,handleOptionMouseEnter:p}=Po(Hb),h=mb((()=>{const{value:t}=n;return!!t&&e.tmNode.key===t.key}));return{multiple:o,isGrouped:mb((()=>{const{tmNode:t}=e,{parent:n}=t;return n&&"group"===n.rawNode.type})),showCheckmark:c,nodeProps:u,isPending:h,isSelected:mb((()=>{const{value:n}=t,{value:i}=o;if(null===n)return!1;const a=e.tmNode.rawNode[s.value];if(i){const{value:e}=r;return e.has(a)}return n===a})),labelField:l,renderLabel:i,renderOption:a,handleMouseMove:function(t){const{tmNode:n}=e,{value:o}=h;n.disabled||o||p(t,n)},handleMouseEnter:function(t){const{tmNode:n}=e;n.disabled||p(t,n)},handleClick:function(t){const{tmNode:n}=e;n.disabled||d(t,n)}}},render(){const{clsPrefix:e,tmNode:{rawNode:t},isSelected:n,isPending:o,isGrouped:r,showCheckmark:i,nodeProps:a,renderOption:l,renderLabel:s,handleClick:c,handleMouseEnter:u,handleMouseMove:d}=this,p=function(e,t){return li(vi,{name:"fade-in-scale-up-transition"},{default:()=>e?li(CT,{clsPrefix:t,class:`${t}-base-select-option__check`},{default:()=>li(JP)}):null})}(n,e),h=s?[s(t,n),i&&p]:[hg(t[this.labelField],t,n),i&&p],f=null==a?void 0:a(t),v=li("div",Object.assign({},f,{class:[`${e}-base-select-option`,t.class,null==f?void 0:f.class,{[`${e}-base-select-option--disabled`]:t.disabled,[`${e}-base-select-option--selected`]:n,[`${e}-base-select-option--grouped`]:r,[`${e}-base-select-option--pending`]:o,[`${e}-base-select-option--show-checkmark`]:i}],style:[(null==f?void 0:f.style)||"",t.style||""],onClick:Sg([c,null==f?void 0:f.onClick]),onMouseenter:Sg([u,null==f?void 0:f.onMouseenter]),onMousemove:Sg([d,null==f?void 0:f.onMousemove])}),li("div",{class:`${e}-base-select-option__content`},h));return t.render?t.render({node:v,option:t,selected:n}):l?l({node:v,option:t,selected:n}):v}}),fR=zn({name:"NBaseSelectGroupHeader",props:{clsPrefix:{type:String,required:!0},tmNode:{type:Object,required:!0}},setup(){const{renderLabelRef:e,renderOptionRef:t,labelFieldRef:n,nodePropsRef:o}=Po(Hb);return{labelField:n,nodeProps:o,renderLabel:e,renderOption:t}},render(){const{clsPrefix:e,renderLabel:t,renderOption:n,nodeProps:o,tmNode:{rawNode:r}}=this,i=null==o?void 0:o(r),a=t?t(r,!1):hg(r[this.labelField],r,!1),l=li("div",Object.assign({},i,{class:[`${e}-base-select-group-header`,null==i?void 0:i.class]}),a);return r.render?r.render({node:l,option:r}):n?n({node:l,option:r,selected:!1}):l}}),{cubicBezierEaseIn:vR,cubicBezierEaseOut:mR}=A_;function gR({transformOrigin:e="inherit",duration:t=".2s",enterScale:n=".9",originalTransform:o="",originalTransition:r=""}={}){return[eb("&.fade-in-scale-up-transition-leave-active",{transformOrigin:e,transition:`opacity ${t} ${vR}, transform ${t} ${vR} ${r&&`,${r}`}`}),eb("&.fade-in-scale-up-transition-enter-active",{transformOrigin:e,transition:`opacity ${t} ${mR}, transform ${t} ${mR} ${r&&`,${r}`}`}),eb("&.fade-in-scale-up-transition-enter-from, &.fade-in-scale-up-transition-leave-to",{opacity:0,transform:`${o} scale(${n})`}),eb("&.fade-in-scale-up-transition-leave-from, &.fade-in-scale-up-transition-enter-to",{opacity:1,transform:`${o} scale(1)`})]}const bR=nb("base-select-menu","\n line-height: 1.5;\n outline: none;\n z-index: 0;\n position: relative;\n border-radius: var(--n-border-radius);\n transition:\n background-color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier);\n background-color: var(--n-color);\n",[nb("scrollbar","\n max-height: var(--n-height);\n "),nb("virtual-list","\n max-height: var(--n-height);\n "),nb("base-select-option","\n min-height: var(--n-option-height);\n font-size: var(--n-option-font-size);\n display: flex;\n align-items: center;\n ",[ob("content","\n z-index: 1;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n ")]),nb("base-select-group-header","\n min-height: var(--n-option-height);\n font-size: .93em;\n display: flex;\n align-items: center;\n "),nb("base-select-menu-option-wrapper","\n position: relative;\n width: 100%;\n "),ob("loading, empty","\n display: flex;\n padding: 12px 32px;\n flex: 1;\n justify-content: center;\n "),ob("loading","\n color: var(--n-loading-color);\n font-size: var(--n-loading-size);\n "),ob("header","\n padding: 8px var(--n-option-padding-left);\n font-size: var(--n-option-font-size);\n transition: \n color .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n border-bottom: 1px solid var(--n-action-divider-color);\n color: var(--n-action-text-color);\n "),ob("action","\n padding: 8px var(--n-option-padding-left);\n font-size: var(--n-option-font-size);\n transition: \n color .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n border-top: 1px solid var(--n-action-divider-color);\n color: var(--n-action-text-color);\n "),nb("base-select-group-header","\n position: relative;\n cursor: default;\n padding: var(--n-option-padding);\n color: var(--n-group-header-text-color);\n "),nb("base-select-option","\n cursor: pointer;\n position: relative;\n padding: var(--n-option-padding);\n transition:\n color .3s var(--n-bezier),\n opacity .3s var(--n-bezier);\n box-sizing: border-box;\n color: var(--n-option-text-color);\n opacity: 1;\n ",[rb("show-checkmark","\n padding-right: calc(var(--n-option-padding-right) + 20px);\n "),eb("&::before",'\n content: "";\n position: absolute;\n left: 4px;\n right: 4px;\n top: 0;\n bottom: 0;\n border-radius: var(--n-border-radius);\n transition: background-color .3s var(--n-bezier);\n '),eb("&:active","\n color: var(--n-option-text-color-pressed);\n "),rb("grouped","\n padding-left: calc(var(--n-option-padding-left) * 1.5);\n "),rb("pending",[eb("&::before","\n background-color: var(--n-option-color-pending);\n ")]),rb("selected","\n color: var(--n-option-text-color-active);\n ",[eb("&::before","\n background-color: var(--n-option-color-active);\n "),rb("pending",[eb("&::before","\n background-color: var(--n-option-color-active-pending);\n ")])]),rb("disabled","\n cursor: not-allowed;\n ",[ib("selected","\n color: var(--n-option-text-color-disabled);\n "),rb("selected","\n opacity: var(--n-option-opacity-disabled);\n ")]),ob("check","\n font-size: 16px;\n position: absolute;\n right: calc(var(--n-option-padding-right) - 4px);\n top: calc(50% - 7px);\n color: var(--n-option-check-color);\n transition: color .3s var(--n-bezier);\n ",[gR({enterScale:"0.5"})])])]),yR=zn({name:"InternalSelectMenu",props:Object.assign(Object.assign({},I_.props),{clsPrefix:{type:String,required:!0},scrollable:{type:Boolean,default:!0},treeMate:{type:Object,required:!0},multiple:Boolean,size:{type:String,default:"medium"},value:{type:[String,Number,Array],default:null},autoPending:Boolean,virtualScroll:{type:Boolean,default:!0},show:{type:Boolean,default:!0},labelField:{type:String,default:"label"},valueField:{type:String,default:"value"},loading:Boolean,focusable:Boolean,renderLabel:Function,renderOption:Function,nodeProps:Function,showCheckmark:{type:Boolean,default:!0},onMousedown:Function,onScroll:Function,onFocus:Function,onBlur:Function,onKeyup:Function,onKeydown:Function,onTabOut:Function,onMouseenter:Function,onMouseleave:Function,onResize:Function,resetMenuOnOptionsChange:{type:Boolean,default:!0},inlineThemeDisabled:Boolean,onToggle:Function}),setup(e){const{mergedClsPrefixRef:t,mergedRtlRef:n}=B_(e),o=GP("InternalSelectMenu",n,t),r=I_("InternalSelectMenu","-internal-select-menu",bR,dR,e,jt(e,"clsPrefix")),i=Et(null),a=Et(null),l=Et(null),s=ai((()=>e.treeMate.getFlattenedNodes())),c=ai((()=>function(e){const t=new Map;return e.forEach(((e,n)=>{t.set(e.key,n)})),e=>{var n;return null!==(n=t.get(e))&&void 0!==n?n:null}}(s.value))),u=Et(null);function d(){const{value:t}=u;t&&!e.treeMate.getNode(t.key)&&(u.value=null)}let p;lr((()=>e.show),(t=>{t?p=lr((()=>e.treeMate),(()=>{e.resetMenuOnOptionsChange?(e.autoPending?function(){const{treeMate:t}=e;let n=null;const{value:o}=e;null===o?n=t.getFirstAvailableNode():(n=e.multiple?t.getNode((o||[])[(o||[]).length-1]):t.getNode(o),n&&!n.disabled||(n=t.getFirstAvailableNode())),b(n||null)}():d(),tn(y)):d()}),{immediate:!0}):null==p||p()}),{immediate:!0}),Hn((()=>{null==p||p()}));const h=ai((()=>Im(r.value.self[ub("optionHeight",e.size)]))),f=ai((()=>Bm(r.value.self[ub("padding",e.size)]))),v=ai((()=>e.multiple&&Array.isArray(e.value)?new Set(e.value):new Set)),m=ai((()=>{const e=s.value;return e&&0===e.length}));function g(t){const{onScroll:n}=e;n&&n(t)}function b(e,t=!1){u.value=e,t&&y()}function y(){var t,n;const o=u.value;if(!o)return;const r=c.value(o.key);null!==r&&(e.virtualScroll?null===(t=a.value)||void 0===t||t.scrollTo({index:r}):null===(n=l.value)||void 0===n||n.scrollTo({index:r,elSize:h.value}))}_o(Hb,{handleOptionMouseEnter:function(e,t){t.disabled||b(t,!1)},handleOptionClick:function(t,n){n.disabled||function(t){const{onToggle:n}=e;n&&n(t)}(n)},valueSetRef:v,pendingTmNodeRef:u,nodePropsRef:jt(e,"nodeProps"),showCheckmarkRef:jt(e,"showCheckmark"),multipleRef:jt(e,"multiple"),valueRef:jt(e,"value"),renderLabelRef:jt(e,"renderLabel"),renderOptionRef:jt(e,"renderOption"),labelFieldRef:jt(e,"labelField"),valueFieldRef:jt(e,"valueField")}),_o(Wb,i),$n((()=>{const{value:e}=l;e&&e.sync()}));const x=ai((()=>{const{size:t}=e,{common:{cubicBezierEaseInOut:n},self:{height:o,borderRadius:i,color:a,groupHeaderTextColor:l,actionDividerColor:s,optionTextColorPressed:c,optionTextColor:u,optionTextColorDisabled:d,optionTextColorActive:p,optionOpacityDisabled:h,optionCheckColor:f,actionTextColor:v,optionColorPending:m,optionColorActive:g,loadingColor:b,loadingSize:y,optionColorActivePending:x,[ub("optionFontSize",t)]:C,[ub("optionHeight",t)]:w,[ub("optionPadding",t)]:k}}=r.value;return{"--n-height":o,"--n-action-divider-color":s,"--n-action-text-color":v,"--n-bezier":n,"--n-border-radius":i,"--n-color":a,"--n-option-font-size":C,"--n-group-header-text-color":l,"--n-option-check-color":f,"--n-option-color-pending":m,"--n-option-color-active":g,"--n-option-color-active-pending":x,"--n-option-height":w,"--n-option-opacity-disabled":h,"--n-option-text-color":u,"--n-option-text-color-active":p,"--n-option-text-color-disabled":d,"--n-option-text-color-pressed":c,"--n-option-padding":k,"--n-option-padding-left":Bm(k,"left"),"--n-option-padding-right":Bm(k,"right"),"--n-loading-color":b,"--n-loading-size":y}})),{inlineThemeDisabled:C}=e,w=C?KP("internal-select-menu",ai((()=>e.size[0])),x,e):void 0,k={selfRef:i,next:function(){const{value:e}=u;e&&b(e.getNext({loop:!0}),!0)},prev:function(){const{value:e}=u;e&&b(e.getPrev({loop:!0}),!0)},getPendingTmNode:function(){const{value:e}=u;return e||null}};return Dx(i,e.onResize),Object.assign({mergedTheme:r,mergedClsPrefix:t,rtlEnabled:o,virtualListRef:a,scrollbarRef:l,itemSize:h,padding:f,flattenedNodes:s,empty:m,virtualListContainer(){const{value:e}=a;return null==e?void 0:e.listElRef},virtualListContent(){const{value:e}=a;return null==e?void 0:e.itemsElRef},doScroll:g,handleFocusin:function(t){var n,o;(null===(n=i.value)||void 0===n?void 0:n.contains(t.target))&&(null===(o=e.onFocus)||void 0===o||o.call(e,t))},handleFocusout:function(t){var n,o;(null===(n=i.value)||void 0===n?void 0:n.contains(t.relatedTarget))||null===(o=e.onBlur)||void 0===o||o.call(e,t)},handleKeyUp:function(t){var n;Mm(t,"action")||null===(n=e.onKeyup)||void 0===n||n.call(e,t)},handleKeyDown:function(t){var n;Mm(t,"action")||null===(n=e.onKeydown)||void 0===n||n.call(e,t)},handleMouseDown:function(t){var n;null===(n=e.onMousedown)||void 0===n||n.call(e,t),e.focusable||t.preventDefault()},handleVirtualListResize:function(){var e;null===(e=l.value)||void 0===e||e.sync()},handleVirtualListScroll:function(e){var t;null===(t=l.value)||void 0===t||t.sync(),g(e)},cssVars:C?void 0:x,themeClass:null==w?void 0:w.themeClass,onRender:null==w?void 0:w.onRender},k)},render(){const{$slots:e,virtualScroll:t,clsPrefix:n,mergedTheme:o,themeClass:r,onRender:i}=this;return null==i||i(),li("div",{ref:"selfRef",tabindex:this.focusable?0:-1,class:[`${n}-base-select-menu`,this.rtlEnabled&&`${n}-base-select-menu--rtl`,r,this.multiple&&`${n}-base-select-menu--multiple`],style:this.cssVars,onFocusin:this.handleFocusin,onFocusout:this.handleFocusout,onKeyup:this.handleKeyUp,onKeydown:this.handleKeyDown,onMousedown:this.handleMouseDown,onMouseenter:this.onMouseenter,onMouseleave:this.onMouseleave},wg(e.header,(e=>e&&li("div",{class:`${n}-base-select-menu__header`,"data-header":!0,key:"header"},e))),this.loading?li("div",{class:`${n}-base-select-menu__loading`},li(RT,{clsPrefix:n,strokeWidth:20})):this.empty?li("div",{class:`${n}-base-select-menu__empty`,"data-empty":!0},xg(e.empty,(()=>[li(Zz,{theme:o.peers.Empty,themeOverrides:o.peerOverrides.Empty})]))):li(lR,{ref:"scrollbarRef",theme:o.peers.Scrollbar,themeOverrides:o.peerOverrides.Scrollbar,scrollable:this.scrollable,container:t?this.virtualListContainer:void 0,content:t?this.virtualListContent:void 0,onScroll:t?void 0:this.doScroll},{default:()=>t?li(Ax,{ref:"virtualListRef",class:`${n}-virtual-list`,items:this.flattenedNodes,itemSize:this.itemSize,showScrollbar:!1,paddingTop:this.padding.top,paddingBottom:this.padding.bottom,onResize:this.handleVirtualListResize,onScroll:this.handleVirtualListScroll,itemResizable:!0},{default:({item:e})=>e.isGroup?li(fR,{key:e.key,clsPrefix:n,tmNode:e}):e.ignored?null:li(hR,{clsPrefix:n,key:e.key,tmNode:e})}):li("div",{class:`${n}-base-select-menu-option-wrapper`,style:{paddingTop:this.padding.top,paddingBottom:this.padding.bottom}},this.flattenedNodes.map((e=>e.isGroup?li(fR,{key:e.key,clsPrefix:n,tmNode:e}):li(hR,{clsPrefix:n,key:e.key,tmNode:e}))))}),wg(e.action,(e=>e&&[li("div",{class:`${n}-base-select-menu__action`,"data-action":!0,key:"action"},e),li(ST,{onFocus:this.onTabOut,key:"focus-detector"})])))}}),xR=nb("base-wave","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n border-radius: inherit;\n"),CR=zn({name:"BaseWave",props:{clsPrefix:{type:String,required:!0}},setup(e){qP("-base-wave",xR,jt(e,"clsPrefix"));const t=Et(null),n=Et(!1);let o=null;return Hn((()=>{null!==o&&window.clearTimeout(o)})),{active:n,selfRef:t,play(){null!==o&&(window.clearTimeout(o),n.value=!1,o=null),tn((()=>{var e;null===(e=t.value)||void 0===e||e.offsetHeight,n.value=!0,o=window.setTimeout((()=>{n.value=!1,o=null}),1e3)}))}}},render(){const{clsPrefix:e}=this;return li("div",{ref:"selfRef","aria-hidden":!0,class:[`${e}-base-wave`,this.active&&`${e}-base-wave--active`]})}}),wR={space:"6px",spaceArrow:"10px",arrowOffset:"10px",arrowOffsetVertical:"10px",arrowHeight:"6px",padding:"8px 14px"};function kR(e){const{boxShadow2:t,popoverColor:n,textColor2:o,borderRadius:r,fontSize:i,dividerColor:a}=e;return Object.assign(Object.assign({},wR),{fontSize:i,borderRadius:r,color:n,dividerColor:a,textColor:o,boxShadow:t})}const SR={name:"Popover",common:qz,self:kR},_R={name:"Popover",common:tz,self:kR},PR={top:"bottom",bottom:"top",left:"right",right:"left"},TR="var(--n-arrow-height) * 1.414",AR=eb([nb("popover","\n transition:\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n position: relative;\n font-size: var(--n-font-size);\n color: var(--n-text-color);\n box-shadow: var(--n-box-shadow);\n word-break: break-word;\n ",[eb(">",[nb("scrollbar","\n height: inherit;\n max-height: inherit;\n ")]),ib("raw","\n background-color: var(--n-color);\n border-radius: var(--n-border-radius);\n ",[ib("scrollable",[ib("show-header-or-footer","padding: var(--n-padding);")])]),ob("header","\n padding: var(--n-padding);\n border-bottom: 1px solid var(--n-divider-color);\n transition: border-color .3s var(--n-bezier);\n "),ob("footer","\n padding: var(--n-padding);\n border-top: 1px solid var(--n-divider-color);\n transition: border-color .3s var(--n-bezier);\n "),rb("scrollable, show-header-or-footer",[ob("content","\n padding: var(--n-padding);\n ")])]),nb("popover-shared","\n transform-origin: inherit;\n ",[nb("popover-arrow-wrapper","\n position: absolute;\n overflow: hidden;\n pointer-events: none;\n ",[nb("popover-arrow",`\n transition: background-color .3s var(--n-bezier);\n position: absolute;\n display: block;\n width: calc(${TR});\n height: calc(${TR});\n box-shadow: 0 0 8px 0 rgba(0, 0, 0, .12);\n transform: rotate(45deg);\n background-color: var(--n-color);\n pointer-events: all;\n `)]),eb("&.popover-transition-enter-from, &.popover-transition-leave-to","\n opacity: 0;\n transform: scale(.85);\n "),eb("&.popover-transition-enter-to, &.popover-transition-leave-from","\n transform: scale(1);\n opacity: 1;\n "),eb("&.popover-transition-enter-active","\n transition:\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n opacity .15s var(--n-bezier-ease-out),\n transform .15s var(--n-bezier-ease-out);\n "),eb("&.popover-transition-leave-active","\n transition:\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n opacity .15s var(--n-bezier-ease-in),\n transform .15s var(--n-bezier-ease-in);\n ")]),OR("top-start",`\n top: calc(${TR} / -2);\n left: calc(${ER("top-start")} - var(--v-offset-left));\n `),OR("top",`\n top: calc(${TR} / -2);\n transform: translateX(calc(${TR} / -2)) rotate(45deg);\n left: 50%;\n `),OR("top-end",`\n top: calc(${TR} / -2);\n right: calc(${ER("top-end")} + var(--v-offset-left));\n `),OR("bottom-start",`\n bottom: calc(${TR} / -2);\n left: calc(${ER("bottom-start")} - var(--v-offset-left));\n `),OR("bottom",`\n bottom: calc(${TR} / -2);\n transform: translateX(calc(${TR} / -2)) rotate(45deg);\n left: 50%;\n `),OR("bottom-end",`\n bottom: calc(${TR} / -2);\n right: calc(${ER("bottom-end")} + var(--v-offset-left));\n `),OR("left-start",`\n left: calc(${TR} / -2);\n top: calc(${ER("left-start")} - var(--v-offset-top));\n `),OR("left",`\n left: calc(${TR} / -2);\n transform: translateY(calc(${TR} / -2)) rotate(45deg);\n top: 50%;\n `),OR("left-end",`\n left: calc(${TR} / -2);\n bottom: calc(${ER("left-end")} + var(--v-offset-top));\n `),OR("right-start",`\n right: calc(${TR} / -2);\n top: calc(${ER("right-start")} - var(--v-offset-top));\n `),OR("right",`\n right: calc(${TR} / -2);\n transform: translateY(calc(${TR} / -2)) rotate(45deg);\n top: 50%;\n `),OR("right-end",`\n right: calc(${TR} / -2);\n bottom: calc(${ER("right-end")} + var(--v-offset-top));\n `),...(zR={top:["right-start","left-start"],right:["top-end","bottom-end"],bottom:["right-end","left-end"],left:["top-start","bottom-start"]},RR=(e,t)=>{const n=["right","left"].includes(t),o=n?"width":"height";return e.map((e=>{const r="end"===e.split("-")[1],i=`calc((var(--v-target-${o}, 0px) - ${TR}) / 2)`,a=ER(e);return eb(`[v-placement="${e}"] >`,[nb("popover-shared",[rb("center-arrow",[nb("popover-arrow",`${t}: calc(max(${i}, ${a}) ${r?"+":"-"} var(--v-offset-${n?"left":"top"}));`)])])])}))},(vC(zR)?fC:C_)(zR,u_(RR)))]);var zR,RR;function ER(e){return["top","bottom"].includes(e.split("-")[0])?"var(--n-arrow-offset)":"var(--n-arrow-offset-vertical)"}function OR(e,t){const n=e.split("-")[0],o=["top","bottom"].includes(n)?"height: var(--n-space-arrow);":"width: var(--n-space-arrow);";return eb(`[v-placement="${e}"] >`,[nb("popover-shared",`\n margin-${PR[n]}: var(--n-space);\n `,[rb("show-arrow",`\n margin-${PR[n]}: var(--n-space-arrow);\n `),rb("overlap","\n margin: 0;\n "),cb("popover-arrow-wrapper",`\n right: 0;\n left: 0;\n top: 0;\n bottom: 0;\n ${n}: 100%;\n ${PR[n]}: auto;\n ${o}\n `,[nb("popover-arrow",t)])])])}const MR=Object.assign(Object.assign({},I_.props),{to:Yb.propTo,show:Boolean,trigger:String,showArrow:Boolean,delay:Number,duration:Number,raw:Boolean,arrowPointToCenter:Boolean,arrowClass:String,arrowStyle:[String,Object],arrowWrapperClass:String,arrowWrapperStyle:[String,Object],displayDirective:String,x:Number,y:Number,flip:Boolean,overlap:Boolean,placement:String,width:[Number,String],keepAliveOnHover:Boolean,scrollable:Boolean,contentClass:String,contentStyle:[Object,String],headerClass:String,headerStyle:[Object,String],footerClass:String,footerStyle:[Object,String],internalDeactivateImmediately:Boolean,animated:Boolean,onClickoutside:Function,internalTrapFocus:Boolean,internalOnAfterLeave:Function,minWidth:Number,maxWidth:Number});function FR({arrowClass:e,arrowStyle:t,arrowWrapperClass:n,arrowWrapperStyle:o,clsPrefix:r}){return li("div",{key:"__popover-arrow__",style:o,class:[`${r}-popover-arrow-wrapper`,n]},li("div",{class:[`${r}-popover-arrow`,e],style:t}))}const IR=zn({name:"PopoverBody",inheritAttrs:!1,props:MR,setup(e,{slots:t,attrs:n}){const{namespaceRef:o,mergedClsPrefixRef:r,inlineThemeDisabled:i}=B_(e),a=I_("Popover","-popover",AR,SR,e,r),l=Et(null),s=Po("NPopover"),c=Et(null),u=Et(e.show),d=Et(!1);ir((()=>{const{show:t}=e;!t||(void 0===db&&(db=navigator.userAgent.includes("Node.js")||navigator.userAgent.includes("jsdom")),db)||e.internalDeactivateImmediately||(d.value=!0)}));const p=ai((()=>{const{trigger:t,onClickoutside:n}=e,o=[],{positionManuallyRef:{value:r}}=s;return r||("click"!==t||n||o.push([dy,y,void 0,{capture:!0}]),"hover"===t&&o.push([cy,b])),n&&o.push([dy,y,void 0,{capture:!0}]),("show"===e.displayDirective||e.animated&&d.value)&&o.push([Mi,e.show]),o})),h=ai((()=>{const{common:{cubicBezierEaseInOut:e,cubicBezierEaseIn:t,cubicBezierEaseOut:n},self:{space:o,spaceArrow:r,padding:i,fontSize:l,textColor:s,dividerColor:c,color:u,boxShadow:d,borderRadius:p,arrowHeight:h,arrowOffset:f,arrowOffsetVertical:v}}=a.value;return{"--n-box-shadow":d,"--n-bezier":e,"--n-bezier-ease-in":t,"--n-bezier-ease-out":n,"--n-font-size":l,"--n-text-color":s,"--n-color":u,"--n-divider-color":c,"--n-border-radius":p,"--n-arrow-height":h,"--n-arrow-offset":f,"--n-arrow-offset-vertical":v,"--n-padding":i,"--n-space":o,"--n-space-arrow":r}})),f=ai((()=>{const t="trigger"===e.width?void 0:Ag(e.width),n=[];t&&n.push({width:t});const{maxWidth:o,minWidth:r}=e;return o&&n.push({maxWidth:Ag(o)}),r&&n.push({maxWidth:Ag(r)}),i||n.push(h.value),n})),v=i?KP("popover",void 0,h,e):void 0;function m(t){"hover"===e.trigger&&e.keepAliveOnHover&&e.show&&s.handleMouseEnter(t)}function g(t){"hover"===e.trigger&&e.keepAliveOnHover&&s.handleMouseLeave(t)}function b(t){"hover"!==e.trigger||x().contains(Fm(t))||s.handleMouseMoveOutside(t)}function y(t){("click"===e.trigger&&!x().contains(Fm(t))||e.onClickoutside)&&s.handleClickOutside(t)}function x(){return s.getTriggerElement()}return s.setBodyInstance({syncPosition:function(){var e;null===(e=l.value)||void 0===e||e.syncPosition()}}),Hn((()=>{s.setBodyInstance(null)})),lr(jt(e,"show"),(t=>{e.animated||(u.value=!!t)})),_o(Gb,c),_o(qb,null),_o(Ub,null),{displayed:d,namespace:o,isMounted:s.isMountedRef,zIndex:s.zIndexRef,followerRef:l,adjustedTo:Yb(e),followerEnabled:u,renderContentNode:function(){if(null==v||v.onRender(),!("show"===e.displayDirective||e.show||e.animated&&d.value))return null;let o;const i=s.internalRenderBodyRef.value,{value:a}=r;if(i)o=i([`${a}-popover-shared`,null==v?void 0:v.themeClass.value,e.overlap&&`${a}-popover-shared--overlap`,e.showArrow&&`${a}-popover-shared--show-arrow`,e.arrowPointToCenter&&`${a}-popover-shared--center-arrow`],c,f.value,m,g);else{const{value:r}=s.extraClassRef,{internalTrapFocus:i}=e,l=!kg(t.header)||!kg(t.footer),u=()=>{var n,o;const r=l?li(yr,null,wg(t.header,(t=>t?li("div",{class:[`${a}-popover__header`,e.headerClass],style:e.headerStyle},t):null)),wg(t.default,(n=>n?li("div",{class:[`${a}-popover__content`,e.contentClass],style:e.contentStyle},t):null)),wg(t.footer,(t=>t?li("div",{class:[`${a}-popover__footer`,e.footerClass],style:e.footerStyle},t):null))):e.scrollable?null===(n=t.default)||void 0===n?void 0:n.call(t):li("div",{class:[`${a}-popover__content`,e.contentClass],style:e.contentStyle},t);return[e.scrollable?li(sR,{contentClass:l?void 0:`${a}-popover__content ${null!==(o=e.contentClass)&&void 0!==o?o:""}`,contentStyle:l?void 0:e.contentStyle},{default:()=>r}):r,e.showArrow?FR({arrowClass:e.arrowClass,arrowStyle:e.arrowStyle,arrowWrapperClass:e.arrowWrapperClass,arrowWrapperStyle:e.arrowWrapperStyle,clsPrefix:a}):null]};o=li("div",Wr({class:[`${a}-popover`,`${a}-popover-shared`,null==v?void 0:v.themeClass.value,r.map((e=>`${a}-${e}`)),{[`${a}-popover--scrollable`]:e.scrollable,[`${a}-popover--show-header-or-footer`]:l,[`${a}-popover--raw`]:e.raw,[`${a}-popover-shared--overlap`]:e.overlap,[`${a}-popover-shared--show-arrow`]:e.showArrow,[`${a}-popover-shared--center-arrow`]:e.arrowPointToCenter}],ref:c,style:f.value,onKeydown:s.handleKeydown,onMouseenter:m,onMouseleave:g},n),i?li(Bx,{active:e.show,autoFocus:!0},{default:u}):u())}return fn(o,p.value)}}},render(){return li(Fy,{ref:"followerRef",zIndex:this.zIndex,show:this.show,enabled:this.followerEnabled,to:this.adjustedTo,x:this.x,y:this.y,flip:this.flip,placement:this.placement,containerClass:this.namespace,overlap:this.overlap,width:"trigger"===this.width?"target":void 0,teleportDisabled:this.adjustedTo===Yb.tdkey},{default:()=>this.animated?li(vi,{name:"popover-transition",appear:this.isMounted,onEnter:()=>{this.followerEnabled=!0},onAfterLeave:()=>{var e;null===(e=this.internalOnAfterLeave)||void 0===e||e.call(this),this.followerEnabled=!1,this.displayed=!1}},{default:this.renderContentNode}):this.renderContentNode()})}}),LR=Object.keys(MR),BR={focus:["onFocus","onBlur"],click:["onClick"],hover:["onMouseenter","onMouseleave"],manual:[],nested:["onFocus","onBlur","onMouseenter","onMouseleave","onClick"]},DR={show:{type:Boolean,default:void 0},defaultShow:Boolean,showArrow:{type:Boolean,default:!0},trigger:{type:String,default:"hover"},delay:{type:Number,default:100},duration:{type:Number,default:100},raw:Boolean,placement:{type:String,default:"top"},x:Number,y:Number,arrowPointToCenter:Boolean,disabled:Boolean,getDisabled:Function,displayDirective:{type:String,default:"if"},arrowClass:String,arrowStyle:[String,Object],arrowWrapperClass:String,arrowWrapperStyle:[String,Object],flip:{type:Boolean,default:!0},animated:{type:Boolean,default:!0},width:{type:[Number,String],default:void 0},overlap:Boolean,keepAliveOnHover:{type:Boolean,default:!0},zIndex:Number,to:Yb.propTo,scrollable:Boolean,contentClass:String,contentStyle:[Object,String],headerClass:String,headerStyle:[Object,String],footerClass:String,footerStyle:[Object,String],onClickoutside:Function,"onUpdate:show":[Function,Array],onUpdateShow:[Function,Array],internalDeactivateImmediately:Boolean,internalSyncTargetWithParent:Boolean,internalInheritedEventHandlers:{type:Array,default:()=>[]},internalTrapFocus:Boolean,internalExtraClass:{type:Array,default:()=>[]},onShow:[Function,Array],onHide:[Function,Array],arrow:{type:Boolean,default:void 0},minWidth:Number,maxWidth:Number},$R=zn({name:"Popover",inheritAttrs:!1,props:Object.assign(Object.assign(Object.assign({},I_.props),DR),{internalOnAfterLeave:Function,internalRenderBody:Function}),__popover__:!0,setup(e){const t=$b(),n=Et(null),o=ai((()=>e.show)),r=Et(e.defaultShow),i=Db(o,r),a=mb((()=>!e.disabled&&i.value)),l=()=>{if(e.disabled)return!0;const{getDisabled:t}=e;return!!(null==t?void 0:t())},s=()=>!l()&&i.value,c=Nb(e,["arrow","showArrow"]),u=ai((()=>!e.overlap&&c.value));let d=null;const p=Et(null),h=Et(null),f=mb((()=>void 0!==e.x&&void 0!==e.y));function v(t){const{"onUpdate:show":n,onUpdateShow:o,onShow:i,onHide:a}=e;r.value=t,n&&dg(n,t),o&&dg(o,t),t&&i&&dg(i,!0),t&&a&&dg(a,!1)}function m(){const{value:e}=p;e&&(window.clearTimeout(e),p.value=null)}function g(){const{value:e}=h;e&&(window.clearTimeout(e),h.value=null)}function b(){const t=l();if("hover"===e.trigger&&!t){if(g(),null!==p.value)return;if(s())return;const t=()=>{v(!0),p.value=null},{delay:n}=e;0===n?t():p.value=window.setTimeout(t,n)}}function y(){const t=l();if("hover"===e.trigger&&!t){if(m(),null!==h.value)return;if(!s())return;const t=()=>{v(!1),h.value=null},{duration:n}=e;0===n?t():h.value=window.setTimeout(t,n)}}return _o("NPopover",{getTriggerElement:function(){var e;return null===(e=n.value)||void 0===e?void 0:e.targetRef},handleKeydown:function(t){e.internalTrapFocus&&"Escape"===t.key&&(m(),g(),v(!1))},handleMouseEnter:b,handleMouseLeave:y,handleClickOutside:function(t){var n;s()&&("click"===e.trigger&&(m(),g(),v(!1)),null===(n=e.onClickoutside)||void 0===n||n.call(e,t))},handleMouseMoveOutside:function(){y()},setBodyInstance:function(e){d=e},positionManuallyRef:f,isMountedRef:t,zIndexRef:jt(e,"zIndex"),extraClassRef:jt(e,"internalExtraClass"),internalRenderBodyRef:jt(e,"internalRenderBody")}),ir((()=>{i.value&&l()&&v(!1)})),{binderInstRef:n,positionManually:f,mergedShowConsideringDisabledProp:a,uncontrolledShow:r,mergedShowArrow:u,getMergedShow:s,setShow:function(e){r.value=e},handleClick:function(){"click"!==e.trigger||l()||(m(),g(),v(!s()))},handleMouseEnter:b,handleMouseLeave:y,handleFocus:function(){const t=l();if("focus"===e.trigger&&!t){if(s())return;v(!0)}},handleBlur:function(){const t=l();if("focus"===e.trigger&&!t){if(!s())return;v(!1)}},syncPosition:function(){d&&d.syncPosition()}}},render(){var e;const{positionManually:t,$slots:n}=this;let o,r=!1;if(!t&&(o=n.activator?gg(n,"activator"):gg(n,"trigger"),o)){o=Br(o),o=o.type===xr?li("span",[o]):o;const n={onClick:this.handleClick,onMouseenter:this.handleMouseEnter,onMouseleave:this.handleMouseLeave,onFocus:this.handleFocus,onBlur:this.handleBlur};if(null===(e=o.type)||void 0===e?void 0:e.__popover__)r=!0,o.props||(o.props={internalSyncTargetWithParent:!0,internalInheritedEventHandlers:[]}),o.props.internalSyncTargetWithParent=!0,o.props.internalInheritedEventHandlers?o.props.internalInheritedEventHandlers=[n,...o.props.internalInheritedEventHandlers]:o.props.internalInheritedEventHandlers=[n];else{const{internalInheritedEventHandlers:e}=this,r=[n,...e],s={onBlur:e=>{r.forEach((t=>{t.onBlur(e)}))},onFocus:e=>{r.forEach((t=>{t.onFocus(e)}))},onClick:e=>{r.forEach((t=>{t.onClick(e)}))},onMouseenter:e=>{r.forEach((t=>{t.onMouseenter(e)}))},onMouseleave:e=>{r.forEach((t=>{t.onMouseleave(e)}))}};i=o,a=e?"nested":t?"manual":this.trigger,l=s,BR[a].forEach((e=>{i.props?i.props=Object.assign({},i.props):i.props={};const t=i.props[e],n=l[e];i.props[e]=t?(...e)=>{t(...e),n(...e)}:n}))}}var i,a,l;return li(ay,{ref:"binderInstRef",syncTarget:!r,syncTargetWithParent:this.internalSyncTargetWithParent},{default:()=>{this.mergedShowConsideringDisabledProp;const e=this.getMergedShow();return[this.internalTrapFocus&&e?fn(li("div",{style:{position:"fixed",inset:0}}),[[fy,{enabled:e,zIndex:this.zIndex}]]):null,t?null:li(ly,null,{default:()=>o}),li(IR,sg(this.$props,LR,Object.assign(Object.assign({},this.$attrs),{showArrow:this.mergedShowArrow,show:e})),{default:()=>{var e,t;return null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)},header:()=>{var e,t;return null===(t=(e=this.$slots).header)||void 0===t?void 0:t.call(e)},footer:()=>{var e,t;return null===(t=(e=this.$slots).footer)||void 0===t?void 0:t.call(e)}})]}})}}),NR={closeIconSizeTiny:"12px",closeIconSizeSmall:"12px",closeIconSizeMedium:"14px",closeIconSizeLarge:"14px",closeSizeTiny:"16px",closeSizeSmall:"16px",closeSizeMedium:"18px",closeSizeLarge:"18px",padding:"0 7px",closeMargin:"0 0 0 4px"},jR={name:"Tag",common:tz,self(e){const{textColor2:t,primaryColorHover:n,primaryColorPressed:o,primaryColor:r,infoColor:i,successColor:a,warningColor:l,errorColor:s,baseColor:c,borderColor:u,tagColor:d,opacityDisabled:p,closeIconColor:h,closeIconColorHover:f,closeIconColorPressed:v,closeColorHover:m,closeColorPressed:g,borderRadiusSmall:b,fontSizeMini:y,fontSizeTiny:x,fontSizeSmall:C,fontSizeMedium:w,heightMini:k,heightTiny:S,heightSmall:_,heightMedium:P,buttonColor2Hover:T,buttonColor2Pressed:A,fontWeightStrong:z}=e;return Object.assign(Object.assign({},NR),{closeBorderRadius:b,heightTiny:k,heightSmall:S,heightMedium:_,heightLarge:P,borderRadius:b,opacityDisabled:p,fontSizeTiny:y,fontSizeSmall:x,fontSizeMedium:C,fontSizeLarge:w,fontWeightStrong:z,textColorCheckable:t,textColorHoverCheckable:t,textColorPressedCheckable:t,textColorChecked:c,colorCheckable:"#0000",colorHoverCheckable:T,colorPressedCheckable:A,colorChecked:r,colorCheckedHover:n,colorCheckedPressed:o,border:`1px solid ${u}`,textColor:t,color:d,colorBordered:"#0000",closeIconColor:h,closeIconColorHover:f,closeIconColorPressed:v,closeColorHover:m,closeColorPressed:g,borderPrimary:`1px solid ${tg(r,{alpha:.3})}`,textColorPrimary:r,colorPrimary:tg(r,{alpha:.16}),colorBorderedPrimary:"#0000",closeIconColorPrimary:ng(r,{lightness:.7}),closeIconColorHoverPrimary:ng(r,{lightness:.7}),closeIconColorPressedPrimary:ng(r,{lightness:.7}),closeColorHoverPrimary:tg(r,{alpha:.16}),closeColorPressedPrimary:tg(r,{alpha:.12}),borderInfo:`1px solid ${tg(i,{alpha:.3})}`,textColorInfo:i,colorInfo:tg(i,{alpha:.16}),colorBorderedInfo:"#0000",closeIconColorInfo:ng(i,{alpha:.7}),closeIconColorHoverInfo:ng(i,{alpha:.7}),closeIconColorPressedInfo:ng(i,{alpha:.7}),closeColorHoverInfo:tg(i,{alpha:.16}),closeColorPressedInfo:tg(i,{alpha:.12}),borderSuccess:`1px solid ${tg(a,{alpha:.3})}`,textColorSuccess:a,colorSuccess:tg(a,{alpha:.16}),colorBorderedSuccess:"#0000",closeIconColorSuccess:ng(a,{alpha:.7}),closeIconColorHoverSuccess:ng(a,{alpha:.7}),closeIconColorPressedSuccess:ng(a,{alpha:.7}),closeColorHoverSuccess:tg(a,{alpha:.16}),closeColorPressedSuccess:tg(a,{alpha:.12}),borderWarning:`1px solid ${tg(l,{alpha:.3})}`,textColorWarning:l,colorWarning:tg(l,{alpha:.16}),colorBorderedWarning:"#0000",closeIconColorWarning:ng(l,{alpha:.7}),closeIconColorHoverWarning:ng(l,{alpha:.7}),closeIconColorPressedWarning:ng(l,{alpha:.7}),closeColorHoverWarning:tg(l,{alpha:.16}),closeColorPressedWarning:tg(l,{alpha:.11}),borderError:`1px solid ${tg(s,{alpha:.3})}`,textColorError:s,colorError:tg(s,{alpha:.16}),colorBorderedError:"#0000",closeIconColorError:ng(s,{alpha:.7}),closeIconColorHoverError:ng(s,{alpha:.7}),closeIconColorPressedError:ng(s,{alpha:.7}),closeColorHoverError:tg(s,{alpha:.16}),closeColorPressedError:tg(s,{alpha:.12})})}},HR={name:"Tag",common:qz,self:function(e){const{textColor2:t,primaryColorHover:n,primaryColorPressed:o,primaryColor:r,infoColor:i,successColor:a,warningColor:l,errorColor:s,baseColor:c,borderColor:u,opacityDisabled:d,tagColor:p,closeIconColor:h,closeIconColorHover:f,closeIconColorPressed:v,borderRadiusSmall:m,fontSizeMini:g,fontSizeTiny:b,fontSizeSmall:y,fontSizeMedium:x,heightMini:C,heightTiny:w,heightSmall:k,heightMedium:S,closeColorHover:_,closeColorPressed:P,buttonColor2Hover:T,buttonColor2Pressed:A,fontWeightStrong:z}=e;return Object.assign(Object.assign({},NR),{closeBorderRadius:m,heightTiny:C,heightSmall:w,heightMedium:k,heightLarge:S,borderRadius:m,opacityDisabled:d,fontSizeTiny:g,fontSizeSmall:b,fontSizeMedium:y,fontSizeLarge:x,fontWeightStrong:z,textColorCheckable:t,textColorHoverCheckable:t,textColorPressedCheckable:t,textColorChecked:c,colorCheckable:"#0000",colorHoverCheckable:T,colorPressedCheckable:A,colorChecked:r,colorCheckedHover:n,colorCheckedPressed:o,border:`1px solid ${u}`,textColor:t,color:p,colorBordered:"rgb(250, 250, 252)",closeIconColor:h,closeIconColorHover:f,closeIconColorPressed:v,closeColorHover:_,closeColorPressed:P,borderPrimary:`1px solid ${tg(r,{alpha:.3})}`,textColorPrimary:r,colorPrimary:tg(r,{alpha:.12}),colorBorderedPrimary:tg(r,{alpha:.1}),closeIconColorPrimary:r,closeIconColorHoverPrimary:r,closeIconColorPressedPrimary:r,closeColorHoverPrimary:tg(r,{alpha:.12}),closeColorPressedPrimary:tg(r,{alpha:.18}),borderInfo:`1px solid ${tg(i,{alpha:.3})}`,textColorInfo:i,colorInfo:tg(i,{alpha:.12}),colorBorderedInfo:tg(i,{alpha:.1}),closeIconColorInfo:i,closeIconColorHoverInfo:i,closeIconColorPressedInfo:i,closeColorHoverInfo:tg(i,{alpha:.12}),closeColorPressedInfo:tg(i,{alpha:.18}),borderSuccess:`1px solid ${tg(a,{alpha:.3})}`,textColorSuccess:a,colorSuccess:tg(a,{alpha:.12}),colorBorderedSuccess:tg(a,{alpha:.1}),closeIconColorSuccess:a,closeIconColorHoverSuccess:a,closeIconColorPressedSuccess:a,closeColorHoverSuccess:tg(a,{alpha:.12}),closeColorPressedSuccess:tg(a,{alpha:.18}),borderWarning:`1px solid ${tg(l,{alpha:.35})}`,textColorWarning:l,colorWarning:tg(l,{alpha:.15}),colorBorderedWarning:tg(l,{alpha:.12}),closeIconColorWarning:l,closeIconColorHoverWarning:l,closeIconColorPressedWarning:l,closeColorHoverWarning:tg(l,{alpha:.12}),closeColorPressedWarning:tg(l,{alpha:.18}),borderError:`1px solid ${tg(s,{alpha:.23})}`,textColorError:s,colorError:tg(s,{alpha:.1}),colorBorderedError:tg(s,{alpha:.08}),closeIconColorError:s,closeIconColorHoverError:s,closeIconColorPressedError:s,closeColorHoverError:tg(s,{alpha:.12}),closeColorPressedError:tg(s,{alpha:.18})})}},WR={color:Object,type:{type:String,default:"default"},round:Boolean,size:{type:String,default:"medium"},closable:Boolean,disabled:{type:Boolean,default:void 0}},UR=nb("tag","\n --n-close-margin: var(--n-close-margin-top) var(--n-close-margin-right) var(--n-close-margin-bottom) var(--n-close-margin-left);\n white-space: nowrap;\n position: relative;\n box-sizing: border-box;\n cursor: default;\n display: inline-flex;\n align-items: center;\n flex-wrap: nowrap;\n padding: var(--n-padding);\n border-radius: var(--n-border-radius);\n color: var(--n-text-color);\n background-color: var(--n-color);\n transition: \n border-color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n opacity .3s var(--n-bezier);\n line-height: 1;\n height: var(--n-height);\n font-size: var(--n-font-size);\n",[rb("strong","\n font-weight: var(--n-font-weight-strong);\n "),ob("border","\n pointer-events: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n border-radius: inherit;\n border: var(--n-border);\n transition: border-color .3s var(--n-bezier);\n "),ob("icon","\n display: flex;\n margin: 0 4px 0 0;\n color: var(--n-text-color);\n transition: color .3s var(--n-bezier);\n font-size: var(--n-avatar-size-override);\n "),ob("avatar","\n display: flex;\n margin: 0 6px 0 0;\n "),ob("close","\n margin: var(--n-close-margin);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n "),rb("round","\n padding: 0 calc(var(--n-height) / 3);\n border-radius: calc(var(--n-height) / 2);\n ",[ob("icon","\n margin: 0 4px 0 calc((var(--n-height) - 8px) / -2);\n "),ob("avatar","\n margin: 0 6px 0 calc((var(--n-height) - 8px) / -2);\n "),rb("closable","\n padding: 0 calc(var(--n-height) / 4) 0 calc(var(--n-height) / 3);\n ")]),rb("icon, avatar",[rb("round","\n padding: 0 calc(var(--n-height) / 3) 0 calc(var(--n-height) / 2);\n ")]),rb("disabled","\n cursor: not-allowed !important;\n opacity: var(--n-opacity-disabled);\n "),rb("checkable","\n cursor: pointer;\n box-shadow: none;\n color: var(--n-text-color-checkable);\n background-color: var(--n-color-checkable);\n ",[ib("disabled",[eb("&:hover","background-color: var(--n-color-hover-checkable);",[ib("checked","color: var(--n-text-color-hover-checkable);")]),eb("&:active","background-color: var(--n-color-pressed-checkable);",[ib("checked","color: var(--n-text-color-pressed-checkable);")])]),rb("checked","\n color: var(--n-text-color-checked);\n background-color: var(--n-color-checked);\n ",[ib("disabled",[eb("&:hover","background-color: var(--n-color-checked-hover);"),eb("&:active","background-color: var(--n-color-checked-pressed);")])])])]),VR=Object.assign(Object.assign(Object.assign({},I_.props),WR),{bordered:{type:Boolean,default:void 0},checked:Boolean,checkable:Boolean,strong:Boolean,triggerClickOnClose:Boolean,onClose:[Array,Function],onMouseenter:Function,onMouseleave:Function,"onUpdate:checked":Function,onUpdateChecked:Function,internalCloseFocusable:{type:Boolean,default:!0},internalCloseIsButtonTag:{type:Boolean,default:!0},onCheckedChange:Function}),qR=zn({name:"Tag",props:VR,setup(e){const t=Et(null),{mergedBorderedRef:n,mergedClsPrefixRef:o,inlineThemeDisabled:r,mergedRtlRef:i}=B_(e),a=I_("Tag","-tag",UR,HR,e,o);_o("n-tag",{roundRef:jt(e,"round")});const l={setTextContent(e){const{value:n}=t;n&&(n.textContent=e)}},s=GP("Tag",i,o),c=ai((()=>{const{type:t,size:o,color:{color:r,textColor:i}={}}=e,{common:{cubicBezierEaseInOut:l},self:{padding:s,closeMargin:c,borderRadius:u,opacityDisabled:d,textColorCheckable:p,textColorHoverCheckable:h,textColorPressedCheckable:f,textColorChecked:v,colorCheckable:m,colorHoverCheckable:g,colorPressedCheckable:b,colorChecked:y,colorCheckedHover:x,colorCheckedPressed:C,closeBorderRadius:w,fontWeightStrong:k,[ub("colorBordered",t)]:S,[ub("closeSize",o)]:_,[ub("closeIconSize",o)]:P,[ub("fontSize",o)]:T,[ub("height",o)]:A,[ub("color",t)]:z,[ub("textColor",t)]:R,[ub("border",t)]:E,[ub("closeIconColor",t)]:O,[ub("closeIconColorHover",t)]:M,[ub("closeIconColorPressed",t)]:F,[ub("closeColorHover",t)]:I,[ub("closeColorPressed",t)]:L}}=a.value,B=Bm(c);return{"--n-font-weight-strong":k,"--n-avatar-size-override":`calc(${A} - 8px)`,"--n-bezier":l,"--n-border-radius":u,"--n-border":E,"--n-close-icon-size":P,"--n-close-color-pressed":L,"--n-close-color-hover":I,"--n-close-border-radius":w,"--n-close-icon-color":O,"--n-close-icon-color-hover":M,"--n-close-icon-color-pressed":F,"--n-close-icon-color-disabled":O,"--n-close-margin-top":B.top,"--n-close-margin-right":B.right,"--n-close-margin-bottom":B.bottom,"--n-close-margin-left":B.left,"--n-close-size":_,"--n-color":r||(n.value?S:z),"--n-color-checkable":m,"--n-color-checked":y,"--n-color-checked-hover":x,"--n-color-checked-pressed":C,"--n-color-hover-checkable":g,"--n-color-pressed-checkable":b,"--n-font-size":T,"--n-height":A,"--n-opacity-disabled":d,"--n-padding":s,"--n-text-color":i||R,"--n-text-color-checkable":p,"--n-text-color-checked":v,"--n-text-color-hover-checkable":h,"--n-text-color-pressed-checkable":f}})),u=r?KP("tag",ai((()=>{let t="";const{type:o,size:r,color:{color:i,textColor:a}={}}=e;return t+=o[0],t+=r[0],i&&(t+=`a${zg(i)}`),a&&(t+=`b${zg(a)}`),n.value&&(t+="c"),t})),c,e):void 0;return Object.assign(Object.assign({},l),{rtlEnabled:s,mergedClsPrefix:o,contentRef:t,mergedBordered:n,handleClick:function(){if(!e.disabled&&e.checkable){const{checked:t,onCheckedChange:n,onUpdateChecked:o,"onUpdate:checked":r}=e;o&&o(!t),r&&r(!t),n&&n(!t)}},handleCloseClick:function(t){if(e.triggerClickOnClose||t.stopPropagation(),!e.disabled){const{onClose:n}=e;n&&dg(n,t)}},cssVars:r?void 0:c,themeClass:null==u?void 0:u.themeClass,onRender:null==u?void 0:u.onRender})},render(){var e,t;const{mergedClsPrefix:n,rtlEnabled:o,closable:r,color:{borderColor:i}={},round:a,onRender:l,$slots:s}=this;null==l||l();const c=wg(s.avatar,(e=>e&&li("div",{class:`${n}-tag__avatar`},e))),u=wg(s.icon,(e=>e&&li("div",{class:`${n}-tag__icon`},e)));return li("div",{class:[`${n}-tag`,this.themeClass,{[`${n}-tag--rtl`]:o,[`${n}-tag--strong`]:this.strong,[`${n}-tag--disabled`]:this.disabled,[`${n}-tag--checkable`]:this.checkable,[`${n}-tag--checked`]:this.checkable&&this.checked,[`${n}-tag--round`]:a,[`${n}-tag--avatar`]:c,[`${n}-tag--icon`]:u,[`${n}-tag--closable`]:r}],style:this.cssVars,onClick:this.handleClick,onMouseenter:this.onMouseenter,onMouseleave:this.onMouseleave},u||c,li("span",{class:`${n}-tag__content`,ref:"contentRef"},null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)),!this.checkable&&r?li(kT,{clsPrefix:n,class:`${n}-tag__close`,disabled:this.disabled,onClick:this.handleCloseClick,focusable:this.internalCloseFocusable,round:a,isButtonTag:this.internalCloseIsButtonTag,absolute:!0}):null,!this.checkable&&this.mergedBordered?li("div",{class:`${n}-tag__border`,style:{borderColor:i}}):null)}}),KR=nb("base-clear","\n flex-shrink: 0;\n height: 1em;\n width: 1em;\n position: relative;\n",[eb(">",[ob("clear","\n font-size: var(--n-clear-size);\n height: 1em;\n width: 1em;\n cursor: pointer;\n color: var(--n-clear-color);\n transition: color .3s var(--n-bezier);\n display: flex;\n ",[eb("&:hover","\n color: var(--n-clear-color-hover)!important;\n "),eb("&:active","\n color: var(--n-clear-color-pressed)!important;\n ")]),ob("placeholder","\n display: flex;\n "),ob("clear, placeholder","\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translateX(-50%) translateY(-50%);\n ",[PT({originalTransform:"translateX(-50%) translateY(-50%)",left:"50%",top:"50%"})])])]),GR=zn({name:"BaseClear",props:{clsPrefix:{type:String,required:!0},show:Boolean,onClear:Function},setup:e=>(qP("-base-clear",KR,jt(e,"clsPrefix")),{handleMouseDown(e){e.preventDefault()}}),render(){const{clsPrefix:e}=this;return li("div",{class:`${e}-base-clear`},li(bT,null,{default:()=>{var t,n;return this.show?li("div",{key:"dismiss",class:`${e}-base-clear__clear`,onClick:this.onClear,onMousedown:this.handleMouseDown,"data-clear":!0},xg(this.$slots.icon,(()=>[li(CT,{clsPrefix:e},{default:()=>li(mT,null)})]))):li("div",{key:"icon",class:`${e}-base-clear__placeholder`},null===(n=(t=this.$slots).placeholder)||void 0===n?void 0:n.call(t))}}))}}),XR=zn({name:"InternalSelectionSuffix",props:{clsPrefix:{type:String,required:!0},showArrow:{type:Boolean,default:void 0},showClear:{type:Boolean,default:void 0},loading:{type:Boolean,default:!1},onClear:Function},setup:(e,{slots:t})=>()=>{const{clsPrefix:n}=e;return li(RT,{clsPrefix:n,class:`${n}-base-suffix`,strokeWidth:24,scale:.85,show:e.loading},{default:()=>e.showArrow?li(GR,{clsPrefix:n,show:e.showClear,onClear:e.onClear},{placeholder:()=>li(CT,{clsPrefix:n,class:`${n}-base-suffix__arrow`},{default:()=>xg(t.default,(()=>[li(vT,null)]))})}):null})}}),YR={paddingSingle:"0 26px 0 12px",paddingMultiple:"3px 26px 0 12px",clearSize:"16px",arrowSize:"16px"},QR={name:"InternalSelection",common:qz,peers:{Popover:SR},self:function(e){const{borderRadius:t,textColor2:n,textColorDisabled:o,inputColor:r,inputColorDisabled:i,primaryColor:a,primaryColorHover:l,warningColor:s,warningColorHover:c,errorColor:u,errorColorHover:d,borderColor:p,iconColor:h,iconColorDisabled:f,clearColor:v,clearColorHover:m,clearColorPressed:g,placeholderColor:b,placeholderColorDisabled:y,fontSizeTiny:x,fontSizeSmall:C,fontSizeMedium:w,fontSizeLarge:k,heightTiny:S,heightSmall:_,heightMedium:P,heightLarge:T}=e;return Object.assign(Object.assign({},YR),{fontSizeTiny:x,fontSizeSmall:C,fontSizeMedium:w,fontSizeLarge:k,heightTiny:S,heightSmall:_,heightMedium:P,heightLarge:T,borderRadius:t,textColor:n,textColorDisabled:o,placeholderColor:b,placeholderColorDisabled:y,color:r,colorDisabled:i,colorActive:r,border:`1px solid ${p}`,borderHover:`1px solid ${l}`,borderActive:`1px solid ${a}`,borderFocus:`1px solid ${l}`,boxShadowHover:"none",boxShadowActive:`0 0 0 2px ${tg(a,{alpha:.2})}`,boxShadowFocus:`0 0 0 2px ${tg(a,{alpha:.2})}`,caretColor:a,arrowColor:h,arrowColorDisabled:f,loadingColor:a,borderWarning:`1px solid ${s}`,borderHoverWarning:`1px solid ${c}`,borderActiveWarning:`1px solid ${s}`,borderFocusWarning:`1px solid ${c}`,boxShadowHoverWarning:"none",boxShadowActiveWarning:`0 0 0 2px ${tg(s,{alpha:.2})}`,boxShadowFocusWarning:`0 0 0 2px ${tg(s,{alpha:.2})}`,colorActiveWarning:r,caretColorWarning:s,borderError:`1px solid ${u}`,borderHoverError:`1px solid ${d}`,borderActiveError:`1px solid ${u}`,borderFocusError:`1px solid ${d}`,boxShadowHoverError:"none",boxShadowActiveError:`0 0 0 2px ${tg(u,{alpha:.2})}`,boxShadowFocusError:`0 0 0 2px ${tg(u,{alpha:.2})}`,colorActiveError:r,caretColorError:u,clearColor:v,clearColorHover:m,clearColorPressed:g})}},ZR={name:"InternalSelection",common:tz,peers:{Popover:_R},self(e){const{borderRadius:t,textColor2:n,textColorDisabled:o,inputColor:r,inputColorDisabled:i,primaryColor:a,primaryColorHover:l,warningColor:s,warningColorHover:c,errorColor:u,errorColorHover:d,iconColor:p,iconColorDisabled:h,clearColor:f,clearColorHover:v,clearColorPressed:m,placeholderColor:g,placeholderColorDisabled:b,fontSizeTiny:y,fontSizeSmall:x,fontSizeMedium:C,fontSizeLarge:w,heightTiny:k,heightSmall:S,heightMedium:_,heightLarge:P}=e;return Object.assign(Object.assign({},YR),{fontSizeTiny:y,fontSizeSmall:x,fontSizeMedium:C,fontSizeLarge:w,heightTiny:k,heightSmall:S,heightMedium:_,heightLarge:P,borderRadius:t,textColor:n,textColorDisabled:o,placeholderColor:g,placeholderColorDisabled:b,color:r,colorDisabled:i,colorActive:tg(a,{alpha:.1}),border:"1px solid #0000",borderHover:`1px solid ${l}`,borderActive:`1px solid ${a}`,borderFocus:`1px solid ${l}`,boxShadowHover:"none",boxShadowActive:`0 0 8px 0 ${tg(a,{alpha:.4})}`,boxShadowFocus:`0 0 8px 0 ${tg(a,{alpha:.4})}`,caretColor:a,arrowColor:p,arrowColorDisabled:h,loadingColor:a,borderWarning:`1px solid ${s}`,borderHoverWarning:`1px solid ${c}`,borderActiveWarning:`1px solid ${s}`,borderFocusWarning:`1px solid ${c}`,boxShadowHoverWarning:"none",boxShadowActiveWarning:`0 0 8px 0 ${tg(s,{alpha:.4})}`,boxShadowFocusWarning:`0 0 8px 0 ${tg(s,{alpha:.4})}`,colorActiveWarning:tg(s,{alpha:.1}),caretColorWarning:s,borderError:`1px solid ${u}`,borderHoverError:`1px solid ${d}`,borderActiveError:`1px solid ${u}`,borderFocusError:`1px solid ${d}`,boxShadowHoverError:"none",boxShadowActiveError:`0 0 8px 0 ${tg(u,{alpha:.4})}`,boxShadowFocusError:`0 0 8px 0 ${tg(u,{alpha:.4})}`,colorActiveError:tg(u,{alpha:.1}),caretColorError:u,clearColor:f,clearColorHover:v,clearColorPressed:m})}},JR=eb([nb("base-selection","\n --n-padding-single: var(--n-padding-single-top) var(--n-padding-single-right) var(--n-padding-single-bottom) var(--n-padding-single-left);\n --n-padding-multiple: var(--n-padding-multiple-top) var(--n-padding-multiple-right) var(--n-padding-multiple-bottom) var(--n-padding-multiple-left);\n position: relative;\n z-index: auto;\n box-shadow: none;\n width: 100%;\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n border-radius: var(--n-border-radius);\n min-height: var(--n-height);\n line-height: 1.5;\n font-size: var(--n-font-size);\n ",[nb("base-loading","\n color: var(--n-loading-color);\n "),nb("base-selection-tags","min-height: var(--n-height);"),ob("border, state-border","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n pointer-events: none;\n border: var(--n-border);\n border-radius: inherit;\n transition:\n box-shadow .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n "),ob("state-border","\n z-index: 1;\n border-color: #0000;\n "),nb("base-suffix","\n cursor: pointer;\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n right: 10px;\n ",[ob("arrow","\n font-size: var(--n-arrow-size);\n color: var(--n-arrow-color);\n transition: color .3s var(--n-bezier);\n ")]),nb("base-selection-overlay","\n display: flex;\n align-items: center;\n white-space: nowrap;\n pointer-events: none;\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: var(--n-padding-single);\n transition: color .3s var(--n-bezier);\n ",[ob("wrapper","\n flex-basis: 0;\n flex-grow: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n ")]),nb("base-selection-placeholder","\n color: var(--n-placeholder-color);\n ",[ob("inner","\n max-width: 100%;\n overflow: hidden;\n ")]),nb("base-selection-tags","\n cursor: pointer;\n outline: none;\n box-sizing: border-box;\n position: relative;\n z-index: auto;\n display: flex;\n padding: var(--n-padding-multiple);\n flex-wrap: wrap;\n align-items: center;\n width: 100%;\n vertical-align: bottom;\n background-color: var(--n-color);\n border-radius: inherit;\n transition:\n color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n "),nb("base-selection-label","\n height: var(--n-height);\n display: inline-flex;\n width: 100%;\n vertical-align: bottom;\n cursor: pointer;\n outline: none;\n z-index: auto;\n box-sizing: border-box;\n position: relative;\n transition:\n color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n border-radius: inherit;\n background-color: var(--n-color);\n align-items: center;\n ",[nb("base-selection-input","\n font-size: inherit;\n line-height: inherit;\n outline: none;\n cursor: pointer;\n box-sizing: border-box;\n border:none;\n width: 100%;\n padding: var(--n-padding-single);\n background-color: #0000;\n color: var(--n-text-color);\n transition: color .3s var(--n-bezier);\n caret-color: var(--n-caret-color);\n ",[ob("content","\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap; \n ")]),ob("render-label","\n color: var(--n-text-color);\n ")]),ib("disabled",[eb("&:hover",[ob("state-border","\n box-shadow: var(--n-box-shadow-hover);\n border: var(--n-border-hover);\n ")]),rb("focus",[ob("state-border","\n box-shadow: var(--n-box-shadow-focus);\n border: var(--n-border-focus);\n ")]),rb("active",[ob("state-border","\n box-shadow: var(--n-box-shadow-active);\n border: var(--n-border-active);\n "),nb("base-selection-label","background-color: var(--n-color-active);"),nb("base-selection-tags","background-color: var(--n-color-active);")])]),rb("disabled","cursor: not-allowed;",[ob("arrow","\n color: var(--n-arrow-color-disabled);\n "),nb("base-selection-label","\n cursor: not-allowed;\n background-color: var(--n-color-disabled);\n ",[nb("base-selection-input","\n cursor: not-allowed;\n color: var(--n-text-color-disabled);\n "),ob("render-label","\n color: var(--n-text-color-disabled);\n ")]),nb("base-selection-tags","\n cursor: not-allowed;\n background-color: var(--n-color-disabled);\n "),nb("base-selection-placeholder","\n cursor: not-allowed;\n color: var(--n-placeholder-color-disabled);\n ")]),nb("base-selection-input-tag","\n height: calc(var(--n-height) - 6px);\n line-height: calc(var(--n-height) - 6px);\n outline: none;\n display: none;\n position: relative;\n margin-bottom: 3px;\n max-width: 100%;\n vertical-align: bottom;\n ",[ob("input","\n font-size: inherit;\n font-family: inherit;\n min-width: 1px;\n padding: 0;\n background-color: #0000;\n outline: none;\n border: none;\n max-width: 100%;\n overflow: hidden;\n width: 1em;\n line-height: inherit;\n cursor: pointer;\n color: var(--n-text-color);\n caret-color: var(--n-caret-color);\n "),ob("mirror","\n position: absolute;\n left: 0;\n top: 0;\n white-space: pre;\n visibility: hidden;\n user-select: none;\n -webkit-user-select: none;\n opacity: 0;\n ")]),["warning","error"].map((e=>rb(`${e}-status`,[ob("state-border",`border: var(--n-border-${e});`),ib("disabled",[eb("&:hover",[ob("state-border",`\n box-shadow: var(--n-box-shadow-hover-${e});\n border: var(--n-border-hover-${e});\n `)]),rb("active",[ob("state-border",`\n box-shadow: var(--n-box-shadow-active-${e});\n border: var(--n-border-active-${e});\n `),nb("base-selection-label",`background-color: var(--n-color-active-${e});`),nb("base-selection-tags",`background-color: var(--n-color-active-${e});`)]),rb("focus",[ob("state-border",`\n box-shadow: var(--n-box-shadow-focus-${e});\n border: var(--n-border-focus-${e});\n `)])])])))]),nb("base-selection-popover","\n margin-bottom: -3px;\n display: flex;\n flex-wrap: wrap;\n margin-right: -8px;\n "),nb("base-selection-tag-wrapper","\n max-width: 100%;\n display: inline-flex;\n padding: 0 7px 3px 0;\n ",[eb("&:last-child","padding-right: 0;"),nb("tag","\n font-size: 14px;\n max-width: 100%;\n ",[ob("content","\n line-height: 1.25;\n text-overflow: ellipsis;\n overflow: hidden;\n ")])])]),eE=zn({name:"InternalSelection",props:Object.assign(Object.assign({},I_.props),{clsPrefix:{type:String,required:!0},bordered:{type:Boolean,default:void 0},active:Boolean,pattern:{type:String,default:""},placeholder:String,selectedOption:{type:Object,default:null},selectedOptions:{type:Array,default:null},labelField:{type:String,default:"label"},valueField:{type:String,default:"value"},multiple:Boolean,filterable:Boolean,clearable:Boolean,disabled:Boolean,size:{type:String,default:"medium"},loading:Boolean,autofocus:Boolean,showArrow:{type:Boolean,default:!0},inputProps:Object,focused:Boolean,renderTag:Function,onKeydown:Function,onClick:Function,onBlur:Function,onFocus:Function,onDeleteOption:Function,maxTagCount:[String,Number],ellipsisTagPopoverProps:Object,onClear:Function,onPatternInput:Function,onPatternFocus:Function,onPatternBlur:Function,renderLabel:Function,status:String,inlineThemeDisabled:Boolean,ignoreComposition:{type:Boolean,default:!0},onResize:Function}),setup(e){const{mergedClsPrefixRef:t,mergedRtlRef:n}=B_(e),o=GP("InternalSelection",n,t),r=Et(null),i=Et(null),a=Et(null),l=Et(null),s=Et(null),c=Et(null),u=Et(null),d=Et(null),p=Et(null),h=Et(null),f=Et(!1),v=Et(!1),m=Et(!1),g=I_("InternalSelection","-internal-selection",JR,QR,e,jt(e,"clsPrefix")),b=ai((()=>e.clearable&&!e.disabled&&(m.value||e.active))),y=ai((()=>e.selectedOption?e.renderTag?e.renderTag({option:e.selectedOption,handleClose:()=>{}}):e.renderLabel?e.renderLabel(e.selectedOption,!0):hg(e.selectedOption[e.labelField],e.selectedOption,!0):e.placeholder)),x=ai((()=>{const t=e.selectedOption;if(t)return t[e.labelField]})),C=ai((()=>e.multiple?!(!Array.isArray(e.selectedOptions)||!e.selectedOptions.length):null!==e.selectedOption));function w(){var t;const{value:n}=r;if(n){const{value:o}=i;o&&(o.style.width=`${n.offsetWidth}px`,"responsive"!==e.maxTagCount&&(null===(t=p.value)||void 0===t||t.sync({showAllItemsBeforeCalculate:!1})))}}function k(t){const{onPatternInput:n}=e;n&&n(t)}function S(t){!function(t){const{onDeleteOption:n}=e;n&&n(t)}(t)}lr(jt(e,"active"),(e=>{e||function(){const{value:e}=h;e&&(e.style.display="none")}()})),lr(jt(e,"pattern"),(()=>{e.multiple&&tn(w)}));const _=Et(!1);let P=null,T=null;function A(){null!==T&&window.clearTimeout(T)}lr(C,(e=>{e||(f.value=!1)})),$n((()=>{ir((()=>{const t=c.value;t&&(e.disabled?t.removeAttribute("tabindex"):t.tabIndex=v.value?-1:0)}))})),Dx(a,e.onResize);const{inlineThemeDisabled:z}=e,R=ai((()=>{const{size:t}=e,{common:{cubicBezierEaseInOut:n},self:{borderRadius:o,color:r,placeholderColor:i,textColor:a,paddingSingle:l,paddingMultiple:s,caretColor:c,colorDisabled:u,textColorDisabled:d,placeholderColorDisabled:p,colorActive:h,boxShadowFocus:f,boxShadowActive:v,boxShadowHover:m,border:b,borderFocus:y,borderHover:x,borderActive:C,arrowColor:w,arrowColorDisabled:k,loadingColor:S,colorActiveWarning:_,boxShadowFocusWarning:P,boxShadowActiveWarning:T,boxShadowHoverWarning:A,borderWarning:z,borderFocusWarning:R,borderHoverWarning:E,borderActiveWarning:O,colorActiveError:M,boxShadowFocusError:F,boxShadowActiveError:I,boxShadowHoverError:L,borderError:B,borderFocusError:D,borderHoverError:$,borderActiveError:N,clearColor:j,clearColorHover:H,clearColorPressed:W,clearSize:U,arrowSize:V,[ub("height",t)]:q,[ub("fontSize",t)]:K}}=g.value,G=Bm(l),X=Bm(s);return{"--n-bezier":n,"--n-border":b,"--n-border-active":C,"--n-border-focus":y,"--n-border-hover":x,"--n-border-radius":o,"--n-box-shadow-active":v,"--n-box-shadow-focus":f,"--n-box-shadow-hover":m,"--n-caret-color":c,"--n-color":r,"--n-color-active":h,"--n-color-disabled":u,"--n-font-size":K,"--n-height":q,"--n-padding-single-top":G.top,"--n-padding-multiple-top":X.top,"--n-padding-single-right":G.right,"--n-padding-multiple-right":X.right,"--n-padding-single-left":G.left,"--n-padding-multiple-left":X.left,"--n-padding-single-bottom":G.bottom,"--n-padding-multiple-bottom":X.bottom,"--n-placeholder-color":i,"--n-placeholder-color-disabled":p,"--n-text-color":a,"--n-text-color-disabled":d,"--n-arrow-color":w,"--n-arrow-color-disabled":k,"--n-loading-color":S,"--n-color-active-warning":_,"--n-box-shadow-focus-warning":P,"--n-box-shadow-active-warning":T,"--n-box-shadow-hover-warning":A,"--n-border-warning":z,"--n-border-focus-warning":R,"--n-border-hover-warning":E,"--n-border-active-warning":O,"--n-color-active-error":M,"--n-box-shadow-focus-error":F,"--n-box-shadow-active-error":I,"--n-box-shadow-hover-error":L,"--n-border-error":B,"--n-border-focus-error":D,"--n-border-hover-error":$,"--n-border-active-error":N,"--n-clear-size":U,"--n-clear-color":j,"--n-clear-color-hover":H,"--n-clear-color-pressed":W,"--n-arrow-size":V}})),E=z?KP("internal-selection",ai((()=>e.size[0])),R,e):void 0;return{mergedTheme:g,mergedClearable:b,mergedClsPrefix:t,rtlEnabled:o,patternInputFocused:v,filterablePlaceholder:y,label:x,selected:C,showTagsPanel:f,isComposing:_,counterRef:u,counterWrapperRef:d,patternInputMirrorRef:r,patternInputRef:i,selfRef:a,multipleElRef:l,singleElRef:s,patternInputWrapperRef:c,overflowRef:p,inputTagElRef:h,handleMouseDown:function(t){e.active&&e.filterable&&t.target!==i.value&&t.preventDefault()},handleFocusin:function(t){var n;t.relatedTarget&&(null===(n=a.value)||void 0===n?void 0:n.contains(t.relatedTarget))||function(t){const{onFocus:n}=e;n&&n(t)}(t)},handleClear:function(t){!function(t){const{onClear:n}=e;n&&n(t)}(t)},handleMouseEnter:function(){m.value=!0},handleMouseLeave:function(){m.value=!1},handleDeleteOption:S,handlePatternKeyDown:function(t){if("Backspace"===t.key&&!_.value&&!e.pattern.length){const{selectedOptions:t}=e;(null==t?void 0:t.length)&&S(t[t.length-1])}},handlePatternInputInput:function(t){const{value:n}=r;if(n){const e=t.target.value;n.textContent=e,w()}e.ignoreComposition&&_.value?P=t:k(t)},handlePatternInputBlur:function(t){var n;v.value=!1,null===(n=e.onPatternBlur)||void 0===n||n.call(e,t)},handlePatternInputFocus:function(t){var n;v.value=!0,null===(n=e.onPatternFocus)||void 0===n||n.call(e,t)},handleMouseEnterCounter:function(){e.active||(A(),T=window.setTimeout((()=>{C.value&&(f.value=!0)}),100))},handleMouseLeaveCounter:function(){A()},handleFocusout:function(t){var n;(null===(n=a.value)||void 0===n?void 0:n.contains(t.relatedTarget))||function(t){const{onBlur:n}=e;n&&n(t)}(t)},handleCompositionEnd:function(){_.value=!1,e.ignoreComposition&&k(P),P=null},handleCompositionStart:function(){_.value=!0},onPopoverUpdateShow:function(e){e||(A(),f.value=!1)},focus:function(){var t,n,o;e.filterable?(v.value=!1,null===(t=c.value)||void 0===t||t.focus()):e.multiple?null===(n=l.value)||void 0===n||n.focus():null===(o=s.value)||void 0===o||o.focus()},focusInput:function(){const{value:e}=i;e&&(function(){const{value:e}=h;e&&(e.style.display="inline-block")}(),e.focus())},blur:function(){var t,n;if(e.filterable)v.value=!1,null===(t=c.value)||void 0===t||t.blur(),null===(n=i.value)||void 0===n||n.blur();else if(e.multiple){const{value:e}=l;null==e||e.blur()}else{const{value:e}=s;null==e||e.blur()}},blurInput:function(){const{value:e}=i;e&&e.blur()},updateCounter:function(e){const{value:t}=u;t&&t.setTextContent(`+${e}`)},getCounter:function(){const{value:e}=d;return e},getTail:function(){return i.value},renderLabel:e.renderLabel,cssVars:z?void 0:R,themeClass:null==E?void 0:E.themeClass,onRender:null==E?void 0:E.onRender}},render(){const{status:e,multiple:t,size:n,disabled:o,filterable:r,maxTagCount:i,bordered:a,clsPrefix:l,ellipsisTagPopoverProps:s,onRender:c,renderTag:u,renderLabel:d}=this;null==c||c();const p="responsive"===i,h="number"==typeof i,f=p||h,v=li(_g,null,{default:()=>li(XR,{clsPrefix:l,loading:this.loading,showArrow:this.showArrow,showClear:this.mergedClearable&&this.selected,onClear:this.handleClear},{default:()=>{var e,t;return null===(t=(e=this.$slots).arrow)||void 0===t?void 0:t.call(e)}})});let m;if(t){const{labelField:e}=this,t=t=>li("div",{class:`${l}-base-selection-tag-wrapper`,key:t.value},u?u({option:t,handleClose:()=>{this.handleDeleteOption(t)}}):li(qR,{size:n,closable:!t.disabled,disabled:o,onClose:()=>{this.handleDeleteOption(t)},internalCloseIsButtonTag:!1,internalCloseFocusable:!1},{default:()=>d?d(t,!0):hg(t[e],t,!0)})),a=()=>(h?this.selectedOptions.slice(0,i):this.selectedOptions).map(t),c=r?li("div",{class:`${l}-base-selection-input-tag`,ref:"inputTagElRef",key:"__input-tag__"},li("input",Object.assign({},this.inputProps,{ref:"patternInputRef",tabindex:-1,disabled:o,value:this.pattern,autofocus:this.autofocus,class:`${l}-base-selection-input-tag__input`,onBlur:this.handlePatternInputBlur,onFocus:this.handlePatternInputFocus,onKeydown:this.handlePatternKeyDown,onInput:this.handlePatternInputInput,onCompositionstart:this.handleCompositionStart,onCompositionend:this.handleCompositionEnd})),li("span",{ref:"patternInputMirrorRef",class:`${l}-base-selection-input-tag__mirror`},this.pattern)):null,g=p?()=>li("div",{class:`${l}-base-selection-tag-wrapper`,ref:"counterWrapperRef"},li(qR,{size:n,ref:"counterRef",onMouseenter:this.handleMouseEnterCounter,onMouseleave:this.handleMouseLeaveCounter,disabled:o})):void 0;let b;if(h){const e=this.selectedOptions.length-i;e>0&&(b=li("div",{class:`${l}-base-selection-tag-wrapper`,key:"__counter__"},li(qR,{size:n,ref:"counterRef",onMouseenter:this.handleMouseEnterCounter,disabled:o},{default:()=>`+${e}`})))}const y=p?r?li(Ex,{ref:"overflowRef",updateCounter:this.updateCounter,getCounter:this.getCounter,getTail:this.getTail,style:{width:"100%",display:"flex",overflow:"hidden"}},{default:a,counter:g,tail:()=>c}):li(Ex,{ref:"overflowRef",updateCounter:this.updateCounter,getCounter:this.getCounter,style:{width:"100%",display:"flex",overflow:"hidden"}},{default:a,counter:g}):h&&b?a().concat(b):a(),x=f?()=>li("div",{class:`${l}-base-selection-popover`},p?a():this.selectedOptions.map(t)):void 0,C=f?Object.assign({show:this.showTagsPanel,trigger:"hover",overlap:!0,placement:"top",width:"trigger",onUpdateShow:this.onPopoverUpdateShow,theme:this.mergedTheme.peers.Popover,themeOverrides:this.mergedTheme.peerOverrides.Popover},s):null,w=this.selected||this.active&&(this.pattern||this.isComposing)?null:li("div",{class:`${l}-base-selection-placeholder ${l}-base-selection-overlay`},li("div",{class:`${l}-base-selection-placeholder__inner`},this.placeholder)),k=r?li("div",{ref:"patternInputWrapperRef",class:`${l}-base-selection-tags`},y,p?null:c,v):li("div",{ref:"multipleElRef",class:`${l}-base-selection-tags`,tabindex:o?void 0:0},y,v);m=li(yr,null,f?li($R,Object.assign({},C,{scrollable:!0,style:"max-height: calc(var(--v-target-height) * 6.6);"}),{trigger:()=>k,default:x}):k,w)}else if(r){const e=this.pattern||this.isComposing,t=this.active?!e:!this.selected,n=!this.active&&this.selected;m=li("div",{ref:"patternInputWrapperRef",class:`${l}-base-selection-label`,title:this.patternInputFocused?void 0:mg(this.label)},li("input",Object.assign({},this.inputProps,{ref:"patternInputRef",class:`${l}-base-selection-input`,value:this.active?this.pattern:"",placeholder:"",readonly:o,disabled:o,tabindex:-1,autofocus:this.autofocus,onFocus:this.handlePatternInputFocus,onBlur:this.handlePatternInputBlur,onInput:this.handlePatternInputInput,onCompositionstart:this.handleCompositionStart,onCompositionend:this.handleCompositionEnd})),n?li("div",{class:`${l}-base-selection-label__render-label ${l}-base-selection-overlay`,key:"input"},li("div",{class:`${l}-base-selection-overlay__wrapper`},u?u({option:this.selectedOption,handleClose:()=>{}}):d?d(this.selectedOption,!0):hg(this.label,this.selectedOption,!0))):null,t?li("div",{class:`${l}-base-selection-placeholder ${l}-base-selection-overlay`,key:"placeholder"},li("div",{class:`${l}-base-selection-overlay__wrapper`},this.filterablePlaceholder)):null,v)}else m=li("div",{ref:"singleElRef",class:`${l}-base-selection-label`,tabindex:this.disabled?void 0:0},void 0!==this.label?li("div",{class:`${l}-base-selection-input`,title:mg(this.label),key:"input"},li("div",{class:`${l}-base-selection-input__content`},u?u({option:this.selectedOption,handleClose:()=>{}}):d?d(this.selectedOption,!0):hg(this.label,this.selectedOption,!0))):li("div",{class:`${l}-base-selection-placeholder ${l}-base-selection-overlay`,key:"placeholder"},li("div",{class:`${l}-base-selection-placeholder__inner`},this.placeholder)),v);return li("div",{ref:"selfRef",class:[`${l}-base-selection`,this.rtlEnabled&&`${l}-base-selection--rtl`,this.themeClass,e&&`${l}-base-selection--${e}-status`,{[`${l}-base-selection--active`]:this.active,[`${l}-base-selection--selected`]:this.selected||this.active&&this.pattern,[`${l}-base-selection--disabled`]:this.disabled,[`${l}-base-selection--multiple`]:this.multiple,[`${l}-base-selection--focus`]:this.focused}],style:this.cssVars,onClick:this.onClick,onMouseenter:this.handleMouseEnter,onMouseleave:this.handleMouseLeave,onKeydown:this.onKeydown,onFocusin:this.handleFocusin,onFocusout:this.handleFocusout,onMousedown:this.handleMouseDown},m,a?li("div",{class:`${l}-base-selection__border`}):null,a?li("div",{class:`${l}-base-selection__state-border`}):null)}}),{cubicBezierEaseInOut:tE}=A_,nE={iconMargin:"11px 8px 0 12px",iconMarginRtl:"11px 12px 0 8px",iconSize:"24px",closeIconSize:"16px",closeSize:"20px",closeMargin:"13px 14px 0 0",closeMarginRtl:"13px 0 0 14px",padding:"13px"},oE={name:"Alert",common:tz,self(e){const{lineHeight:t,borderRadius:n,fontWeightStrong:o,dividerColor:r,inputColor:i,textColor1:a,textColor2:l,closeColorHover:s,closeColorPressed:c,closeIconColor:u,closeIconColorHover:d,closeIconColorPressed:p,infoColorSuppl:h,successColorSuppl:f,warningColorSuppl:v,errorColorSuppl:m,fontSize:g}=e;return Object.assign(Object.assign({},nE),{fontSize:g,lineHeight:t,titleFontWeight:o,borderRadius:n,border:`1px solid ${r}`,color:i,titleTextColor:a,iconColor:l,contentTextColor:l,closeBorderRadius:n,closeColorHover:s,closeColorPressed:c,closeIconColor:u,closeIconColorHover:d,closeIconColorPressed:p,borderInfo:`1px solid ${tg(h,{alpha:.35})}`,colorInfo:tg(h,{alpha:.25}),titleTextColorInfo:a,iconColorInfo:h,contentTextColorInfo:l,closeColorHoverInfo:s,closeColorPressedInfo:c,closeIconColorInfo:u,closeIconColorHoverInfo:d,closeIconColorPressedInfo:p,borderSuccess:`1px solid ${tg(f,{alpha:.35})}`,colorSuccess:tg(f,{alpha:.25}),titleTextColorSuccess:a,iconColorSuccess:f,contentTextColorSuccess:l,closeColorHoverSuccess:s,closeColorPressedSuccess:c,closeIconColorSuccess:u,closeIconColorHoverSuccess:d,closeIconColorPressedSuccess:p,borderWarning:`1px solid ${tg(v,{alpha:.35})}`,colorWarning:tg(v,{alpha:.25}),titleTextColorWarning:a,iconColorWarning:v,contentTextColorWarning:l,closeColorHoverWarning:s,closeColorPressedWarning:c,closeIconColorWarning:u,closeIconColorHoverWarning:d,closeIconColorPressedWarning:p,borderError:`1px solid ${tg(m,{alpha:.35})}`,colorError:tg(m,{alpha:.25}),titleTextColorError:a,iconColorError:m,contentTextColorError:l,closeColorHoverError:s,closeColorPressedError:c,closeIconColorError:u,closeIconColorHoverError:d,closeIconColorPressedError:p})}},rE={name:"Alert",common:qz,self:function(e){const{lineHeight:t,borderRadius:n,fontWeightStrong:o,baseColor:r,dividerColor:i,actionColor:a,textColor1:l,textColor2:s,closeColorHover:c,closeColorPressed:u,closeIconColor:d,closeIconColorHover:p,closeIconColorPressed:h,infoColor:f,successColor:v,warningColor:m,errorColor:g,fontSize:b}=e;return Object.assign(Object.assign({},nE),{fontSize:b,lineHeight:t,titleFontWeight:o,borderRadius:n,border:`1px solid ${i}`,color:a,titleTextColor:l,iconColor:s,contentTextColor:s,closeBorderRadius:n,closeColorHover:c,closeColorPressed:u,closeIconColor:d,closeIconColorHover:p,closeIconColorPressed:h,borderInfo:`1px solid ${eg(r,tg(f,{alpha:.25}))}`,colorInfo:eg(r,tg(f,{alpha:.08})),titleTextColorInfo:l,iconColorInfo:f,contentTextColorInfo:s,closeColorHoverInfo:c,closeColorPressedInfo:u,closeIconColorInfo:d,closeIconColorHoverInfo:p,closeIconColorPressedInfo:h,borderSuccess:`1px solid ${eg(r,tg(v,{alpha:.25}))}`,colorSuccess:eg(r,tg(v,{alpha:.08})),titleTextColorSuccess:l,iconColorSuccess:v,contentTextColorSuccess:s,closeColorHoverSuccess:c,closeColorPressedSuccess:u,closeIconColorSuccess:d,closeIconColorHoverSuccess:p,closeIconColorPressedSuccess:h,borderWarning:`1px solid ${eg(r,tg(m,{alpha:.33}))}`,colorWarning:eg(r,tg(m,{alpha:.08})),titleTextColorWarning:l,iconColorWarning:m,contentTextColorWarning:s,closeColorHoverWarning:c,closeColorPressedWarning:u,closeIconColorWarning:d,closeIconColorHoverWarning:p,closeIconColorPressedWarning:h,borderError:`1px solid ${eg(r,tg(g,{alpha:.25}))}`,colorError:eg(r,tg(g,{alpha:.08})),titleTextColorError:l,iconColorError:g,contentTextColorError:s,closeColorHoverError:c,closeColorPressedError:u,closeIconColorError:d,closeIconColorHoverError:p,closeIconColorPressedError:h})}},{cubicBezierEaseInOut:iE,cubicBezierEaseOut:aE,cubicBezierEaseIn:lE}=A_;function sE({overflow:e="hidden",duration:t=".3s",originalTransition:n="",leavingDelay:o="0s",foldPadding:r=!1,enterToProps:i,leaveToProps:a,reverse:l=!1}={}){const s=l?"leave":"enter",c=l?"enter":"leave";return[eb(`&.fade-in-height-expand-transition-${c}-from,\n &.fade-in-height-expand-transition-${s}-to`,Object.assign(Object.assign({},i),{opacity:1})),eb(`&.fade-in-height-expand-transition-${c}-to,\n &.fade-in-height-expand-transition-${s}-from`,Object.assign(Object.assign({},a),{opacity:0,marginTop:"0 !important",marginBottom:"0 !important",paddingTop:r?"0 !important":void 0,paddingBottom:r?"0 !important":void 0})),eb(`&.fade-in-height-expand-transition-${c}-active`,`\n overflow: ${e};\n transition:\n max-height ${t} ${iE} ${o},\n opacity ${t} ${aE} ${o},\n margin-top ${t} ${iE} ${o},\n margin-bottom ${t} ${iE} ${o},\n padding-top ${t} ${iE} ${o},\n padding-bottom ${t} ${iE} ${o}\n ${n?`,${n}`:""}\n `),eb(`&.fade-in-height-expand-transition-${s}-active`,`\n overflow: ${e};\n transition:\n max-height ${t} ${iE},\n opacity ${t} ${lE},\n margin-top ${t} ${iE},\n margin-bottom ${t} ${iE},\n padding-top ${t} ${iE},\n padding-bottom ${t} ${iE}\n ${n?`,${n}`:""}\n `)]}const cE=nb("alert","\n line-height: var(--n-line-height);\n border-radius: var(--n-border-radius);\n position: relative;\n transition: background-color .3s var(--n-bezier);\n background-color: var(--n-color);\n text-align: start;\n word-break: break-word;\n",[ob("border","\n border-radius: inherit;\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n transition: border-color .3s var(--n-bezier);\n border: var(--n-border);\n pointer-events: none;\n "),rb("closable",[nb("alert-body",[ob("title","\n padding-right: 24px;\n ")])]),ob("icon",{color:"var(--n-icon-color)"}),nb("alert-body",{padding:"var(--n-padding)"},[ob("title",{color:"var(--n-title-text-color)"}),ob("content",{color:"var(--n-content-text-color)"})]),sE({originalTransition:"transform .3s var(--n-bezier)",enterToProps:{transform:"scale(1)"},leaveToProps:{transform:"scale(0.9)"}}),ob("icon","\n position: absolute;\n left: 0;\n top: 0;\n align-items: center;\n justify-content: center;\n display: flex;\n width: var(--n-icon-size);\n height: var(--n-icon-size);\n font-size: var(--n-icon-size);\n margin: var(--n-icon-margin);\n "),ob("close","\n transition:\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n position: absolute;\n right: 0;\n top: 0;\n margin: var(--n-close-margin);\n "),rb("show-icon",[nb("alert-body",{paddingLeft:"calc(var(--n-icon-margin-left) + var(--n-icon-size) + var(--n-icon-margin-right))"})]),rb("right-adjust",[nb("alert-body",{paddingRight:"calc(var(--n-close-size) + var(--n-padding) + 2px)"})]),nb("alert-body","\n border-radius: var(--n-border-radius);\n transition: border-color .3s var(--n-bezier);\n ",[ob("title","\n transition: color .3s var(--n-bezier);\n font-size: 16px;\n line-height: 19px;\n font-weight: var(--n-title-font-weight);\n ",[eb("& +",[ob("content",{marginTop:"9px"})])]),ob("content",{transition:"color .3s var(--n-bezier)",fontSize:"var(--n-font-size)"})]),ob("icon",{transition:"color .3s var(--n-bezier)"})]),uE=zn({name:"Alert",inheritAttrs:!1,props:Object.assign(Object.assign({},I_.props),{title:String,showIcon:{type:Boolean,default:!0},type:{type:String,default:"default"},bordered:{type:Boolean,default:!0},closable:Boolean,onClose:Function,onAfterLeave:Function,onAfterHide:Function}),setup(e){const{mergedClsPrefixRef:t,mergedBorderedRef:n,inlineThemeDisabled:o,mergedRtlRef:r}=B_(e),i=I_("Alert","-alert",cE,rE,e,t),a=GP("Alert",r,t),l=ai((()=>{const{common:{cubicBezierEaseInOut:t},self:n}=i.value,{fontSize:o,borderRadius:r,titleFontWeight:a,lineHeight:l,iconSize:s,iconMargin:c,iconMarginRtl:u,closeIconSize:d,closeBorderRadius:p,closeSize:h,closeMargin:f,closeMarginRtl:v,padding:m}=n,{type:g}=e,{left:b,right:y}=Bm(c);return{"--n-bezier":t,"--n-color":n[ub("color",g)],"--n-close-icon-size":d,"--n-close-border-radius":p,"--n-close-color-hover":n[ub("closeColorHover",g)],"--n-close-color-pressed":n[ub("closeColorPressed",g)],"--n-close-icon-color":n[ub("closeIconColor",g)],"--n-close-icon-color-hover":n[ub("closeIconColorHover",g)],"--n-close-icon-color-pressed":n[ub("closeIconColorPressed",g)],"--n-icon-color":n[ub("iconColor",g)],"--n-border":n[ub("border",g)],"--n-title-text-color":n[ub("titleTextColor",g)],"--n-content-text-color":n[ub("contentTextColor",g)],"--n-line-height":l,"--n-border-radius":r,"--n-font-size":o,"--n-title-font-weight":a,"--n-icon-size":s,"--n-icon-margin":c,"--n-icon-margin-rtl":u,"--n-close-size":h,"--n-close-margin":f,"--n-close-margin-rtl":v,"--n-padding":m,"--n-icon-margin-left":b,"--n-icon-margin-right":y}})),s=o?KP("alert",ai((()=>e.type[0])),l,e):void 0,c=Et(!0);return{rtlEnabled:a,mergedClsPrefix:t,mergedBordered:n,visible:c,handleCloseClick:()=>{var t;Promise.resolve(null===(t=e.onClose)||void 0===t?void 0:t.call(e)).then((e=>{!1!==e&&(c.value=!1)}))},handleAfterLeave:()=>{(()=>{const{onAfterLeave:t,onAfterHide:n}=e;t&&t(),n&&n()})()},mergedTheme:i,cssVars:o?void 0:l,themeClass:null==s?void 0:s.themeClass,onRender:null==s?void 0:s.onRender}},render(){var e;return null===(e=this.onRender)||void 0===e||e.call(this),li(yT,{onAfterLeave:this.handleAfterLeave},{default:()=>{const{mergedClsPrefix:e,$slots:t}=this,n={class:[`${e}-alert`,this.themeClass,this.closable&&`${e}-alert--closable`,this.showIcon&&`${e}-alert--show-icon`,!this.title&&this.closable&&`${e}-alert--right-adjust`,this.rtlEnabled&&`${e}-alert--rtl`],style:this.cssVars,role:"alert"};return this.visible?li("div",Object.assign({},Wr(this.$attrs,n)),this.closable&&li(kT,{clsPrefix:e,class:`${e}-alert__close`,onClick:this.handleCloseClick}),this.bordered&&li("div",{class:`${e}-alert__border`}),this.showIcon&&li("div",{class:`${e}-alert__icon`,"aria-hidden":"true"},xg(t.icon,(()=>[li(CT,{clsPrefix:e},{default:()=>{switch(this.type){case"success":return li(hT,null);case"info":return li(uT,null);case"warning":return li(fT,null);case"error":return li(iT,null);default:return null}}})]))),li("div",{class:[`${e}-alert-body`,this.mergedBordered&&`${e}-alert-body--bordered`]},wg(t.header,(t=>{const n=t||this.title;return n?li("div",{class:`${e}-alert-body__title`},n):null})),t.default&&li("div",{class:`${e}-alert-body__content`},t))):null}})}}),dE={linkFontSize:"13px",linkPadding:"0 0 0 16px",railWidth:"4px"},pE={name:"Anchor",common:tz,self:function(e){const{borderRadius:t,railColor:n,primaryColor:o,primaryColorHover:r,primaryColorPressed:i,textColor2:a}=e;return Object.assign(Object.assign({},dE),{borderRadius:t,railColor:n,railColorActive:o,linkColor:tg(o,{alpha:.15}),linkTextColor:a,linkTextColorHover:r,linkTextColorPressed:i,linkTextColorActive:o})}};function hE(e){return"group"===e.type}function fE(e){return"ignored"===e.type}function vE(e,t){try{return!!(1+t.toString().toLowerCase().indexOf(e.trim().toLowerCase()))}catch(Cb){return!1}}function mE(e,t){return{getIsGroup:hE,getIgnored:fE,getKey:t=>hE(t)?t.name||t.key||"key-required":t[e],getChildren:e=>e[t]}}const gE=pb&&"chrome"in window;pb&&navigator.userAgent.includes("Firefox");const bE=pb&&navigator.userAgent.includes("Safari")&&!gE,yE={paddingTiny:"0 8px",paddingSmall:"0 10px",paddingMedium:"0 12px",paddingLarge:"0 14px",clearSize:"16px"},xE={name:"Input",common:tz,self(e){const{textColor2:t,textColor3:n,textColorDisabled:o,primaryColor:r,primaryColorHover:i,inputColor:a,inputColorDisabled:l,warningColor:s,warningColorHover:c,errorColor:u,errorColorHover:d,borderRadius:p,lineHeight:h,fontSizeTiny:f,fontSizeSmall:v,fontSizeMedium:m,fontSizeLarge:g,heightTiny:b,heightSmall:y,heightMedium:x,heightLarge:C,clearColor:w,clearColorHover:k,clearColorPressed:S,placeholderColor:_,placeholderColorDisabled:P,iconColor:T,iconColorDisabled:A,iconColorHover:z,iconColorPressed:R}=e;return Object.assign(Object.assign({},yE),{countTextColorDisabled:o,countTextColor:n,heightTiny:b,heightSmall:y,heightMedium:x,heightLarge:C,fontSizeTiny:f,fontSizeSmall:v,fontSizeMedium:m,fontSizeLarge:g,lineHeight:h,lineHeightTextarea:h,borderRadius:p,iconSize:"16px",groupLabelColor:a,textColor:t,textColorDisabled:o,textDecorationColor:t,groupLabelTextColor:t,caretColor:r,placeholderColor:_,placeholderColorDisabled:P,color:a,colorDisabled:l,colorFocus:tg(r,{alpha:.1}),groupLabelBorder:"1px solid #0000",border:"1px solid #0000",borderHover:`1px solid ${i}`,borderDisabled:"1px solid #0000",borderFocus:`1px solid ${i}`,boxShadowFocus:`0 0 8px 0 ${tg(r,{alpha:.3})}`,loadingColor:r,loadingColorWarning:s,borderWarning:`1px solid ${s}`,borderHoverWarning:`1px solid ${c}`,colorFocusWarning:tg(s,{alpha:.1}),borderFocusWarning:`1px solid ${c}`,boxShadowFocusWarning:`0 0 8px 0 ${tg(s,{alpha:.3})}`,caretColorWarning:s,loadingColorError:u,borderError:`1px solid ${u}`,borderHoverError:`1px solid ${d}`,colorFocusError:tg(u,{alpha:.1}),borderFocusError:`1px solid ${d}`,boxShadowFocusError:`0 0 8px 0 ${tg(u,{alpha:.3})}`,caretColorError:u,clearColor:w,clearColorHover:k,clearColorPressed:S,iconColor:T,iconColorDisabled:A,iconColorHover:z,iconColorPressed:R,suffixTextColor:t})}},CE={name:"Input",common:qz,self:function(e){const{textColor2:t,textColor3:n,textColorDisabled:o,primaryColor:r,primaryColorHover:i,inputColor:a,inputColorDisabled:l,borderColor:s,warningColor:c,warningColorHover:u,errorColor:d,errorColorHover:p,borderRadius:h,lineHeight:f,fontSizeTiny:v,fontSizeSmall:m,fontSizeMedium:g,fontSizeLarge:b,heightTiny:y,heightSmall:x,heightMedium:C,heightLarge:w,actionColor:k,clearColor:S,clearColorHover:_,clearColorPressed:P,placeholderColor:T,placeholderColorDisabled:A,iconColor:z,iconColorDisabled:R,iconColorHover:E,iconColorPressed:O}=e;return Object.assign(Object.assign({},yE),{countTextColorDisabled:o,countTextColor:n,heightTiny:y,heightSmall:x,heightMedium:C,heightLarge:w,fontSizeTiny:v,fontSizeSmall:m,fontSizeMedium:g,fontSizeLarge:b,lineHeight:f,lineHeightTextarea:f,borderRadius:h,iconSize:"16px",groupLabelColor:k,groupLabelTextColor:t,textColor:t,textColorDisabled:o,textDecorationColor:t,caretColor:r,placeholderColor:T,placeholderColorDisabled:A,color:a,colorDisabled:l,colorFocus:a,groupLabelBorder:`1px solid ${s}`,border:`1px solid ${s}`,borderHover:`1px solid ${i}`,borderDisabled:`1px solid ${s}`,borderFocus:`1px solid ${i}`,boxShadowFocus:`0 0 0 2px ${tg(r,{alpha:.2})}`,loadingColor:r,loadingColorWarning:c,borderWarning:`1px solid ${c}`,borderHoverWarning:`1px solid ${u}`,colorFocusWarning:a,borderFocusWarning:`1px solid ${u}`,boxShadowFocusWarning:`0 0 0 2px ${tg(c,{alpha:.2})}`,caretColorWarning:c,loadingColorError:d,borderError:`1px solid ${d}`,borderHoverError:`1px solid ${p}`,colorFocusError:a,borderFocusError:`1px solid ${p}`,boxShadowFocusError:`0 0 0 2px ${tg(d,{alpha:.2})}`,caretColorError:d,clearColor:S,clearColorHover:_,clearColorPressed:P,iconColor:z,iconColorDisabled:R,iconColorHover:E,iconColorPressed:O,suffixTextColor:t})}},wE="n-input";function kE(e){let t=0;for(const n of e)t++;return t}function SE(e){return""===e||null==e}const _E=zn({name:"InputWordCount",setup(e,{slots:t}){const{mergedValueRef:n,maxlengthRef:o,mergedClsPrefixRef:r,countGraphemesRef:i}=Po(wE),a=ai((()=>{const{value:e}=n;return null===e||Array.isArray(e)?0:(i.value||kE)(e)}));return()=>{const{value:e}=o,{value:i}=n;return li("span",{class:`${r.value}-input-word-count`},Cg(t.default,{value:null===i||Array.isArray(i)?"":i},(()=>[void 0===e?a.value:`${a.value} / ${e}`])))}}}),PE=nb("input","\n max-width: 100%;\n cursor: text;\n line-height: 1.5;\n z-index: auto;\n outline: none;\n box-sizing: border-box;\n position: relative;\n display: inline-flex;\n border-radius: var(--n-border-radius);\n background-color: var(--n-color);\n transition: background-color .3s var(--n-bezier);\n font-size: var(--n-font-size);\n --n-padding-vertical: calc((var(--n-height) - 1.5 * var(--n-font-size)) / 2);\n",[ob("input, textarea","\n overflow: hidden;\n flex-grow: 1;\n position: relative;\n "),ob("input-el, textarea-el, input-mirror, textarea-mirror, separator, placeholder","\n box-sizing: border-box;\n font-size: inherit;\n line-height: 1.5;\n font-family: inherit;\n border: none;\n outline: none;\n background-color: #0000;\n text-align: inherit;\n transition:\n -webkit-text-fill-color .3s var(--n-bezier),\n caret-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n text-decoration-color .3s var(--n-bezier);\n "),ob("input-el, textarea-el","\n -webkit-appearance: none;\n scrollbar-width: none;\n width: 100%;\n min-width: 0;\n text-decoration-color: var(--n-text-decoration-color);\n color: var(--n-text-color);\n caret-color: var(--n-caret-color);\n background-color: transparent;\n ",[eb("&::-webkit-scrollbar, &::-webkit-scrollbar-track-piece, &::-webkit-scrollbar-thumb","\n width: 0;\n height: 0;\n display: none;\n "),eb("&::placeholder","\n color: #0000;\n -webkit-text-fill-color: transparent !important;\n "),eb("&:-webkit-autofill ~",[ob("placeholder","display: none;")])]),rb("round",[ib("textarea","border-radius: calc(var(--n-height) / 2);")]),ob("placeholder","\n pointer-events: none;\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n overflow: hidden;\n color: var(--n-placeholder-color);\n ",[eb("span","\n width: 100%;\n display: inline-block;\n ")]),rb("textarea",[ob("placeholder","overflow: visible;")]),ib("autosize","width: 100%;"),rb("autosize",[ob("textarea-el, input-el","\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n ")]),nb("input-wrapper","\n overflow: hidden;\n display: inline-flex;\n flex-grow: 1;\n position: relative;\n padding-left: var(--n-padding-left);\n padding-right: var(--n-padding-right);\n "),ob("input-mirror","\n padding: 0;\n height: var(--n-height);\n line-height: var(--n-height);\n overflow: hidden;\n visibility: hidden;\n position: static;\n white-space: pre;\n pointer-events: none;\n "),ob("input-el","\n padding: 0;\n height: var(--n-height);\n line-height: var(--n-height);\n ",[eb("&[type=password]::-ms-reveal","display: none;"),eb("+",[ob("placeholder","\n display: flex;\n align-items: center; \n ")])]),ib("textarea",[ob("placeholder","white-space: nowrap;")]),ob("eye","\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color .3s var(--n-bezier);\n "),rb("textarea","width: 100%;",[nb("input-word-count","\n position: absolute;\n right: var(--n-padding-right);\n bottom: var(--n-padding-vertical);\n "),rb("resizable",[nb("input-wrapper","\n resize: vertical;\n min-height: var(--n-height);\n ")]),ob("textarea-el, textarea-mirror, placeholder","\n height: 100%;\n padding-left: 0;\n padding-right: 0;\n padding-top: var(--n-padding-vertical);\n padding-bottom: var(--n-padding-vertical);\n word-break: break-word;\n display: inline-block;\n vertical-align: bottom;\n box-sizing: border-box;\n line-height: var(--n-line-height-textarea);\n margin: 0;\n resize: none;\n white-space: pre-wrap;\n scroll-padding-block-end: var(--n-padding-vertical);\n "),ob("textarea-mirror","\n width: 100%;\n pointer-events: none;\n overflow: hidden;\n visibility: hidden;\n position: static;\n white-space: pre-wrap;\n overflow-wrap: break-word;\n ")]),rb("pair",[ob("input-el, placeholder","text-align: center;"),ob("separator","\n display: flex;\n align-items: center;\n transition: color .3s var(--n-bezier);\n color: var(--n-text-color);\n white-space: nowrap;\n ",[nb("icon","\n color: var(--n-icon-color);\n "),nb("base-icon","\n color: var(--n-icon-color);\n ")])]),rb("disabled","\n cursor: not-allowed;\n background-color: var(--n-color-disabled);\n ",[ob("border","border: var(--n-border-disabled);"),ob("input-el, textarea-el","\n cursor: not-allowed;\n color: var(--n-text-color-disabled);\n text-decoration-color: var(--n-text-color-disabled);\n "),ob("placeholder","color: var(--n-placeholder-color-disabled);"),ob("separator","color: var(--n-text-color-disabled);",[nb("icon","\n color: var(--n-icon-color-disabled);\n "),nb("base-icon","\n color: var(--n-icon-color-disabled);\n ")]),nb("input-word-count","\n color: var(--n-count-text-color-disabled);\n "),ob("suffix, prefix","color: var(--n-text-color-disabled);",[nb("icon","\n color: var(--n-icon-color-disabled);\n "),nb("internal-icon","\n color: var(--n-icon-color-disabled);\n ")])]),ib("disabled",[ob("eye","\n color: var(--n-icon-color);\n cursor: pointer;\n ",[eb("&:hover","\n color: var(--n-icon-color-hover);\n "),eb("&:active","\n color: var(--n-icon-color-pressed);\n ")]),eb("&:hover",[ob("state-border","border: var(--n-border-hover);")]),rb("focus","background-color: var(--n-color-focus);",[ob("state-border","\n border: var(--n-border-focus);\n box-shadow: var(--n-box-shadow-focus);\n ")])]),ob("border, state-border","\n box-sizing: border-box;\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n pointer-events: none;\n border-radius: inherit;\n border: var(--n-border);\n transition:\n box-shadow .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n "),ob("state-border","\n border-color: #0000;\n z-index: 1;\n "),ob("prefix","margin-right: 4px;"),ob("suffix","\n margin-left: 4px;\n "),ob("suffix, prefix","\n transition: color .3s var(--n-bezier);\n flex-wrap: nowrap;\n flex-shrink: 0;\n line-height: var(--n-height);\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--n-suffix-text-color);\n ",[nb("base-loading","\n font-size: var(--n-icon-size);\n margin: 0 2px;\n color: var(--n-loading-color);\n "),nb("base-clear","\n font-size: var(--n-icon-size);\n ",[ob("placeholder",[nb("base-icon","\n transition: color .3s var(--n-bezier);\n color: var(--n-icon-color);\n font-size: var(--n-icon-size);\n ")])]),eb(">",[nb("icon","\n transition: color .3s var(--n-bezier);\n color: var(--n-icon-color);\n font-size: var(--n-icon-size);\n ")]),nb("base-icon","\n font-size: var(--n-icon-size);\n ")]),nb("input-word-count","\n pointer-events: none;\n line-height: 1.5;\n font-size: .85em;\n color: var(--n-count-text-color);\n transition: color .3s var(--n-bezier);\n margin-left: 4px;\n font-variant: tabular-nums;\n "),["warning","error"].map((e=>rb(`${e}-status`,[ib("disabled",[nb("base-loading",`\n color: var(--n-loading-color-${e})\n `),ob("input-el, textarea-el",`\n caret-color: var(--n-caret-color-${e});\n `),ob("state-border",`\n border: var(--n-border-${e});\n `),eb("&:hover",[ob("state-border",`\n border: var(--n-border-hover-${e});\n `)]),eb("&:focus",`\n background-color: var(--n-color-focus-${e});\n `,[ob("state-border",`\n box-shadow: var(--n-box-shadow-focus-${e});\n border: var(--n-border-focus-${e});\n `)]),rb("focus",`\n background-color: var(--n-color-focus-${e});\n `,[ob("state-border",`\n box-shadow: var(--n-box-shadow-focus-${e});\n border: var(--n-border-focus-${e});\n `)])])])))]),TE=nb("input",[rb("disabled",[ob("input-el, textarea-el","\n -webkit-text-fill-color: var(--n-text-color-disabled);\n ")])]),AE=zn({name:"Input",props:Object.assign(Object.assign({},I_.props),{bordered:{type:Boolean,default:void 0},type:{type:String,default:"text"},placeholder:[Array,String],defaultValue:{type:[String,Array],default:null},value:[String,Array],disabled:{type:Boolean,default:void 0},size:String,rows:{type:[Number,String],default:3},round:Boolean,minlength:[String,Number],maxlength:[String,Number],clearable:Boolean,autosize:{type:[Boolean,Object],default:!1},pair:Boolean,separator:String,readonly:{type:[String,Boolean],default:!1},passivelyActivated:Boolean,showPasswordOn:String,stateful:{type:Boolean,default:!0},autofocus:Boolean,inputProps:Object,resizable:{type:Boolean,default:!0},showCount:Boolean,loading:{type:Boolean,default:void 0},allowInput:Function,renderCount:Function,onMousedown:Function,onKeydown:Function,onKeyup:[Function,Array],onInput:[Function,Array],onFocus:[Function,Array],onBlur:[Function,Array],onClick:[Function,Array],onChange:[Function,Array],onClear:[Function,Array],countGraphemes:Function,status:String,"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array],textDecoration:[String,Array],attrSize:{type:Number,default:20},onInputBlur:[Function,Array],onInputFocus:[Function,Array],onDeactivate:[Function,Array],onActivate:[Function,Array],onWrapperFocus:[Function,Array],onWrapperBlur:[Function,Array],internalDeactivateOnEnter:Boolean,internalForceFocus:Boolean,internalLoadingBeforeSuffix:{type:Boolean,default:!0},showPasswordToggle:Boolean}),setup(e){const{mergedClsPrefixRef:t,mergedBorderedRef:n,inlineThemeDisabled:o,mergedRtlRef:r}=B_(e),i=I_("Input","-input",PE,CE,e,t);bE&&qP("-input-safari",TE,t);const a=Et(null),l=Et(null),s=Et(null),c=Et(null),u=Et(null),d=Et(null),p=Et(null),h=function(e){const t=Et(null);function n(){t.value=null}return lr(e,n),{recordCursor:function(){const{value:o}=e;if(!(null==o?void 0:o.focus))return void n();const{selectionStart:r,selectionEnd:i,value:a}=o;null!=r&&null!=i?t.value={start:r,end:i,beforeText:a.slice(0,r),afterText:a.slice(i)}:n()},restoreCursor:function(){var n;const{value:o}=t,{value:r}=e;if(!o||!r)return;const{value:i}=r,{start:a,beforeText:l,afterText:s}=o;let c=i.length;if(i.endsWith(s))c=i.length-s.length;else if(i.startsWith(l))c=l.length;else{const e=l[a-1],t=i.indexOf(e,a-1);-1!==t&&(c=t+1)}null===(n=r.setSelectionRange)||void 0===n||n.call(r,c,c)}}}(p),f=Et(null),{localeRef:v}=VP("Input"),m=Et(e.defaultValue),g=Db(jt(e,"value"),m),b=eC(e),{mergedSizeRef:y,mergedDisabledRef:x,mergedStatusRef:C}=b,w=Et(!1),k=Et(!1),S=Et(!1),_=Et(!1);let P=null;const T=ai((()=>{const{placeholder:t,pair:n}=e;return n?Array.isArray(t)?t:void 0===t?["",""]:[t,t]:void 0===t?[v.value.placeholder]:[t]})),A=ai((()=>{const{value:e}=S,{value:t}=g,{value:n}=T;return!e&&(SE(t)||Array.isArray(t)&&SE(t[0]))&&n[0]})),z=ai((()=>{const{value:e}=S,{value:t}=g,{value:n}=T;return!e&&n[1]&&(SE(t)||Array.isArray(t)&&SE(t[1]))})),R=mb((()=>e.internalForceFocus||w.value)),E=mb((()=>{if(x.value||e.readonly||!e.clearable||!R.value&&!k.value)return!1;const{value:t}=g,{value:n}=R;return e.pair?!(!Array.isArray(t)||!t[0]&&!t[1])&&(k.value||n):!!t&&(k.value||n)})),O=ai((()=>{const{showPasswordOn:t}=e;return t||(e.showPasswordToggle?"click":void 0)})),M=Et(!1),F=ai((()=>{const{textDecoration:t}=e;return t?Array.isArray(t)?t.map((e=>({textDecoration:e}))):[{textDecoration:t}]:["",""]})),I=Et(void 0),L=ai((()=>{const{maxlength:t}=e;return void 0===t?void 0:Number(t)}));$n((()=>{const{value:e}=g;Array.isArray(e)||V(e)}));const B=Gr().proxy;function D(t,n){const{onUpdateValue:o,"onUpdate:value":r,onInput:i}=e,{nTriggerFormInput:a}=b;o&&dg(o,t,n),r&&dg(r,t,n),i&&dg(i,t,n),m.value=t,a()}function $(t,n){const{onChange:o}=e,{nTriggerFormChange:r}=b;o&&dg(o,t,n),m.value=t,r()}function N(t,n=0,o="input"){const r=t.target.value;if(V(r),t instanceof InputEvent&&!t.isComposing&&(S.value=!1),"textarea"===e.type){const{value:e}=f;e&&e.syncUnifiedContainer()}if(P=r,S.value)return;h.recordCursor();const i=function(t){const{countGraphemes:n,maxlength:o,minlength:r}=e;if(n){let e;if(void 0!==o&&(void 0===e&&(e=n(t)),e>Number(o)))return!1;if(void 0!==r&&(void 0===e&&(e=n(t)),e{var e;null===(e=a.value)||void 0===e||e.focus()})))}function U(){var t,n,o;x.value||(e.passivelyActivated?null===(t=a.value)||void 0===t||t.focus():(null===(n=l.value)||void 0===n||n.focus(),null===(o=u.value)||void 0===o||o.focus()))}function V(t){const{type:n,pair:o,autosize:r}=e;if(!o&&r)if("textarea"===n){const{value:e}=s;e&&(e.textContent=`${null!=t?t:""}\r\n`)}else{const{value:e}=c;e&&(t?e.textContent=t:e.innerHTML=" ")}}const q=Et({top:"0"});let K=null;ir((()=>{const{autosize:t,type:n}=e;t&&"textarea"===n?K=lr(g,(e=>{Array.isArray(e)||e===P||V(e)})):null==K||K()}));let G=null;ir((()=>{"textarea"===e.type?G=lr(g,(e=>{var t;Array.isArray(e)||e===P||null===(t=f.value)||void 0===t||t.syncUnifiedContainer()})):null==G||G()})),_o(wE,{mergedValueRef:g,maxlengthRef:L,mergedClsPrefixRef:t,countGraphemesRef:jt(e,"countGraphemes")});const X={wrapperElRef:a,inputElRef:u,textareaElRef:l,isCompositing:S,clear:H,focus:U,blur:function(){var e;(null===(e=a.value)||void 0===e?void 0:e.contains(document.activeElement))&&document.activeElement.blur()},select:function(){var e,t;null===(e=l.value)||void 0===e||e.select(),null===(t=u.value)||void 0===t||t.select()},deactivate:function(){const{value:e}=a;(null==e?void 0:e.contains(document.activeElement))&&e!==document.activeElement&&W()},activate:function(){x.value||(l.value?l.value.focus():u.value&&u.value.focus())},scrollTo:function(t){if("textarea"===e.type){const{value:e}=l;null==e||e.scrollTo(t)}else{const{value:e}=u;null==e||e.scrollTo(t)}}},Y=GP("Input",r,t),Q=ai((()=>{const{value:e}=y,{common:{cubicBezierEaseInOut:t},self:{color:n,borderRadius:o,textColor:r,caretColor:a,caretColorError:l,caretColorWarning:s,textDecorationColor:c,border:u,borderDisabled:d,borderHover:p,borderFocus:h,placeholderColor:f,placeholderColorDisabled:v,lineHeightTextarea:m,colorDisabled:g,colorFocus:b,textColorDisabled:x,boxShadowFocus:C,iconSize:w,colorFocusWarning:k,boxShadowFocusWarning:S,borderWarning:_,borderFocusWarning:P,borderHoverWarning:T,colorFocusError:A,boxShadowFocusError:z,borderError:R,borderFocusError:E,borderHoverError:O,clearSize:M,clearColor:F,clearColorHover:I,clearColorPressed:L,iconColor:B,iconColorDisabled:D,suffixTextColor:$,countTextColor:N,countTextColorDisabled:j,iconColorHover:H,iconColorPressed:W,loadingColor:U,loadingColorError:V,loadingColorWarning:q,[ub("padding",e)]:K,[ub("fontSize",e)]:G,[ub("height",e)]:X}}=i.value,{left:Y,right:Q}=Bm(K);return{"--n-bezier":t,"--n-count-text-color":N,"--n-count-text-color-disabled":j,"--n-color":n,"--n-font-size":G,"--n-border-radius":o,"--n-height":X,"--n-padding-left":Y,"--n-padding-right":Q,"--n-text-color":r,"--n-caret-color":a,"--n-text-decoration-color":c,"--n-border":u,"--n-border-disabled":d,"--n-border-hover":p,"--n-border-focus":h,"--n-placeholder-color":f,"--n-placeholder-color-disabled":v,"--n-icon-size":w,"--n-line-height-textarea":m,"--n-color-disabled":g,"--n-color-focus":b,"--n-text-color-disabled":x,"--n-box-shadow-focus":C,"--n-loading-color":U,"--n-caret-color-warning":s,"--n-color-focus-warning":k,"--n-box-shadow-focus-warning":S,"--n-border-warning":_,"--n-border-focus-warning":P,"--n-border-hover-warning":T,"--n-loading-color-warning":q,"--n-caret-color-error":l,"--n-color-focus-error":A,"--n-box-shadow-focus-error":z,"--n-border-error":R,"--n-border-focus-error":E,"--n-border-hover-error":O,"--n-loading-color-error":V,"--n-clear-color":F,"--n-clear-size":M,"--n-clear-color-hover":I,"--n-clear-color-pressed":L,"--n-icon-color":B,"--n-icon-color-hover":H,"--n-icon-color-pressed":W,"--n-icon-color-disabled":D,"--n-suffix-text-color":$}})),Z=o?KP("input",ai((()=>{const{value:e}=y;return e[0]})),Q,e):void 0;return Object.assign(Object.assign({},X),{wrapperElRef:a,inputElRef:u,inputMirrorElRef:c,inputEl2Ref:d,textareaElRef:l,textareaMirrorElRef:s,textareaScrollbarInstRef:f,rtlEnabled:Y,uncontrolledValue:m,mergedValue:g,passwordVisible:M,mergedPlaceholder:T,showPlaceholder1:A,showPlaceholder2:z,mergedFocus:R,isComposing:S,activated:_,showClearButton:E,mergedSize:y,mergedDisabled:x,textDecorationStyle:F,mergedClsPrefix:t,mergedBordered:n,mergedShowPasswordOn:O,placeholderStyle:q,mergedStatus:C,textAreaScrollContainerWidth:I,handleTextAreaScroll:function(e){var t;const{scrollTop:n}=e.target;q.value.top=-n+"px",null===(t=f.value)||void 0===t||t.syncUnifiedContainer()},handleCompositionStart:function(){S.value=!0},handleCompositionEnd:function(e){S.value=!1,e.target===d.value?N(e,1):N(e,0)},handleInput:N,handleInputBlur:function(t){!function(t){const{onInputBlur:n}=e;n&&dg(n,t)}(t),t.relatedTarget===a.value&&function(){const{onDeactivate:t}=e;t&&dg(t)}(),(null===t.relatedTarget||t.relatedTarget!==u.value&&t.relatedTarget!==d.value&&t.relatedTarget!==l.value)&&(_.value=!1),j(t,"blur"),p.value=null},handleInputFocus:function(t,n){!function(t){const{onInputFocus:n}=e;n&&dg(n,t)}(t),w.value=!0,_.value=!0,function(){const{onActivate:t}=e;t&&dg(t)}(),j(t,"focus"),0===n?p.value=u.value:1===n?p.value=d.value:2===n&&(p.value=l.value)},handleWrapperBlur:function(t){e.passivelyActivated&&(function(t){const{onWrapperBlur:n}=e;n&&dg(n,t)}(t),j(t,"blur"))},handleWrapperFocus:function(t){e.passivelyActivated&&(w.value=!0,function(t){const{onWrapperFocus:n}=e;n&&dg(n,t)}(t),j(t,"focus"))},handleMouseEnter:function(){var t;k.value=!0,"textarea"===e.type&&(null===(t=f.value)||void 0===t||t.handleMouseEnterWrapper())},handleMouseLeave:function(){var t;k.value=!1,"textarea"===e.type&&(null===(t=f.value)||void 0===t||t.handleMouseLeaveWrapper())},handleMouseDown:function(t){const{onMousedown:n}=e;n&&n(t);const{tagName:o}=t.target;if("INPUT"!==o&&"TEXTAREA"!==o){if(e.resizable){const{value:e}=a;if(e){const{left:n,top:o,width:r,height:i}=e.getBoundingClientRect(),a=14;if(n+r-a{e.preventDefault(),Tb("mouseup",document,t)};if(Pb("mouseup",document,t),"mousedown"!==O.value)return;M.value=!0;const n=()=>{M.value=!1,Tb("mouseup",document,n)};Pb("mouseup",document,n)},handleWrapperKeydown:function(t){switch(e.onKeydown&&dg(e.onKeydown,t),t.key){case"Escape":W();break;case"Enter":!function(t){var n,o;if(e.passivelyActivated){const{value:r}=_;if(r)return void(e.internalDeactivateOnEnter&&W());t.preventDefault(),"textarea"===e.type?null===(n=l.value)||void 0===n||n.focus():null===(o=u.value)||void 0===o||o.focus()}}(t)}},handleWrapperKeyup:function(t){e.onKeyup&&dg(e.onKeyup,t)},handleTextAreaMirrorResize:function(){(()=>{var t,n;if("textarea"===e.type){const{autosize:o}=e;if(o&&(I.value=null===(n=null===(t=f.value)||void 0===t?void 0:t.$el)||void 0===n?void 0:n.offsetWidth),!l.value)return;if("boolean"==typeof o)return;const{paddingTop:r,paddingBottom:i,lineHeight:a}=window.getComputedStyle(l.value),c=Number(r.slice(0,-2)),u=Number(i.slice(0,-2)),d=Number(a.slice(0,-2)),{value:p}=s;if(!p)return;if(o.minRows){const e=`${c+u+d*Math.max(o.minRows,1)}px`;p.style.minHeight=e}if(o.maxRows){const e=`${c+u+d*o.maxRows}px`;p.style.maxHeight=e}}})()},getTextareaScrollContainer:()=>l.value,mergedTheme:i,cssVars:o?void 0:Q,themeClass:null==Z?void 0:Z.themeClass,onRender:null==Z?void 0:Z.onRender})},render(){var e,t;const{mergedClsPrefix:n,mergedStatus:o,themeClass:r,type:i,countGraphemes:a,onRender:l}=this,s=this.$slots;return null==l||l(),li("div",{ref:"wrapperElRef",class:[`${n}-input`,r,o&&`${n}-input--${o}-status`,{[`${n}-input--rtl`]:this.rtlEnabled,[`${n}-input--disabled`]:this.mergedDisabled,[`${n}-input--textarea`]:"textarea"===i,[`${n}-input--resizable`]:this.resizable&&!this.autosize,[`${n}-input--autosize`]:this.autosize,[`${n}-input--round`]:this.round&&!("textarea"===i),[`${n}-input--pair`]:this.pair,[`${n}-input--focus`]:this.mergedFocus,[`${n}-input--stateful`]:this.stateful}],style:this.cssVars,tabindex:this.mergedDisabled||!this.passivelyActivated||this.activated?void 0:0,onFocus:this.handleWrapperFocus,onBlur:this.handleWrapperBlur,onClick:this.handleClick,onMousedown:this.handleMouseDown,onMouseenter:this.handleMouseEnter,onMouseleave:this.handleMouseLeave,onCompositionstart:this.handleCompositionStart,onCompositionend:this.handleCompositionEnd,onKeyup:this.handleWrapperKeyup,onKeydown:this.handleWrapperKeydown},li("div",{class:`${n}-input-wrapper`},wg(s.prefix,(e=>e&&li("div",{class:`${n}-input__prefix`},e))),"textarea"===i?li(lR,{ref:"textareaScrollbarInstRef",class:`${n}-input__textarea`,container:this.getTextareaScrollContainer,triggerDisplayManually:!0,useUnifiedContainer:!0,internalHoistYRail:!0},{default:()=>{var e,t;const{textAreaScrollContainerWidth:o}=this,r={width:this.autosize&&o&&`${o}px`};return li(yr,null,li("textarea",Object.assign({},this.inputProps,{ref:"textareaElRef",class:[`${n}-input__textarea-el`,null===(e=this.inputProps)||void 0===e?void 0:e.class],autofocus:this.autofocus,rows:Number(this.rows),placeholder:this.placeholder,value:this.mergedValue,disabled:this.mergedDisabled,maxlength:a?void 0:this.maxlength,minlength:a?void 0:this.minlength,readonly:this.readonly,tabindex:this.passivelyActivated&&!this.activated?-1:void 0,style:[this.textDecorationStyle[0],null===(t=this.inputProps)||void 0===t?void 0:t.style,r],onBlur:this.handleInputBlur,onFocus:e=>{this.handleInputFocus(e,2)},onInput:this.handleInput,onChange:this.handleChange,onScroll:this.handleTextAreaScroll})),this.showPlaceholder1?li("div",{class:`${n}-input__placeholder`,style:[this.placeholderStyle,r],key:"placeholder"},this.mergedPlaceholder[0]):null,this.autosize?li(kx,{onResize:this.handleTextAreaMirrorResize},{default:()=>li("div",{ref:"textareaMirrorElRef",class:`${n}-input__textarea-mirror`,key:"mirror"})}):null)}}):li("div",{class:`${n}-input__input`},li("input",Object.assign({type:"password"===i&&this.mergedShowPasswordOn&&this.passwordVisible?"text":i},this.inputProps,{ref:"inputElRef",class:[`${n}-input__input-el`,null===(e=this.inputProps)||void 0===e?void 0:e.class],style:[this.textDecorationStyle[0],null===(t=this.inputProps)||void 0===t?void 0:t.style],tabindex:this.passivelyActivated&&!this.activated?-1:void 0,placeholder:this.mergedPlaceholder[0],disabled:this.mergedDisabled,maxlength:a?void 0:this.maxlength,minlength:a?void 0:this.minlength,value:Array.isArray(this.mergedValue)?this.mergedValue[0]:this.mergedValue,readonly:this.readonly,autofocus:this.autofocus,size:this.attrSize,onBlur:this.handleInputBlur,onFocus:e=>{this.handleInputFocus(e,0)},onInput:e=>{this.handleInput(e,0)},onChange:e=>{this.handleChange(e,0)}})),this.showPlaceholder1?li("div",{class:`${n}-input__placeholder`},li("span",null,this.mergedPlaceholder[0])):null,this.autosize?li("div",{class:`${n}-input__input-mirror`,key:"mirror",ref:"inputMirrorElRef"}," "):null),!this.pair&&wg(s.suffix,(e=>e||this.clearable||this.showCount||this.mergedShowPasswordOn||void 0!==this.loading?li("div",{class:`${n}-input__suffix`},[wg(s["clear-icon-placeholder"],(e=>(this.clearable||e)&&li(GR,{clsPrefix:n,show:this.showClearButton,onClear:this.handleClear},{placeholder:()=>e,icon:()=>{var e,t;return null===(t=(e=this.$slots)["clear-icon"])||void 0===t?void 0:t.call(e)}}))),this.internalLoadingBeforeSuffix?null:e,void 0!==this.loading?li(XR,{clsPrefix:n,loading:this.loading,showArrow:!1,showClear:!1,style:this.cssVars}):null,this.internalLoadingBeforeSuffix?e:null,this.showCount&&"textarea"!==this.type?li(_E,null,{default:e=>{var t;return null===(t=s.count)||void 0===t?void 0:t.call(s,e)}}):null,this.mergedShowPasswordOn&&"password"===this.type?li("div",{class:`${n}-input__eye`,onMousedown:this.handlePasswordToggleMousedown,onClick:this.handlePasswordToggleClick},this.passwordVisible?xg(s["password-visible-icon"],(()=>[li(CT,{clsPrefix:n},{default:()=>li(nT,null)})])):xg(s["password-invisible-icon"],(()=>[li(CT,{clsPrefix:n},{default:()=>li(oT,null)})]))):null]):null))),this.pair?li("span",{class:`${n}-input__separator`},xg(s.separator,(()=>[this.separator]))):null,this.pair?li("div",{class:`${n}-input-wrapper`},li("div",{class:`${n}-input__input`},li("input",{ref:"inputEl2Ref",type:this.type,class:`${n}-input__input-el`,tabindex:this.passivelyActivated&&!this.activated?-1:void 0,placeholder:this.mergedPlaceholder[1],disabled:this.mergedDisabled,maxlength:a?void 0:this.maxlength,minlength:a?void 0:this.minlength,value:Array.isArray(this.mergedValue)?this.mergedValue[1]:void 0,readonly:this.readonly,style:this.textDecorationStyle[1],onBlur:this.handleInputBlur,onFocus:e=>{this.handleInputFocus(e,1)},onInput:e=>{this.handleInput(e,1)},onChange:e=>{this.handleChange(e,1)}}),this.showPlaceholder2?li("div",{class:`${n}-input__placeholder`},li("span",null,this.mergedPlaceholder[1])):null),wg(s.suffix,(e=>(this.clearable||e)&&li("div",{class:`${n}-input__suffix`},[this.clearable&&li(GR,{clsPrefix:n,show:this.showClearButton,onClear:this.handleClear},{icon:()=>{var e;return null===(e=s["clear-icon"])||void 0===e?void 0:e.call(s)},placeholder:()=>{var e;return null===(e=s["clear-icon-placeholder"])||void 0===e?void 0:e.call(s)}}),e])))):null,this.mergedBordered?li("div",{class:`${n}-input__border`}):null,this.mergedBordered?li("div",{class:`${n}-input__state-border`}):null,this.showCount&&"textarea"===i?li(_E,null,{default:e=>{var t;const{renderCount:n}=this;return n?n(e):null===(t=s.count)||void 0===t?void 0:t.call(s,e)}}):null)}}),zE=nb("input-group","\n display: inline-flex;\n width: 100%;\n flex-wrap: nowrap;\n vertical-align: bottom;\n",[eb(">",[nb("input",[eb("&:not(:last-child)","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n "),eb("&:not(:first-child)","\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n margin-left: -1px!important;\n ")]),nb("button",[eb("&:not(:last-child)","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n ",[ob("state-border, border","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n ")]),eb("&:not(:first-child)","\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n ",[ob("state-border, border","\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n ")])]),eb("*",[eb("&:not(:last-child)","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n ",[eb(">",[nb("input","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n "),nb("base-selection",[nb("base-selection-label","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n "),nb("base-selection-tags","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n "),ob("box-shadow, border, state-border","\n border-top-right-radius: 0!important;\n border-bottom-right-radius: 0!important;\n ")])])]),eb("&:not(:first-child)","\n margin-left: -1px!important;\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n ",[eb(">",[nb("input","\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n "),nb("base-selection",[nb("base-selection-label","\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n "),nb("base-selection-tags","\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n "),ob("box-shadow, border, state-border","\n border-top-left-radius: 0!important;\n border-bottom-left-radius: 0!important;\n ")])])])])])]),RE=zn({name:"InputGroup",props:{},setup(e){const{mergedClsPrefixRef:t}=B_(e);return qP("-input-group",zE,t),{mergedClsPrefix:t}},render(){const{mergedClsPrefix:e}=this;return li("div",{class:`${e}-input-group`},this.$slots)}}),EE={name:"AutoComplete",common:tz,peers:{InternalSelectMenu:pR,Input:xE},self:function(e){const{boxShadow2:t}=e;return{menuBoxShadow:t}}},OE={name:"Avatar",common:tz,self:function(e){const{borderRadius:t,avatarColor:n,cardColor:o,fontSize:r,heightTiny:i,heightSmall:a,heightMedium:l,heightLarge:s,heightHuge:c,modalColor:u,popoverColor:d}=e;return{borderRadius:t,fontSize:r,border:`2px solid ${o}`,heightTiny:i,heightSmall:a,heightMedium:l,heightLarge:s,heightHuge:c,color:eg(o,n),colorModal:eg(u,n),colorPopover:eg(d,n)}}},ME={name:"AvatarGroup",common:tz,peers:{Avatar:OE},self:function(){return{gap:"-12px"}}},FE={width:"44px",height:"44px",borderRadius:"22px",iconSize:"26px"},IE={name:"BackTop",common:tz,self(e){const{popoverColor:t,textColor2:n,primaryColorHover:o,primaryColorPressed:r}=e;return Object.assign(Object.assign({},FE),{color:t,textColor:n,iconColor:n,iconColorHover:o,iconColorPressed:r,boxShadow:"0 2px 8px 0px rgba(0, 0, 0, .12)",boxShadowHover:"0 2px 12px 0px rgba(0, 0, 0, .18)",boxShadowPressed:"0 2px 12px 0px rgba(0, 0, 0, .18)"})}},LE={name:"BackTop",common:qz,self:function(e){const{popoverColor:t,textColor2:n,primaryColorHover:o,primaryColorPressed:r}=e;return Object.assign(Object.assign({},FE),{color:t,textColor:n,iconColor:n,iconColorHover:o,iconColorPressed:r,boxShadow:"0 2px 8px 0px rgba(0, 0, 0, .12)",boxShadowHover:"0 2px 12px 0px rgba(0, 0, 0, .18)",boxShadowPressed:"0 2px 12px 0px rgba(0, 0, 0, .18)"})}},BE=li("svg",{viewBox:"0 0 24 24",version:"1.1",xmlns:"http://www.w3.org/2000/svg",xlinkHref:"http://www.w3.org/1999/xlink"},li("g",{stroke:"none","stroke-width":"1","fill-rule":"evenodd"},li("g",{transform:"translate(-139.000000, -4423.000000)","fill-rule":"nonzero"},li("g",{transform:"translate(120.000000, 4285.000000)"},li("g",{transform:"translate(7.000000, 126.000000)"},li("g",{transform:"translate(24.000000, 24.000000) scale(1, -1) translate(-24.000000, -24.000000) translate(12.000000, 12.000000)"},li("g",{transform:"translate(4.000000, 2.000000)"},li("path",{d:"M8,0 C8.51283584,0 8.93550716,0.38604019 8.99327227,0.883378875 L9,1 L9,10.584 L12.2928932,7.29289322 C12.6834175,6.90236893 13.3165825,6.90236893 13.7071068,7.29289322 C14.0675907,7.65337718 14.0953203,8.22060824 13.7902954,8.61289944 L13.7071068,8.70710678 L8.70710678,13.7071068 L8.62544899,13.7803112 L8.618,13.784 L8.59530661,13.8036654 L8.4840621,13.8753288 L8.37133602,13.9287745 L8.22929083,13.9735893 L8.14346259,13.9897165 L8.03324678,13.9994506 L7.9137692,13.9962979 L7.77070917,13.9735893 L7.6583843,13.9401293 L7.57677845,13.9063266 L7.47929125,13.8540045 L7.4048407,13.8036865 L7.38131006,13.7856883 C7.35030318,13.7612383 7.32077858,13.7349921 7.29289322,13.7071068 L2.29289322,8.70710678 L2.20970461,8.61289944 C1.90467972,8.22060824 1.93240926,7.65337718 2.29289322,7.29289322 C2.65337718,6.93240926 3.22060824,6.90467972 3.61289944,7.20970461 L3.70710678,7.29289322 L7,10.585 L7,1 L7.00672773,0.883378875 C7.06449284,0.38604019 7.48716416,0 8,0 Z"}),li("path",{d:"M14.9333333,15.9994506 C15.5224371,15.9994506 16,16.4471659 16,16.9994506 C16,17.5122865 15.5882238,17.9349578 15.0577292,17.9927229 L14.9333333,17.9994506 L1.06666667,17.9994506 C0.477562934,17.9994506 0,17.5517354 0,16.9994506 C0,16.4866148 0.411776203,16.0639435 0.9422708,16.0061783 L1.06666667,15.9994506 L14.9333333,15.9994506 Z"})))))))),DE=nb("back-top","\n position: fixed;\n right: 40px;\n bottom: 40px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--n-text-color);\n transition:\n color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n border-radius: var(--n-border-radius);\n height: var(--n-height);\n min-width: var(--n-width);\n box-shadow: var(--n-box-shadow);\n background-color: var(--n-color);\n",[gR(),rb("transition-disabled",{transition:"none !important"}),nb("base-icon","\n font-size: var(--n-icon-size);\n color: var(--n-icon-color);\n transition: color .3s var(--n-bezier);\n "),eb("svg",{pointerEvents:"none"}),eb("&:hover",{boxShadow:"var(--n-box-shadow-hover)"},[nb("base-icon",{color:"var(--n-icon-color-hover)"})]),eb("&:active",{boxShadow:"var(--n-box-shadow-pressed)"},[nb("base-icon",{color:"var(--n-icon-color-pressed)"})])]),$E=zn({name:"BackTop",inheritAttrs:!1,props:Object.assign(Object.assign({},I_.props),{show:{type:Boolean,default:void 0},right:{type:[Number,String],default:40},bottom:{type:[Number,String],default:40},to:{type:[String,Object],default:"body"},visibilityHeight:{type:Number,default:180},listenTo:[String,Object,Function],"onUpdate:show":{type:Function,default:()=>{}},target:Function,onShow:Function,onHide:Function}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=Et(null),r=Et(!1);ir((()=>{const{value:t}=o;r.value=null!==t&&t>=e.visibilityHeight}));const i=Et(!1);lr(r,(t=>{var n;i.value&&(null===(n=e["onUpdate:show"])||void 0===n||n.call(e,t))}));const a=Db(jt(e,"show"),r),l=Et(!0),s=Et(null),c=ai((()=>({right:`calc(${Ag(e.right)} + ${Ux.value})`,bottom:Ag(e.bottom)})));let u,d;lr(a,(t=>{var n,o;i.value&&(t&&(null===(n=e.onShow)||void 0===n||n.call(e)),null===(o=e.onHide)||void 0===o||o.call(e))}));const p=I_("BackTop","-back-top",DE,LE,e,t);function h(){var t;if(d)return;d=!0;const n=(null===(t=e.target)||void 0===t?void 0:t.call(e))||("string"==typeof(o=e.listenTo)?document.querySelector(o):"function"==typeof o?o():o)||Om(s.value);var o;if(!n)return;u=n===document.documentElement?document:n;const{to:r}=e;"string"==typeof r&&document.querySelector(r),u.addEventListener("scroll",f),f()}function f(){o.value=(Zx(u)?document.documentElement:u).scrollTop,i.value||tn((()=>{i.value=!0}))}$n((()=>{h(),l.value=a.value})),Hn((()=>{u&&u.removeEventListener("scroll",f)}));const v=ai((()=>{const{self:{color:e,boxShadow:t,boxShadowHover:n,boxShadowPressed:o,iconColor:r,iconColorHover:i,iconColorPressed:a,width:l,height:s,iconSize:c,borderRadius:u,textColor:d},common:{cubicBezierEaseInOut:h}}=p.value;return{"--n-bezier":h,"--n-border-radius":u,"--n-height":s,"--n-width":l,"--n-box-shadow":t,"--n-box-shadow-hover":n,"--n-box-shadow-pressed":o,"--n-color":e,"--n-icon-size":c,"--n-icon-color":r,"--n-icon-color-hover":i,"--n-icon-color-pressed":a,"--n-text-color":d}})),m=n?KP("back-top",void 0,v,e):void 0;return{placeholderRef:s,style:c,mergedShow:a,isMounted:$b(),scrollElement:Et(null),scrollTop:o,DomInfoReady:i,transitionDisabled:l,mergedClsPrefix:t,handleAfterEnter:function(){l.value=!1},handleScroll:f,handleClick:function(){(Zx(u)?document.documentElement:u).scrollTo({top:0,behavior:"smooth"})},cssVars:n?void 0:v,themeClass:null==m?void 0:m.themeClass,onRender:null==m?void 0:m.onRender}},render(){const{mergedClsPrefix:e}=this;return li("div",{ref:"placeholderRef",class:`${e}-back-top-placeholder`,style:"display: none","aria-hidden":!0},li(Sy,{to:this.to,show:this.mergedShow},{default:()=>li(vi,{name:"fade-in-scale-up-transition",appear:this.isMounted,onAfterEnter:this.handleAfterEnter},{default:()=>{var t;return null===(t=this.onRender)||void 0===t||t.call(this),this.mergedShow?li("div",Wr(this.$attrs,{class:[`${e}-back-top`,this.themeClass,this.transitionDisabled&&`${e}-back-top--transition-disabled`],style:[this.style,this.cssVars],onClick:this.handleClick}),xg(this.$slots.default,(()=>[li(CT,{clsPrefix:e},{default:()=>BE})]))):null}})}))}}),NE={name:"Badge",common:tz,self(e){const{errorColorSuppl:t,infoColorSuppl:n,successColorSuppl:o,warningColorSuppl:r,fontFamily:i}=e;return{color:t,colorInfo:n,colorSuccess:o,colorError:t,colorWarning:r,fontSize:"12px",fontFamily:i}}},jE={fontWeightActive:"400"};function HE(e){const{fontSize:t,textColor3:n,textColor2:o,borderRadius:r,buttonColor2Hover:i,buttonColor2Pressed:a}=e;return Object.assign(Object.assign({},jE),{fontSize:t,itemLineHeight:"1.25",itemTextColor:n,itemTextColorHover:o,itemTextColorPressed:o,itemTextColorActive:o,itemBorderRadius:r,itemColorHover:i,itemColorPressed:a,separatorColor:n})}const WE={name:"Breadcrumb",common:qz,self:HE},UE={name:"Breadcrumb",common:tz,self:HE},VE=nb("breadcrumb","\n white-space: nowrap;\n cursor: default;\n line-height: var(--n-item-line-height);\n",[eb("ul","\n list-style: none;\n padding: 0;\n margin: 0;\n "),eb("a","\n color: inherit;\n text-decoration: inherit;\n "),nb("breadcrumb-item","\n font-size: var(--n-font-size);\n transition: color .3s var(--n-bezier);\n display: inline-flex;\n align-items: center;\n ",[nb("icon","\n font-size: 18px;\n vertical-align: -.2em;\n transition: color .3s var(--n-bezier);\n color: var(--n-item-text-color);\n "),eb("&:not(:last-child)",[rb("clickable",[ob("link","\n cursor: pointer;\n ",[eb("&:hover","\n background-color: var(--n-item-color-hover);\n "),eb("&:active","\n background-color: var(--n-item-color-pressed); \n ")])])]),ob("link","\n padding: 4px;\n border-radius: var(--n-item-border-radius);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n color: var(--n-item-text-color);\n position: relative;\n ",[eb("&:hover","\n color: var(--n-item-text-color-hover);\n ",[nb("icon","\n color: var(--n-item-text-color-hover);\n ")]),eb("&:active","\n color: var(--n-item-text-color-pressed);\n ",[nb("icon","\n color: var(--n-item-text-color-pressed);\n ")])]),ob("separator","\n margin: 0 8px;\n color: var(--n-separator-color);\n transition: color .3s var(--n-bezier);\n user-select: none;\n -webkit-user-select: none;\n "),eb("&:last-child",[ob("link","\n font-weight: var(--n-font-weight-active);\n cursor: unset;\n color: var(--n-item-text-color-active);\n ",[nb("icon","\n color: var(--n-item-text-color-active);\n ")]),ob("separator","\n display: none;\n ")])])]),qE="n-breadcrumb",KE=zn({name:"Breadcrumb",props:Object.assign(Object.assign({},I_.props),{separator:{type:String,default:"/"}}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Breadcrumb","-breadcrumb",VE,WE,e,t);_o(qE,{separatorRef:jt(e,"separator"),mergedClsPrefixRef:t});const r=ai((()=>{const{common:{cubicBezierEaseInOut:e},self:{separatorColor:t,itemTextColor:n,itemTextColorHover:r,itemTextColorPressed:i,itemTextColorActive:a,fontSize:l,fontWeightActive:s,itemBorderRadius:c,itemColorHover:u,itemColorPressed:d,itemLineHeight:p}}=o.value;return{"--n-font-size":l,"--n-bezier":e,"--n-item-text-color":n,"--n-item-text-color-hover":r,"--n-item-text-color-pressed":i,"--n-item-text-color-active":a,"--n-separator-color":t,"--n-item-color-hover":u,"--n-item-color-pressed":d,"--n-item-border-radius":c,"--n-font-weight-active":s,"--n-item-line-height":p}})),i=n?KP("breadcrumb",void 0,r,e):void 0;return{mergedClsPrefix:t,cssVars:n?void 0:r,themeClass:null==i?void 0:i.themeClass,onRender:null==i?void 0:i.onRender}},render(){var e;return null===(e=this.onRender)||void 0===e||e.call(this),li("nav",{class:[`${this.mergedClsPrefix}-breadcrumb`,this.themeClass],style:this.cssVars,"aria-label":"Breadcrumb"},li("ul",null,this.$slots))}}),GE=zn({name:"BreadcrumbItem",props:{separator:String,href:String,clickable:{type:Boolean,default:!0},onClick:Function},setup(e,{slots:t}){const n=Po(qE,null);if(!n)return()=>null;const{separatorRef:o,mergedClsPrefixRef:r}=n,i=function(e=(pb?window:null)){const t=()=>{const{hash:t,host:n,hostname:o,href:r,origin:i,pathname:a,port:l,protocol:s,search:c}=(null==e?void 0:e.location)||{};return{hash:t,host:n,hostname:o,href:r,origin:i,pathname:a,port:l,protocol:s,search:c}},n=Et(t()),o=()=>{n.value=t()};return $n((()=>{e&&(e.addEventListener("popstate",o),e.addEventListener("hashchange",o))})),Wn((()=>{e&&(e.removeEventListener("popstate",o),e.removeEventListener("hashchange",o))})),n}(),a=ai((()=>e.href?"a":"span")),l=ai((()=>i.value.href===e.href?"location":null));return()=>{const{value:n}=r;return li("li",{class:[`${n}-breadcrumb-item`,e.clickable&&`${n}-breadcrumb-item--clickable`]},li(a.value,{class:`${n}-breadcrumb-item__link`,"aria-current":l.value,href:e.href,onClick:e.onClick},t),li("span",{class:`${n}-breadcrumb-item__separator`,"aria-hidden":"true"},xg(t.separator,(()=>{var t;return[null!==(t=e.separator)&&void 0!==t?t:o.value]}))))}}});function XE(e){return eg(e,[255,255,255,.16])}function YE(e){return eg(e,[0,0,0,.12])}const QE={paddingTiny:"0 6px",paddingSmall:"0 10px",paddingMedium:"0 14px",paddingLarge:"0 18px",paddingRoundTiny:"0 10px",paddingRoundSmall:"0 14px",paddingRoundMedium:"0 18px",paddingRoundLarge:"0 22px",iconMarginTiny:"6px",iconMarginSmall:"6px",iconMarginMedium:"6px",iconMarginLarge:"6px",iconSizeTiny:"14px",iconSizeSmall:"18px",iconSizeMedium:"18px",iconSizeLarge:"20px",rippleDuration:".6s"};function ZE(e){const{heightTiny:t,heightSmall:n,heightMedium:o,heightLarge:r,borderRadius:i,fontSizeTiny:a,fontSizeSmall:l,fontSizeMedium:s,fontSizeLarge:c,opacityDisabled:u,textColor2:d,textColor3:p,primaryColorHover:h,primaryColorPressed:f,borderColor:v,primaryColor:m,baseColor:g,infoColor:b,infoColorHover:y,infoColorPressed:x,successColor:C,successColorHover:w,successColorPressed:k,warningColor:S,warningColorHover:_,warningColorPressed:P,errorColor:T,errorColorHover:A,errorColorPressed:z,fontWeight:R,buttonColor2:E,buttonColor2Hover:O,buttonColor2Pressed:M,fontWeightStrong:F}=e;return Object.assign(Object.assign({},QE),{heightTiny:t,heightSmall:n,heightMedium:o,heightLarge:r,borderRadiusTiny:i,borderRadiusSmall:i,borderRadiusMedium:i,borderRadiusLarge:i,fontSizeTiny:a,fontSizeSmall:l,fontSizeMedium:s,fontSizeLarge:c,opacityDisabled:u,colorOpacitySecondary:"0.16",colorOpacitySecondaryHover:"0.22",colorOpacitySecondaryPressed:"0.28",colorSecondary:E,colorSecondaryHover:O,colorSecondaryPressed:M,colorTertiary:E,colorTertiaryHover:O,colorTertiaryPressed:M,colorQuaternary:"#0000",colorQuaternaryHover:O,colorQuaternaryPressed:M,color:"#0000",colorHover:"#0000",colorPressed:"#0000",colorFocus:"#0000",colorDisabled:"#0000",textColor:d,textColorTertiary:p,textColorHover:h,textColorPressed:f,textColorFocus:h,textColorDisabled:d,textColorText:d,textColorTextHover:h,textColorTextPressed:f,textColorTextFocus:h,textColorTextDisabled:d,textColorGhost:d,textColorGhostHover:h,textColorGhostPressed:f,textColorGhostFocus:h,textColorGhostDisabled:d,border:`1px solid ${v}`,borderHover:`1px solid ${h}`,borderPressed:`1px solid ${f}`,borderFocus:`1px solid ${h}`,borderDisabled:`1px solid ${v}`,rippleColor:m,colorPrimary:m,colorHoverPrimary:h,colorPressedPrimary:f,colorFocusPrimary:h,colorDisabledPrimary:m,textColorPrimary:g,textColorHoverPrimary:g,textColorPressedPrimary:g,textColorFocusPrimary:g,textColorDisabledPrimary:g,textColorTextPrimary:m,textColorTextHoverPrimary:h,textColorTextPressedPrimary:f,textColorTextFocusPrimary:h,textColorTextDisabledPrimary:d,textColorGhostPrimary:m,textColorGhostHoverPrimary:h,textColorGhostPressedPrimary:f,textColorGhostFocusPrimary:h,textColorGhostDisabledPrimary:m,borderPrimary:`1px solid ${m}`,borderHoverPrimary:`1px solid ${h}`,borderPressedPrimary:`1px solid ${f}`,borderFocusPrimary:`1px solid ${h}`,borderDisabledPrimary:`1px solid ${m}`,rippleColorPrimary:m,colorInfo:b,colorHoverInfo:y,colorPressedInfo:x,colorFocusInfo:y,colorDisabledInfo:b,textColorInfo:g,textColorHoverInfo:g,textColorPressedInfo:g,textColorFocusInfo:g,textColorDisabledInfo:g,textColorTextInfo:b,textColorTextHoverInfo:y,textColorTextPressedInfo:x,textColorTextFocusInfo:y,textColorTextDisabledInfo:d,textColorGhostInfo:b,textColorGhostHoverInfo:y,textColorGhostPressedInfo:x,textColorGhostFocusInfo:y,textColorGhostDisabledInfo:b,borderInfo:`1px solid ${b}`,borderHoverInfo:`1px solid ${y}`,borderPressedInfo:`1px solid ${x}`,borderFocusInfo:`1px solid ${y}`,borderDisabledInfo:`1px solid ${b}`,rippleColorInfo:b,colorSuccess:C,colorHoverSuccess:w,colorPressedSuccess:k,colorFocusSuccess:w,colorDisabledSuccess:C,textColorSuccess:g,textColorHoverSuccess:g,textColorPressedSuccess:g,textColorFocusSuccess:g,textColorDisabledSuccess:g,textColorTextSuccess:C,textColorTextHoverSuccess:w,textColorTextPressedSuccess:k,textColorTextFocusSuccess:w,textColorTextDisabledSuccess:d,textColorGhostSuccess:C,textColorGhostHoverSuccess:w,textColorGhostPressedSuccess:k,textColorGhostFocusSuccess:w,textColorGhostDisabledSuccess:C,borderSuccess:`1px solid ${C}`,borderHoverSuccess:`1px solid ${w}`,borderPressedSuccess:`1px solid ${k}`,borderFocusSuccess:`1px solid ${w}`,borderDisabledSuccess:`1px solid ${C}`,rippleColorSuccess:C,colorWarning:S,colorHoverWarning:_,colorPressedWarning:P,colorFocusWarning:_,colorDisabledWarning:S,textColorWarning:g,textColorHoverWarning:g,textColorPressedWarning:g,textColorFocusWarning:g,textColorDisabledWarning:g,textColorTextWarning:S,textColorTextHoverWarning:_,textColorTextPressedWarning:P,textColorTextFocusWarning:_,textColorTextDisabledWarning:d,textColorGhostWarning:S,textColorGhostHoverWarning:_,textColorGhostPressedWarning:P,textColorGhostFocusWarning:_,textColorGhostDisabledWarning:S,borderWarning:`1px solid ${S}`,borderHoverWarning:`1px solid ${_}`,borderPressedWarning:`1px solid ${P}`,borderFocusWarning:`1px solid ${_}`,borderDisabledWarning:`1px solid ${S}`,rippleColorWarning:S,colorError:T,colorHoverError:A,colorPressedError:z,colorFocusError:A,colorDisabledError:T,textColorError:g,textColorHoverError:g,textColorPressedError:g,textColorFocusError:g,textColorDisabledError:g,textColorTextError:T,textColorTextHoverError:A,textColorTextPressedError:z,textColorTextFocusError:A,textColorTextDisabledError:d,textColorGhostError:T,textColorGhostHoverError:A,textColorGhostPressedError:z,textColorGhostFocusError:A,textColorGhostDisabledError:T,borderError:`1px solid ${T}`,borderHoverError:`1px solid ${A}`,borderPressedError:`1px solid ${z}`,borderFocusError:`1px solid ${A}`,borderDisabledError:`1px solid ${T}`,rippleColorError:T,waveOpacity:"0.6",fontWeight:R,fontWeightStrong:F})}const JE={name:"Button",common:qz,self:ZE},eO={name:"Button",common:tz,self(e){const t=ZE(e);return t.waveOpacity="0.8",t.colorOpacitySecondary="0.16",t.colorOpacitySecondaryHover="0.2",t.colorOpacitySecondaryPressed="0.12",t}},tO=eb([nb("button","\n margin: 0;\n font-weight: var(--n-font-weight);\n line-height: 1;\n font-family: inherit;\n padding: var(--n-padding);\n height: var(--n-height);\n font-size: var(--n-font-size);\n border-radius: var(--n-border-radius);\n color: var(--n-text-color);\n background-color: var(--n-color);\n width: var(--n-width);\n white-space: nowrap;\n outline: none;\n position: relative;\n z-index: auto;\n border: none;\n display: inline-flex;\n flex-wrap: nowrap;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n user-select: none;\n -webkit-user-select: none;\n text-align: center;\n cursor: pointer;\n text-decoration: none;\n transition:\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n ",[rb("color",[ob("border",{borderColor:"var(--n-border-color)"}),rb("disabled",[ob("border",{borderColor:"var(--n-border-color-disabled)"})]),ib("disabled",[eb("&:focus",[ob("state-border",{borderColor:"var(--n-border-color-focus)"})]),eb("&:hover",[ob("state-border",{borderColor:"var(--n-border-color-hover)"})]),eb("&:active",[ob("state-border",{borderColor:"var(--n-border-color-pressed)"})]),rb("pressed",[ob("state-border",{borderColor:"var(--n-border-color-pressed)"})])])]),rb("disabled",{backgroundColor:"var(--n-color-disabled)",color:"var(--n-text-color-disabled)"},[ob("border",{border:"var(--n-border-disabled)"})]),ib("disabled",[eb("&:focus",{backgroundColor:"var(--n-color-focus)",color:"var(--n-text-color-focus)"},[ob("state-border",{border:"var(--n-border-focus)"})]),eb("&:hover",{backgroundColor:"var(--n-color-hover)",color:"var(--n-text-color-hover)"},[ob("state-border",{border:"var(--n-border-hover)"})]),eb("&:active",{backgroundColor:"var(--n-color-pressed)",color:"var(--n-text-color-pressed)"},[ob("state-border",{border:"var(--n-border-pressed)"})]),rb("pressed",{backgroundColor:"var(--n-color-pressed)",color:"var(--n-text-color-pressed)"},[ob("state-border",{border:"var(--n-border-pressed)"})])]),rb("loading","cursor: wait;"),nb("base-wave","\n pointer-events: none;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n animation-iteration-count: 1;\n animation-duration: var(--n-ripple-duration);\n animation-timing-function: var(--n-bezier-ease-out), var(--n-bezier-ease-out);\n ",[rb("active",{zIndex:1,animationName:"button-wave-spread, button-wave-opacity"})]),pb&&"MozBoxSizing"in document.createElement("div").style?eb("&::moz-focus-inner",{border:0}):null,ob("border, state-border","\n position: absolute;\n left: 0;\n top: 0;\n right: 0;\n bottom: 0;\n border-radius: inherit;\n transition: border-color .3s var(--n-bezier);\n pointer-events: none;\n "),ob("border",{border:"var(--n-border)"}),ob("state-border",{border:"var(--n-border)",borderColor:"#0000",zIndex:1}),ob("icon","\n margin: var(--n-icon-margin);\n margin-left: 0;\n height: var(--n-icon-size);\n width: var(--n-icon-size);\n max-width: var(--n-icon-size);\n font-size: var(--n-icon-size);\n position: relative;\n flex-shrink: 0;\n ",[nb("icon-slot","\n height: var(--n-icon-size);\n width: var(--n-icon-size);\n position: absolute;\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n ",[PT({top:"50%",originalTransform:"translateY(-50%)"})]),function({duration:e=".2s",delay:t=".1s"}={}){return[eb("&.fade-in-width-expand-transition-leave-from, &.fade-in-width-expand-transition-enter-to",{opacity:1}),eb("&.fade-in-width-expand-transition-leave-to, &.fade-in-width-expand-transition-enter-from","\n opacity: 0!important;\n margin-left: 0!important;\n margin-right: 0!important;\n "),eb("&.fade-in-width-expand-transition-leave-active",`\n overflow: hidden;\n transition:\n opacity ${e} ${tE},\n max-width ${e} ${tE} ${t},\n margin-left ${e} ${tE} ${t},\n margin-right ${e} ${tE} ${t};\n `),eb("&.fade-in-width-expand-transition-enter-active",`\n overflow: hidden;\n transition:\n opacity ${e} ${tE} ${t},\n max-width ${e} ${tE},\n margin-left ${e} ${tE},\n margin-right ${e} ${tE};\n `)]}()]),ob("content","\n display: flex;\n align-items: center;\n flex-wrap: nowrap;\n min-width: 0;\n ",[eb("~",[ob("icon",{margin:"var(--n-icon-margin)",marginRight:0})])]),rb("block","\n display: flex;\n width: 100%;\n "),rb("dashed",[ob("border, state-border",{borderStyle:"dashed !important"})]),rb("disabled",{cursor:"not-allowed",opacity:"var(--n-opacity-disabled)"})]),eb("@keyframes button-wave-spread",{from:{boxShadow:"0 0 0.5px 0 var(--n-ripple-color)"},to:{boxShadow:"0 0 0.5px 4.5px var(--n-ripple-color)"}}),eb("@keyframes button-wave-opacity",{from:{opacity:"var(--n-wave-opacity)"},to:{opacity:0}})]),nO=zn({name:"Button",props:Object.assign(Object.assign({},I_.props),{color:String,textColor:String,text:Boolean,block:Boolean,loading:Boolean,disabled:Boolean,circle:Boolean,size:String,ghost:Boolean,round:Boolean,secondary:Boolean,tertiary:Boolean,quaternary:Boolean,strong:Boolean,focusable:{type:Boolean,default:!0},keyboard:{type:Boolean,default:!0},tag:{type:String,default:"button"},type:{type:String,default:"default"},dashed:Boolean,renderIcon:Function,iconPlacement:{type:String,default:"left"},attrType:{type:String,default:"button"},bordered:{type:Boolean,default:!0},onClick:[Function,Array],nativeFocusBehavior:{type:Boolean,default:!bE}}),setup(e){const t=Et(null),n=Et(null),o=Et(!1),r=mb((()=>!e.quaternary&&!e.tertiary&&!e.secondary&&!e.text&&(!e.color||e.ghost||e.dashed)&&e.bordered)),i=Po("n-button-group",{}),{mergedSizeRef:a}=eC({},{defaultSize:"medium",mergedSize:t=>{const{size:n}=e;if(n)return n;const{size:o}=i;if(o)return o;const{mergedSize:r}=t||{};return r?r.value:"medium"}}),l=ai((()=>e.focusable&&!e.disabled)),{inlineThemeDisabled:s,mergedClsPrefixRef:c,mergedRtlRef:u}=B_(e),d=I_("Button","-button",tO,JE,e,c),p=GP("Button",u,c),h=ai((()=>{const t=d.value,{common:{cubicBezierEaseInOut:n,cubicBezierEaseOut:o},self:r}=t,{rippleDuration:i,opacityDisabled:l,fontWeight:s,fontWeightStrong:c}=r,u=a.value,{dashed:p,type:h,ghost:f,text:v,color:m,round:g,circle:b,textColor:y,secondary:x,tertiary:C,quaternary:w,strong:k}=e,S={"font-weight":k?c:s};let _={"--n-color":"initial","--n-color-hover":"initial","--n-color-pressed":"initial","--n-color-focus":"initial","--n-color-disabled":"initial","--n-ripple-color":"initial","--n-text-color":"initial","--n-text-color-hover":"initial","--n-text-color-pressed":"initial","--n-text-color-focus":"initial","--n-text-color-disabled":"initial"};const P="tertiary"===h,T="default"===h,A=P?"default":h;if(v){const e=y||m;_={"--n-color":"#0000","--n-color-hover":"#0000","--n-color-pressed":"#0000","--n-color-focus":"#0000","--n-color-disabled":"#0000","--n-ripple-color":"#0000","--n-text-color":e||r[ub("textColorText",A)],"--n-text-color-hover":e?XE(e):r[ub("textColorTextHover",A)],"--n-text-color-pressed":e?YE(e):r[ub("textColorTextPressed",A)],"--n-text-color-focus":e?XE(e):r[ub("textColorTextHover",A)],"--n-text-color-disabled":e||r[ub("textColorTextDisabled",A)]}}else if(f||p){const e=y||m;_={"--n-color":"#0000","--n-color-hover":"#0000","--n-color-pressed":"#0000","--n-color-focus":"#0000","--n-color-disabled":"#0000","--n-ripple-color":m||r[ub("rippleColor",A)],"--n-text-color":e||r[ub("textColorGhost",A)],"--n-text-color-hover":e?XE(e):r[ub("textColorGhostHover",A)],"--n-text-color-pressed":e?YE(e):r[ub("textColorGhostPressed",A)],"--n-text-color-focus":e?XE(e):r[ub("textColorGhostHover",A)],"--n-text-color-disabled":e||r[ub("textColorGhostDisabled",A)]}}else if(x){const e=T?r.textColor:P?r.textColorTertiary:r[ub("color",A)],t=m||e,n="default"!==h&&"tertiary"!==h;_={"--n-color":n?tg(t,{alpha:Number(r.colorOpacitySecondary)}):r.colorSecondary,"--n-color-hover":n?tg(t,{alpha:Number(r.colorOpacitySecondaryHover)}):r.colorSecondaryHover,"--n-color-pressed":n?tg(t,{alpha:Number(r.colorOpacitySecondaryPressed)}):r.colorSecondaryPressed,"--n-color-focus":n?tg(t,{alpha:Number(r.colorOpacitySecondaryHover)}):r.colorSecondaryHover,"--n-color-disabled":r.colorSecondary,"--n-ripple-color":"#0000","--n-text-color":t,"--n-text-color-hover":t,"--n-text-color-pressed":t,"--n-text-color-focus":t,"--n-text-color-disabled":t}}else if(C||w){const e=T?r.textColor:P?r.textColorTertiary:r[ub("color",A)],t=m||e;C?(_["--n-color"]=r.colorTertiary,_["--n-color-hover"]=r.colorTertiaryHover,_["--n-color-pressed"]=r.colorTertiaryPressed,_["--n-color-focus"]=r.colorSecondaryHover,_["--n-color-disabled"]=r.colorTertiary):(_["--n-color"]=r.colorQuaternary,_["--n-color-hover"]=r.colorQuaternaryHover,_["--n-color-pressed"]=r.colorQuaternaryPressed,_["--n-color-focus"]=r.colorQuaternaryHover,_["--n-color-disabled"]=r.colorQuaternary),_["--n-ripple-color"]="#0000",_["--n-text-color"]=t,_["--n-text-color-hover"]=t,_["--n-text-color-pressed"]=t,_["--n-text-color-focus"]=t,_["--n-text-color-disabled"]=t}else _={"--n-color":m||r[ub("color",A)],"--n-color-hover":m?XE(m):r[ub("colorHover",A)],"--n-color-pressed":m?YE(m):r[ub("colorPressed",A)],"--n-color-focus":m?XE(m):r[ub("colorFocus",A)],"--n-color-disabled":m||r[ub("colorDisabled",A)],"--n-ripple-color":m||r[ub("rippleColor",A)],"--n-text-color":y||(m?r.textColorPrimary:P?r.textColorTertiary:r[ub("textColor",A)]),"--n-text-color-hover":y||(m?r.textColorHoverPrimary:r[ub("textColorHover",A)]),"--n-text-color-pressed":y||(m?r.textColorPressedPrimary:r[ub("textColorPressed",A)]),"--n-text-color-focus":y||(m?r.textColorFocusPrimary:r[ub("textColorFocus",A)]),"--n-text-color-disabled":y||(m?r.textColorDisabledPrimary:r[ub("textColorDisabled",A)])};let z={"--n-border":"initial","--n-border-hover":"initial","--n-border-pressed":"initial","--n-border-focus":"initial","--n-border-disabled":"initial"};z=v?{"--n-border":"none","--n-border-hover":"none","--n-border-pressed":"none","--n-border-focus":"none","--n-border-disabled":"none"}:{"--n-border":r[ub("border",A)],"--n-border-hover":r[ub("borderHover",A)],"--n-border-pressed":r[ub("borderPressed",A)],"--n-border-focus":r[ub("borderFocus",A)],"--n-border-disabled":r[ub("borderDisabled",A)]};const{[ub("height",u)]:R,[ub("fontSize",u)]:E,[ub("padding",u)]:O,[ub("paddingRound",u)]:M,[ub("iconSize",u)]:F,[ub("borderRadius",u)]:I,[ub("iconMargin",u)]:L,waveOpacity:B}=r,D={"--n-width":b&&!v?R:"initial","--n-height":v?"initial":R,"--n-font-size":E,"--n-padding":b||v?"initial":g?M:O,"--n-icon-size":F,"--n-icon-margin":L,"--n-border-radius":v?"initial":b||g?R:I};return Object.assign(Object.assign(Object.assign(Object.assign({"--n-bezier":n,"--n-bezier-ease-out":o,"--n-ripple-duration":i,"--n-opacity-disabled":l,"--n-wave-opacity":B},S),_),z),D)})),f=s?KP("button",ai((()=>{let t="";const{dashed:n,type:o,ghost:r,text:i,color:l,round:s,circle:c,textColor:u,secondary:d,tertiary:p,quaternary:h,strong:f}=e;n&&(t+="a"),r&&(t+="b"),i&&(t+="c"),s&&(t+="d"),c&&(t+="e"),d&&(t+="f"),p&&(t+="g"),h&&(t+="h"),f&&(t+="i"),l&&(t+=`j${zg(l)}`),u&&(t+=`k${zg(u)}`);const{value:v}=a;return t+=`l${v[0]}`,t+=`m${o[0]}`,t})),h,e):void 0;return{selfElRef:t,waveElRef:n,mergedClsPrefix:c,mergedFocusable:l,mergedSize:a,showBorder:r,enterPressed:o,rtlEnabled:p,handleMousedown:n=>{var o;l.value||n.preventDefault(),e.nativeFocusBehavior||(n.preventDefault(),e.disabled||l.value&&(null===(o=t.value)||void 0===o||o.focus({preventScroll:!0})))},handleKeydown:t=>{if("Enter"===t.key){if(!e.keyboard||e.loading)return void t.preventDefault();o.value=!0}},handleBlur:()=>{o.value=!1},handleKeyup:t=>{if("Enter"===t.key){if(!e.keyboard)return;o.value=!1}},handleClick:t=>{var o;if(!e.disabled&&!e.loading){const{onClick:r}=e;r&&dg(r,t),e.text||null===(o=n.value)||void 0===o||o.play()}},customColorCssVars:ai((()=>{const{color:t}=e;if(!t)return null;const n=XE(t);return{"--n-border-color":t,"--n-border-color-hover":n,"--n-border-color-pressed":YE(t),"--n-border-color-focus":n,"--n-border-color-disabled":t}})),cssVars:s?void 0:h,themeClass:null==f?void 0:f.themeClass,onRender:null==f?void 0:f.onRender}},render(){const{mergedClsPrefix:e,tag:t,onRender:n}=this;null==n||n();const o=wg(this.$slots.default,(t=>t&&li("span",{class:`${e}-button__content`},t)));return li(t,{ref:"selfElRef",class:[this.themeClass,`${e}-button`,`${e}-button--${this.type}-type`,`${e}-button--${this.mergedSize}-type`,this.rtlEnabled&&`${e}-button--rtl`,this.disabled&&`${e}-button--disabled`,this.block&&`${e}-button--block`,this.enterPressed&&`${e}-button--pressed`,!this.text&&this.dashed&&`${e}-button--dashed`,this.color&&`${e}-button--color`,this.secondary&&`${e}-button--secondary`,this.loading&&`${e}-button--loading`,this.ghost&&`${e}-button--ghost`],tabindex:this.mergedFocusable?0:-1,type:this.attrType,style:this.cssVars,disabled:this.disabled,onClick:this.handleClick,onBlur:this.handleBlur,onMousedown:this.handleMousedown,onKeyup:this.handleKeyup,onKeydown:this.handleKeydown},"right"===this.iconPlacement&&o,li(yT,{width:!0},{default:()=>wg(this.$slots.icon,(t=>(this.loading||this.renderIcon||t)&&li("span",{class:`${e}-button__icon`,style:{margin:kg(this.$slots.default)?"0":""}},li(bT,null,{default:()=>this.loading?li(RT,{clsPrefix:e,key:"loading",class:`${e}-icon-slot`,strokeWidth:20}):li("div",{key:"icon",class:`${e}-icon-slot`,role:"none"},this.renderIcon?this.renderIcon():t)}))))}),"left"===this.iconPlacement&&o,this.text?null:li(CR,{ref:"waveElRef",clsPrefix:e}),this.showBorder?li("div",{"aria-hidden":!0,class:`${e}-button__border`,style:this.customColorCssVars}):null,this.showBorder?li("div",{"aria-hidden":!0,class:`${e}-button__state-border`,style:this.customColorCssVars}):null)}}),oO=nO,rO=nO,iO={titleFontSize:"22px"},aO={name:"Calendar",common:tz,peers:{Button:eO},self:function(e){const{borderRadius:t,fontSize:n,lineHeight:o,textColor2:r,textColor1:i,textColorDisabled:a,dividerColor:l,fontWeightStrong:s,primaryColor:c,baseColor:u,hoverColor:d,cardColor:p,modalColor:h,popoverColor:f}=e;return Object.assign(Object.assign({},iO),{borderRadius:t,borderColor:eg(p,l),borderColorModal:eg(h,l),borderColorPopover:eg(f,l),textColor:r,titleFontWeight:s,titleTextColor:i,dayTextColor:a,fontSize:n,lineHeight:o,dateColorCurrent:c,dateTextColorCurrent:u,cellColorHover:eg(p,d),cellColorHoverModal:eg(h,d),cellColorHoverPopover:eg(f,d),cellColor:p,cellColorModal:h,cellColorPopover:f,barColor:c})}},lO={name:"ColorPicker",common:tz,peers:{Input:xE,Button:eO},self:function(e){const{fontSize:t,boxShadow2:n,popoverColor:o,textColor2:r,borderRadius:i,borderColor:a,heightSmall:l,heightMedium:s,heightLarge:c,fontSizeSmall:u,fontSizeMedium:d,fontSizeLarge:p,dividerColor:h}=e;return{panelFontSize:t,boxShadow:n,color:o,textColor:r,borderRadius:i,border:`1px solid ${a}`,heightSmall:l,heightMedium:s,heightLarge:c,fontSizeSmall:u,fontSizeMedium:d,fontSizeLarge:p,dividerColor:h}}},sO={paddingSmall:"12px 16px 12px",paddingMedium:"19px 24px 20px",paddingLarge:"23px 32px 24px",paddingHuge:"27px 40px 28px",titleFontSizeSmall:"16px",titleFontSizeMedium:"18px",titleFontSizeLarge:"18px",titleFontSizeHuge:"18px",closeIconSize:"18px",closeSize:"22px"};function cO(e){const{primaryColor:t,borderRadius:n,lineHeight:o,fontSize:r,cardColor:i,textColor2:a,textColor1:l,dividerColor:s,fontWeightStrong:c,closeIconColor:u,closeIconColorHover:d,closeIconColorPressed:p,closeColorHover:h,closeColorPressed:f,modalColor:v,boxShadow1:m,popoverColor:g,actionColor:b}=e;return Object.assign(Object.assign({},sO),{lineHeight:o,color:i,colorModal:v,colorPopover:g,colorTarget:t,colorEmbedded:b,colorEmbeddedModal:b,colorEmbeddedPopover:b,textColor:a,titleTextColor:l,borderColor:s,actionColor:b,titleFontWeight:c,closeColorHover:h,closeColorPressed:f,closeBorderRadius:n,closeIconColor:u,closeIconColorHover:d,closeIconColorPressed:p,fontSizeSmall:r,fontSizeMedium:r,fontSizeLarge:r,fontSizeHuge:r,boxShadow:m,borderRadius:n})}const uO={name:"Card",common:qz,self:cO},dO={name:"Card",common:tz,self(e){const t=cO(e),{cardColor:n,modalColor:o,popoverColor:r}=e;return t.colorEmbedded=n,t.colorEmbeddedModal=o,t.colorEmbeddedPopover=r,t}},pO=eb([nb("card","\n font-size: var(--n-font-size);\n line-height: var(--n-line-height);\n display: flex;\n flex-direction: column;\n width: 100%;\n box-sizing: border-box;\n position: relative;\n border-radius: var(--n-border-radius);\n background-color: var(--n-color);\n color: var(--n-text-color);\n word-break: break-word;\n transition: \n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n ",[sb({background:"var(--n-color-modal)"}),rb("hoverable",[eb("&:hover","box-shadow: var(--n-box-shadow);")]),rb("content-segmented",[eb(">",[ob("content",{paddingTop:"var(--n-padding-bottom)"})])]),rb("content-soft-segmented",[eb(">",[ob("content","\n margin: 0 var(--n-padding-left);\n padding: var(--n-padding-bottom) 0;\n ")])]),rb("footer-segmented",[eb(">",[ob("footer",{paddingTop:"var(--n-padding-bottom)"})])]),rb("footer-soft-segmented",[eb(">",[ob("footer","\n padding: var(--n-padding-bottom) 0;\n margin: 0 var(--n-padding-left);\n ")])]),eb(">",[nb("card-header","\n box-sizing: border-box;\n display: flex;\n align-items: center;\n font-size: var(--n-title-font-size);\n padding:\n var(--n-padding-top)\n var(--n-padding-left)\n var(--n-padding-bottom)\n var(--n-padding-left);\n ",[ob("main","\n font-weight: var(--n-title-font-weight);\n transition: color .3s var(--n-bezier);\n flex: 1;\n min-width: 0;\n color: var(--n-title-text-color);\n "),ob("extra","\n display: flex;\n align-items: center;\n font-size: var(--n-font-size);\n font-weight: 400;\n transition: color .3s var(--n-bezier);\n color: var(--n-text-color);\n "),ob("close","\n margin: 0 0 0 8px;\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n ")]),ob("action","\n box-sizing: border-box;\n transition:\n background-color .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n background-clip: padding-box;\n background-color: var(--n-action-color);\n "),ob("content","flex: 1; min-width: 0;"),ob("content, footer","\n box-sizing: border-box;\n padding: 0 var(--n-padding-left) var(--n-padding-bottom) var(--n-padding-left);\n font-size: var(--n-font-size);\n ",[eb("&:first-child",{paddingTop:"var(--n-padding-bottom)"})]),ob("action","\n background-color: var(--n-action-color);\n padding: var(--n-padding-bottom) var(--n-padding-left);\n border-bottom-left-radius: var(--n-border-radius);\n border-bottom-right-radius: var(--n-border-radius);\n ")]),nb("card-cover","\n overflow: hidden;\n width: 100%;\n border-radius: var(--n-border-radius) var(--n-border-radius) 0 0;\n ",[eb("img","\n display: block;\n width: 100%;\n ")]),rb("bordered","\n border: 1px solid var(--n-border-color);\n ",[eb("&:target","border-color: var(--n-color-target);")]),rb("action-segmented",[eb(">",[ob("action",[eb("&:not(:first-child)",{borderTop:"1px solid var(--n-border-color)"})])])]),rb("content-segmented, content-soft-segmented",[eb(">",[ob("content",{transition:"border-color 0.3s var(--n-bezier)"},[eb("&:not(:first-child)",{borderTop:"1px solid var(--n-border-color)"})])])]),rb("footer-segmented, footer-soft-segmented",[eb(">",[ob("footer",{transition:"border-color 0.3s var(--n-bezier)"},[eb("&:not(:first-child)",{borderTop:"1px solid var(--n-border-color)"})])])]),rb("embedded","\n background-color: var(--n-color-embedded);\n ")]),ab(nb("card","\n background: var(--n-color-modal);\n ",[rb("embedded","\n background-color: var(--n-color-embedded-modal);\n ")])),lb(nb("card","\n background: var(--n-color-popover);\n ",[rb("embedded","\n background-color: var(--n-color-embedded-popover);\n ")]))]),hO={title:[String,Function],contentClass:String,contentStyle:[Object,String],headerClass:String,headerStyle:[Object,String],headerExtraClass:String,headerExtraStyle:[Object,String],footerClass:String,footerStyle:[Object,String],embedded:Boolean,segmented:{type:[Boolean,Object],default:!1},size:{type:String,default:"medium"},bordered:{type:Boolean,default:!0},closable:Boolean,hoverable:Boolean,role:String,onClose:[Function,Array],tag:{type:String,default:"div"},cover:Function,content:[String,Function],footer:Function,action:Function,headerExtra:Function},fO=pg(hO),vO=zn({name:"Card",props:Object.assign(Object.assign({},I_.props),hO),setup(e){const{inlineThemeDisabled:t,mergedClsPrefixRef:n,mergedRtlRef:o}=B_(e),r=I_("Card","-card",pO,uO,e,n),i=GP("Card",o,n),a=ai((()=>{const{size:t}=e,{self:{color:n,colorModal:o,colorTarget:i,textColor:a,titleTextColor:l,titleFontWeight:s,borderColor:c,actionColor:u,borderRadius:d,lineHeight:p,closeIconColor:h,closeIconColorHover:f,closeIconColorPressed:v,closeColorHover:m,closeColorPressed:g,closeBorderRadius:b,closeIconSize:y,closeSize:x,boxShadow:C,colorPopover:w,colorEmbedded:k,colorEmbeddedModal:S,colorEmbeddedPopover:_,[ub("padding",t)]:P,[ub("fontSize",t)]:T,[ub("titleFontSize",t)]:A},common:{cubicBezierEaseInOut:z}}=r.value,{top:R,left:E,bottom:O}=Bm(P);return{"--n-bezier":z,"--n-border-radius":d,"--n-color":n,"--n-color-modal":o,"--n-color-popover":w,"--n-color-embedded":k,"--n-color-embedded-modal":S,"--n-color-embedded-popover":_,"--n-color-target":i,"--n-text-color":a,"--n-line-height":p,"--n-action-color":u,"--n-title-text-color":l,"--n-title-font-weight":s,"--n-close-icon-color":h,"--n-close-icon-color-hover":f,"--n-close-icon-color-pressed":v,"--n-close-color-hover":m,"--n-close-color-pressed":g,"--n-border-color":c,"--n-box-shadow":C,"--n-padding-top":R,"--n-padding-bottom":O,"--n-padding-left":E,"--n-font-size":T,"--n-title-font-size":A,"--n-close-size":x,"--n-close-icon-size":y,"--n-close-border-radius":b}})),l=t?KP("card",ai((()=>e.size[0])),a,e):void 0;return{rtlEnabled:i,mergedClsPrefix:n,mergedTheme:r,handleCloseClick:()=>{const{onClose:t}=e;t&&dg(t)},cssVars:t?void 0:a,themeClass:null==l?void 0:l.themeClass,onRender:null==l?void 0:l.onRender}},render(){const{segmented:e,bordered:t,hoverable:n,mergedClsPrefix:o,rtlEnabled:r,onRender:i,embedded:a,tag:l,$slots:s}=this;return null==i||i(),li(l,{class:[`${o}-card`,this.themeClass,a&&`${o}-card--embedded`,{[`${o}-card--rtl`]:r,[`${o}-card--content${"boolean"!=typeof e&&"soft"===e.content?"-soft":""}-segmented`]:!0===e||!1!==e&&e.content,[`${o}-card--footer${"boolean"!=typeof e&&"soft"===e.footer?"-soft":""}-segmented`]:!0===e||!1!==e&&e.footer,[`${o}-card--action-segmented`]:!0===e||!1!==e&&e.action,[`${o}-card--bordered`]:t,[`${o}-card--hoverable`]:n}],style:this.cssVars,role:this.role},wg(s.cover,(e=>{const t=this.cover?yg([this.cover()]):e;return t&&li("div",{class:`${o}-card-cover`,role:"none"},t)})),wg(s.header,(e=>{const{title:t}=this,n=t?yg("function"==typeof t?[t()]:[t]):e;return n||this.closable?li("div",{class:[`${o}-card-header`,this.headerClass],style:this.headerStyle,role:"heading"},li("div",{class:`${o}-card-header__main`,role:"heading"},n),wg(s["header-extra"],(e=>{const t=this.headerExtra?yg([this.headerExtra()]):e;return t&&li("div",{class:[`${o}-card-header__extra`,this.headerExtraClass],style:this.headerExtraStyle},t)})),this.closable&&li(kT,{clsPrefix:o,class:`${o}-card-header__close`,onClick:this.handleCloseClick,absolute:!0})):null})),wg(s.default,(e=>{const{content:t}=this,n=t?yg("function"==typeof t?[t()]:[t]):e;return n&&li("div",{class:[`${o}-card__content`,this.contentClass],style:this.contentStyle,role:"none"},n)})),wg(s.footer,(e=>{const t=this.footer?yg([this.footer()]):e;return t&&li("div",{class:[`${o}-card__footer`,this.footerClass],style:this.footerStyle,role:"none"},t)})),wg(s.action,(e=>{const t=this.action?yg([this.action()]):e;return t&&li("div",{class:`${o}-card__action`,role:"none"},t)})))}});function mO(){return{dotSize:"8px",dotColor:"rgba(255, 255, 255, .3)",dotColorActive:"rgba(255, 255, 255, 1)",dotColorFocus:"rgba(255, 255, 255, .5)",dotLineWidth:"16px",dotLineWidthActive:"24px",arrowColor:"#eee"}}const gO={name:"Carousel",common:qz,self:mO},bO={name:"Carousel",common:tz,self:mO};function yO(e,t,n){return Br(e,{key:`carousel-item-duplicate-${t}-${n}`})}function xO(e,t,n){return 1===t?0:n?0===e?t-3:e===t-1?0:e-1:e}function CO(e,t){return t?e+1:e}function wO(e){return window.TouchEvent&&e instanceof window.TouchEvent}function kO(e,t){let{offsetWidth:n,offsetHeight:o}=e;if(t){const t=getComputedStyle(e);n=n-Number.parseFloat(t.getPropertyValue("padding-left"))-Number.parseFloat(t.getPropertyValue("padding-right")),o=o-Number.parseFloat(t.getPropertyValue("padding-top"))-Number.parseFloat(t.getPropertyValue("padding-bottom"))}return{width:n,height:o}}function SO(e,t,n){return en?n:e}const _O="n-carousel-methods";function PO(e="unknown",t="component"){const n=Po(_O);return n||fg(e,`\`${t}\` must be placed inside \`n-carousel\`.`),n}const TO=zn({name:"CarouselDots",props:{total:{type:Number,default:0},currentIndex:{type:Number,default:0},dotType:{type:String,default:"dot"},trigger:{type:String,default:"click"},keyboard:Boolean},setup(e){const{mergedClsPrefixRef:t}=B_(e),n=Et([]),o=PO();function r(e){var t;null===(t=n.value[e])||void 0===t||t.focus()}return Nn((()=>n.value.length=0)),{mergedClsPrefix:t,dotEls:n,handleKeydown:function(t,n){switch(t.key){case"Enter":case" ":return t.preventDefault(),void o.to(n)}e.keyboard&&function(e){var t;if(e.shiftKey||e.altKey||e.ctrlKey||e.metaKey)return;const n=null===(t=document.activeElement)||void 0===t?void 0:t.nodeName.toLowerCase();if("input"===n||"textarea"===n)return;const{code:i}=e,a="PageUp"===i||"ArrowUp"===i,l="PageDown"===i||"ArrowDown"===i,s="PageUp"===i||"ArrowRight"===i,c="PageDown"===i||"ArrowLeft"===i,u=o.isVertical(),d=u?a:s,p=u?l:c;(d||p)&&(e.preventDefault(),d&&!o.isNextDisabled()?(o.next(),r(o.currentIndexRef.value)):p&&!o.isPrevDisabled()&&(o.prev(),r(o.currentIndexRef.value)))}(t)},handleMouseenter:function(t){"hover"===e.trigger&&o.to(t)},handleClick:function(t){"click"===e.trigger&&o.to(t)}}},render(){const{mergedClsPrefix:e,dotEls:t}=this;return li("div",{class:[`${e}-carousel__dots`,`${e}-carousel__dots--${this.dotType}`],role:"tablist"},function(e,t){const n=[];if(!t){for(let t=0;t{const o=n===this.currentIndex;return li("div",{"aria-selected":o,ref:e=>t.push(e),role:"button",tabindex:"0",class:[`${e}-carousel__dot`,o&&`${e}-carousel__dot--active`],key:n,onClick:()=>{this.handleClick(n)},onMouseenter:()=>{this.handleMouseenter(n)},onKeydown:e=>{this.handleKeydown(e,n)}})})))}}),AO=li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 16 16"},li("g",{fill:"none"},li("path",{d:"M10.26 3.2a.75.75 0 0 1 .04 1.06L6.773 8l3.527 3.74a.75.75 0 1 1-1.1 1.02l-4-4.25a.75.75 0 0 1 0-1.02l4-4.25a.75.75 0 0 1 1.06-.04z",fill:"currentColor"}))),zO=li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 16 16"},li("g",{fill:"none"},li("path",{d:"M5.74 3.2a.75.75 0 0 0-.04 1.06L9.227 8L5.7 11.74a.75.75 0 1 0 1.1 1.02l4-4.25a.75.75 0 0 0 0-1.02l-4-4.25a.75.75 0 0 0-1.06-.04z",fill:"currentColor"}))),RO=zn({name:"CarouselArrow",setup(e){const{mergedClsPrefixRef:t}=B_(e),{isVertical:n,isPrevDisabled:o,isNextDisabled:r,prev:i,next:a}=PO();return{mergedClsPrefix:t,isVertical:n,isPrevDisabled:o,isNextDisabled:r,prev:i,next:a}},render(){const{mergedClsPrefix:e}=this;return li("div",{class:`${e}-carousel__arrow-group`},li("div",{class:[`${e}-carousel__arrow`,this.isPrevDisabled()&&`${e}-carousel__arrow--disabled`],role:"button",onClick:this.prev},AO),li("div",{class:[`${e}-carousel__arrow`,this.isNextDisabled()&&`${e}-carousel__arrow--disabled`],role:"button",onClick:this.next},zO))}}),EO="CarouselItem",OO=zn({name:EO,setup(e){const{mergedClsPrefixRef:t}=B_(e),n=PO(hS(EO),`n-${hS(EO)}`),o=Et(),r=ai((()=>{const{value:e}=o;return e?n.getSlideIndex(e):-1})),i=ai((()=>n.isPrev(r.value))),a=ai((()=>n.isNext(r.value))),l=ai((()=>n.isActive(r.value))),s=ai((()=>n.getSlideStyle(r.value)));return $n((()=>{n.addSlide(o.value)})),Hn((()=>{n.removeSlide(o.value)})),{mergedClsPrefix:t,selfElRef:o,isPrev:i,isNext:a,isActive:l,index:r,style:s,handleClick:function(e){const{value:t}=r;void 0!==t&&(null==n||n.onCarouselItemClick(t,e))}}},render(){var e;const{$slots:t,mergedClsPrefix:n,isPrev:o,isNext:r,isActive:i,index:a,style:l}=this;return li("div",{ref:"selfElRef",class:[`${n}-carousel__slide`,{[`${n}-carousel__slide--current`]:i,[`${n}-carousel__slide--prev`]:o,[`${n}-carousel__slide--next`]:r}],role:"option",tabindex:"-1","data-index":a,"aria-hidden":!i,style:l,onClickCapture:this.handleClick},null===(e=t.default)||void 0===e?void 0:e.call(t,{isPrev:o,isNext:r,isActive:i,index:a}))}}),MO=nb("carousel","\n position: relative;\n width: 100%;\n height: 100%;\n touch-action: pan-y;\n overflow: hidden;\n",[ob("slides","\n display: flex;\n width: 100%;\n height: 100%;\n transition-timing-function: var(--n-bezier);\n transition-property: transform;\n ",[ob("slide","\n flex-shrink: 0;\n position: relative;\n width: 100%;\n height: 100%;\n outline: none;\n overflow: hidden;\n ",[eb("> img","\n display: block;\n ")])]),ob("dots","\n position: absolute;\n display: flex;\n flex-wrap: nowrap;\n ",[rb("dot",[ob("dot","\n height: var(--n-dot-size);\n width: var(--n-dot-size);\n background-color: var(--n-dot-color);\n border-radius: 50%;\n cursor: pointer;\n transition:\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n outline: none;\n ",[eb("&:focus","\n background-color: var(--n-dot-color-focus);\n "),rb("active","\n background-color: var(--n-dot-color-active);\n ")])]),rb("line",[ob("dot","\n border-radius: 9999px;\n width: var(--n-dot-line-width);\n height: 4px;\n background-color: var(--n-dot-color);\n cursor: pointer;\n transition:\n width .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n outline: none;\n ",[eb("&:focus","\n background-color: var(--n-dot-color-focus);\n "),rb("active","\n width: var(--n-dot-line-width-active);\n background-color: var(--n-dot-color-active);\n ")])])]),ob("arrow","\n transition: background-color .3s var(--n-bezier);\n cursor: pointer;\n height: 28px;\n width: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: rgba(255, 255, 255, .2);\n color: var(--n-arrow-color);\n border-radius: 8px;\n user-select: none;\n -webkit-user-select: none;\n font-size: 18px;\n ",[eb("svg","\n height: 1em;\n width: 1em;\n "),eb("&:hover","\n background-color: rgba(255, 255, 255, .3);\n ")]),rb("vertical","\n touch-action: pan-x;\n ",[ob("slides","\n flex-direction: column;\n "),rb("fade",[ob("slide","\n top: 50%;\n left: unset;\n transform: translateY(-50%);\n ")]),rb("card",[ob("slide","\n top: 50%;\n left: unset;\n transform: translateY(-50%) translateZ(-400px);\n ",[rb("current","\n transform: translateY(-50%) translateZ(0);\n "),rb("prev","\n transform: translateY(-100%) translateZ(-200px);\n "),rb("next","\n transform: translateY(0%) translateZ(-200px);\n ")])])]),rb("usercontrol",[ob("slides",[eb(">",[eb("div","\n position: absolute;\n top: 50%;\n left: 50%;\n width: 100%;\n height: 100%;\n transform: translate(-50%, -50%);\n ")])])]),rb("left",[ob("dots","\n transform: translateY(-50%);\n top: 50%;\n left: 12px;\n flex-direction: column;\n ",[rb("line",[ob("dot","\n width: 4px;\n height: var(--n-dot-line-width);\n margin: 4px 0;\n transition:\n height .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n outline: none;\n ",[rb("active","\n height: var(--n-dot-line-width-active);\n ")])])]),ob("dot","\n margin: 4px 0;\n ")]),ob("arrow-group","\n position: absolute;\n display: flex;\n flex-wrap: nowrap;\n "),rb("vertical",[ob("arrow","\n transform: rotate(90deg);\n ")]),rb("show-arrow",[rb("bottom",[ob("dots","\n transform: translateX(0);\n bottom: 18px;\n left: 18px;\n ")]),rb("top",[ob("dots","\n transform: translateX(0);\n top: 18px;\n left: 18px;\n ")]),rb("left",[ob("dots","\n transform: translateX(0);\n top: 18px;\n left: 18px;\n ")]),rb("right",[ob("dots","\n transform: translateX(0);\n top: 18px;\n right: 18px;\n ")])]),rb("left",[ob("arrow-group","\n bottom: 12px;\n left: 12px;\n flex-direction: column;\n ",[eb("> *:first-child","\n margin-bottom: 12px;\n ")])]),rb("right",[ob("dots","\n transform: translateY(-50%);\n top: 50%;\n right: 12px;\n flex-direction: column;\n ",[rb("line",[ob("dot","\n width: 4px;\n height: var(--n-dot-line-width);\n margin: 4px 0;\n transition:\n height .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n outline: none;\n ",[rb("active","\n height: var(--n-dot-line-width-active);\n ")])])]),ob("dot","\n margin: 4px 0;\n "),ob("arrow-group","\n bottom: 12px;\n right: 12px;\n flex-direction: column;\n ",[eb("> *:first-child","\n margin-bottom: 12px;\n ")])]),rb("top",[ob("dots","\n transform: translateX(-50%);\n top: 12px;\n left: 50%;\n ",[rb("line",[ob("dot","\n margin: 0 4px;\n ")])]),ob("dot","\n margin: 0 4px;\n "),ob("arrow-group","\n top: 12px;\n right: 12px;\n ",[eb("> *:first-child","\n margin-right: 12px;\n ")])]),rb("bottom",[ob("dots","\n transform: translateX(-50%);\n bottom: 12px;\n left: 50%;\n ",[rb("line",[ob("dot","\n margin: 0 4px;\n ")])]),ob("dot","\n margin: 0 4px;\n "),ob("arrow-group","\n bottom: 12px;\n right: 12px;\n ",[eb("> *:first-child","\n margin-right: 12px;\n ")])]),rb("fade",[ob("slide","\n position: absolute;\n opacity: 0;\n transition-property: opacity;\n pointer-events: none;\n ",[rb("current","\n opacity: 1;\n pointer-events: auto;\n ")])]),rb("card",[ob("slides","\n perspective: 1000px;\n "),ob("slide","\n position: absolute;\n left: 50%;\n opacity: 0;\n transform: translateX(-50%) translateZ(-400px);\n transition-property: opacity, transform;\n ",[rb("current","\n opacity: 1;\n transform: translateX(-50%) translateZ(0);\n z-index: 1;\n "),rb("prev","\n opacity: 0.4;\n transform: translateX(-100%) translateZ(-200px);\n "),rb("next","\n opacity: 0.4;\n transform: translateX(0%) translateZ(-200px);\n ")])])]),FO=["transitionDuration","transitionTimingFunction"],IO=Object.assign(Object.assign({},I_.props),{defaultIndex:{type:Number,default:0},currentIndex:Number,showArrow:Boolean,dotType:{type:String,default:"dot"},dotPlacement:{type:String,default:"bottom"},slidesPerView:{type:[Number,String],default:1},spaceBetween:{type:Number,default:0},centeredSlides:Boolean,direction:{type:String,default:"horizontal"},autoplay:Boolean,interval:{type:Number,default:5e3},loop:{type:Boolean,default:!0},effect:{type:String,default:"slide"},showDots:{type:Boolean,default:!0},trigger:{type:String,default:"click"},transitionStyle:{type:Object,default:()=>({transitionDuration:"300ms"})},transitionProps:Object,draggable:Boolean,prevSlideStyle:[Object,String],nextSlideStyle:[Object,String],touchable:{type:Boolean,default:!0},mousewheel:Boolean,keyboard:Boolean,"onUpdate:currentIndex":Function,onUpdateCurrentIndex:Function});let LO=!1;const BO=zn({name:"Carousel",props:IO,setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=Et(null),r=Et(null),i=Et([]),a={value:[]},l=ai((()=>"vertical"===e.direction)),s=ai((()=>l.value?"height":"width")),c=ai((()=>l.value?"bottom":"right")),u=ai((()=>"slide"===e.effect)),d=ai((()=>e.loop&&1===e.slidesPerView&&u.value)),p=ai((()=>"custom"===e.effect)),h=ai((()=>!u.value||e.centeredSlides?1:e.slidesPerView)),f=ai((()=>p.value?1:e.slidesPerView)),v=ai((()=>"auto"===h.value||"auto"===e.slidesPerView&&e.centeredSlides)),m=Et({width:0,height:0}),g=ai((()=>{const{value:t}=i;if(!t.length)return[];const{value:n}=v;if(n)return t.map((e=>kO(e)));const{value:o}=f,{value:r}=m,{value:a}=s;let l=r[a];if("auto"!==o){const{spaceBetween:t}=e;l=(l-(o-1)*t)*(1/Math.max(1,o))}const c=Object.assign(Object.assign({},r),{[a]:l});return t.map((()=>c))})),b=ai((()=>{const{value:t}=g;if(!t.length)return[];const{centeredSlides:n,spaceBetween:o}=e,{value:r}=s,{[r]:i}=m.value;let a=0;return t.map((({[r]:e})=>{let t=a;return n&&(t+=(e-i)/2),a+=e+o,t}))})),y=Et(!1),x=ai((()=>{const{transitionStyle:t}=e;return t?sg(t,FO):{}})),C=ai((()=>p.value?0:function(e){if(void 0===e)return 0;if("number"==typeof e)return e;const t=e.match(/^((\d+)?\.?\d+?)(ms|s)?$/);if(t){const[,e,,n="ms"]=t;return Number(e)*("ms"===n?1:1e3)}return 0}(x.value.transitionDuration))),w=ai((()=>{const{value:t}=i;if(!t.length)return[];const n=!(v.value||1===f.value),o=e=>{if(n){const{value:t}=s;return{[t]:`${g.value[e][t]}px`}}};if(p.value)return t.map(((e,t)=>o(t)));const{effect:r,spaceBetween:a}=e,{value:l}=c;return t.reduce(((e,t,n)=>{const i=Object.assign(Object.assign({},o(n)),{[`margin-${l}`]:`${a}px`});return e.push(i),!y.value||"fade"!==r&&"card"!==r||Object.assign(i,x.value),e}),[])})),k=ai((()=>{const{value:e}=h,{length:t}=i.value;if("auto"!==e)return Math.max(t-e,0)+1;{const{value:e}=g,{length:n}=e;if(!n)return t;const{value:o}=b,{value:r}=s,i=m.value[r];let a=e[e.length-1][r],l=n;for(;l>1&&a{return e=k.value,d.value&&e>3?e-2:e;var e})),_=Et(xO(CO(e.defaultIndex,d.value),k.value,d.value)),P=Db(jt(e,"currentIndex"),_),T=ai((()=>CO(P.value,d.value)));function A(t){var n,o;const r=xO(t=SO(t,0,k.value-1),k.value,d.value),{value:i}=P;r!==P.value&&(_.value=r,null===(n=e["onUpdate:currentIndex"])||void 0===n||n.call(e,r,i),null===(o=e.onUpdateCurrentIndex)||void 0===o||o.call(e,r,i))}function z(t=T.value){return n=t,o=k.value,r=e.loop,n<0?null:0===n?r?o-1:null:n-1;var n,o,r}function R(t=T.value){return n=t,o=k.value,r=e.loop,n>o-1?null:n===o-1?r?0:null:n+1;var n,o,r}function E(e){return T.value===q(e)}function O(){return null===z()}function M(){return null===R()}function F(e){const t=SO(CO(e,d.value),0,k.value);e===P.value&&t===T.value||A(t)}function I(){const e=z();null!==e&&A(e)}function L(){const e=R();null!==e&&A(e)}let B=!1,D=0;const $=Et({});function N(e,t=0){$.value=Object.assign({},x.value,{transform:l.value?`translateY(${-e}px)`:`translateX(${-e}px)`,transitionDuration:`${t}ms`})}function j(e=0){u.value?H(T.value,e):0!==D&&(!B&&e>0&&(B=!0),N(D=0,e))}function H(e,t){const n=W(e);n!==D&&t>0&&(B=!0),D=W(T.value),N(n,t)}function W(e){let t;return t=e>=k.value-1?U():b.value[e]||0,t}function U(){if("auto"===h.value){const{value:e}=s,{[e]:t}=m.value,{value:n}=b,o=n[n.length-1];let r;if(void 0===o)r=t;else{const{value:t}=g;r=o+t[t.length-1][e]}return r-t}{const{value:e}=b;return e[k.value-1]||0}}const V={currentIndexRef:P,to:F,prev:function(){B&&d.value||I()},next:function(){B&&d.value||L()},isVertical:()=>l.value,isHorizontal:()=>!l.value,isPrev:function(e){const t=q(e);return null!==t&&z()===t},isNext:function(e){const t=q(e);return null!==t&&R()===t},isActive:E,isPrevDisabled:O,isNextDisabled:M,getSlideIndex:q,getSlideStyle:function(t){const n=q(t);if(-1!==n){const t=[w.value[n]],o=V.isPrev(n),r=V.isNext(n);return o&&t.push(e.prevSlideStyle||""),r&&t.push(e.nextSlideStyle||""),G(t)}},addSlide:function(e){e&&i.value.push(e)},removeSlide:function(e){if(!e)return;const t=q(e);-1!==t&&i.value.splice(t,1)},onCarouselItemClick:function(t,n){let o=!B&&!Z&&!J;"card"===e.effect&&o&&!E(t)&&(F(t),o=!1),o||(n.preventDefault(),n.stopPropagation())}};function q(e){return"number"==typeof e?e:e?i.value.indexOf(e):-1}_o(_O,V);let K=0,X=0,Y=0,Q=0,Z=!1,J=!1,ee=null;function te(){ee&&(clearInterval(ee),ee=null)}function ne(){te(),!e.autoplay||S.value<2||(ee=window.setInterval(L,e.interval))}function oe(t){var n;if(LO)return;if(!(null===(n=r.value)||void 0===n?void 0:n.contains(Fm(t))))return;LO=!0,Z=!0,J=!1,Q=Date.now(),te(),"touchstart"===t.type||t.target.isContentEditable||t.preventDefault();const o=wO(t)?t.touches[0]:t;l.value?X=o.clientY:K=o.clientX,e.touchable&&(Pb("touchmove",document,re),Pb("touchend",document,ie),Pb("touchcancel",document,ie)),e.draggable&&(Pb("mousemove",document,re),Pb("mouseup",document,ie))}function re(e){const{value:t}=l,{value:n}=s,o=wO(e)?e.touches[0]:e,r=t?o.clientY-X:o.clientX-K,i=m.value[n];Y=SO(r,-i,i),e.cancelable&&e.preventDefault(),u.value&&N(D-Y,0)}function ie(){const{value:e}=T;let t=e;if(!B&&0!==Y&&u.value){const e=D-Y,n=[...b.value.slice(0,k.value-1),U()];let o=null;for(let r=0;rr/2||Y/n>.4?t=z(e):(Y<-r/2||Y/n<-.4)&&(t=R(e))}null!==t&&t!==e?(J=!0,A(t),tn((()=>{d.value&&_.value===P.value||j(C.value)}))):j(C.value),ae(),ne()}function ae(){Z&&(LO=!1),Z=!1,K=0,X=0,Y=0,Q=0,Tb("touchmove",document,re),Tb("touchend",document,ie),Tb("touchcancel",document,ie),Tb("mousemove",document,re),Tb("mouseup",document,ie)}function le(e){if(e.preventDefault(),B)return;let{deltaX:t,deltaY:n}=e;e.shiftKey&&!t&&(t=n);const o=(t||n)>0?1:-1;let r=0,i=0;l.value?i=o:r=o,(i*n>=10||r*t>=10)&&(1!==o||M()?-1!==o||O()||I():L())}$n((()=>{ir(ne),requestAnimationFrame((()=>y.value=!0))})),Hn((()=>{ae(),te()})),jn((()=>{const{value:e}=i,{value:t}=a,n=new Map,o=e=>n.has(e)?n.get(e):-1;let r=!1;for(let i=0;it.el===e[i]));o!==i&&(r=!0),n.set(e[i],o)}r&&e.sort(((e,t)=>o(e)-o(t)))})),lr(T,((e,t)=>{if(e!==t)if(ne(),u.value){if(d.value){const{value:n}=k;S.value>2&&e===n-2&&1===t?e=0:1===e&&t===n-2&&(e=n-1)}H(e,C.value)}else j()}),{immediate:!0}),lr([d,h],(()=>{tn((()=>{A(T.value)}))})),lr(b,(()=>{u.value&&j()}),{deep:!0}),lr(u,(e=>{e?j():(B=!1,N(D=0))}));const se=ai((()=>({onTouchstartPassive:e.touchable?oe:void 0,onMousedown:e.draggable?oe:void 0,onWheel:e.mousewheel?le:void 0}))),ce=ai((()=>Object.assign(Object.assign({},sg(V,["to","prev","next","isPrevDisabled","isNextDisabled"])),{total:S.value,currentIndex:P.value}))),ue=ai((()=>({total:S.value,currentIndex:P.value,to:V.to}))),de={getCurrentIndex:()=>P.value,to:F,prev:I,next:L},pe=I_("Carousel","-carousel",MO,gO,e,t),he=ai((()=>{const{common:{cubicBezierEaseInOut:e},self:{dotSize:t,dotColor:n,dotColorActive:o,dotColorFocus:r,dotLineWidth:i,dotLineWidthActive:a,arrowColor:l}}=pe.value;return{"--n-bezier":e,"--n-dot-color":n,"--n-dot-color-focus":r,"--n-dot-color-active":o,"--n-dot-size":t,"--n-dot-line-width":i,"--n-dot-line-width-active":a,"--n-arrow-color":l}})),fe=n?KP("carousel",void 0,he,e):void 0;return Object.assign(Object.assign({mergedClsPrefix:t,selfElRef:o,slidesElRef:r,slideVNodes:a,duplicatedable:d,userWantsControl:p,autoSlideSize:v,realIndex:T,slideStyles:w,translateStyle:$,slidesControlListeners:se,handleTransitionEnd:function(){if(u.value&&B){const{value:e}=T;H(e,0)}else ne();u.value&&($.value.transitionDuration="0ms"),B=!1},handleResize:function(){m.value=kO(o.value,!0),ne()},handleSlideResize:function(){var e,t;v.value&&(null===(t=(e=g.effect).scheduler)||void 0===t||t.call(e),g.effect.run())},handleMouseenter:function(){e.autoplay&&te()},handleMouseleave:function(){e.autoplay&&ne()},isActive:function(e){return P.value===e},arrowSlotProps:ce,dotSlotProps:ue},de),{cssVars:n?void 0:he,themeClass:null==fe?void 0:fe.themeClass,onRender:null==fe?void 0:fe.onRender})},render(){var e;const{mergedClsPrefix:t,showArrow:n,userWantsControl:o,slideStyles:r,dotType:i,dotPlacement:a,slidesControlListeners:l,transitionProps:s={},arrowSlotProps:c,dotSlotProps:u,$slots:{default:d,dots:p,arrow:h}}=this,f=d&&ug(d())||[];let v=f.reduce(((e,t)=>(function(e){var t;return(null===(t=e.type)||void 0===t?void 0:t.name)===EO}(t)&&e.push(t),e)),[]);return v.length||(v=f.map((e=>li(OO,null,{default:()=>Br(e)})))),this.duplicatedable&&(v=function(e){const{length:t}=e;return t>1?(e.push(yO(e[0],0,"append")),e.unshift(yO(e[t-1],t-1,"prepend")),e):e}(v)),this.slideVNodes.value=v,this.autoSlideSize&&(v=v.map((e=>li(kx,{onResize:this.handleSlideResize},{default:()=>e})))),null===(e=this.onRender)||void 0===e||e.call(this),li("div",Object.assign({ref:"selfElRef",class:[this.themeClass,`${t}-carousel`,"vertical"===this.direction&&`${t}-carousel--vertical`,this.showArrow&&`${t}-carousel--show-arrow`,`${t}-carousel--${a}`,`${t}-carousel--${this.direction}`,`${t}-carousel--${this.effect}`,o&&`${t}-carousel--usercontrol`],style:this.cssVars},l,{onMouseenter:this.handleMouseenter,onMouseleave:this.handleMouseleave}),li(kx,{onResize:this.handleResize},{default:()=>li("div",{ref:"slidesElRef",class:`${t}-carousel__slides`,role:"listbox",style:this.translateStyle,onTransitionend:this.handleTransitionEnd},o?v.map(((e,t)=>li("div",{style:r[t],key:t},fn(li(vi,Object.assign({},s),{default:()=>e}),[[Mi,this.isActive(t)]])))):v)}),this.showDots&&u.total>1&&Cg(p,u,(()=>[li(TO,{key:i+a,total:u.total,currentIndex:u.currentIndex,dotType:i,trigger:this.trigger,keyboard:this.keyboard})])),n&&Cg(h,c,(()=>[li(RO,null)])))}}),DO={sizeSmall:"14px",sizeMedium:"16px",sizeLarge:"18px",labelPadding:"0 8px",labelFontWeight:"400"};function $O(e){const{baseColor:t,inputColorDisabled:n,cardColor:o,modalColor:r,popoverColor:i,textColorDisabled:a,borderColor:l,primaryColor:s,textColor2:c,fontSizeSmall:u,fontSizeMedium:d,fontSizeLarge:p,borderRadiusSmall:h,lineHeight:f}=e;return Object.assign(Object.assign({},DO),{labelLineHeight:f,fontSizeSmall:u,fontSizeMedium:d,fontSizeLarge:p,borderRadius:h,color:t,colorChecked:s,colorDisabled:n,colorDisabledChecked:n,colorTableHeader:o,colorTableHeaderModal:r,colorTableHeaderPopover:i,checkMarkColor:t,checkMarkColorDisabled:a,checkMarkColorDisabledChecked:a,border:`1px solid ${l}`,borderDisabled:`1px solid ${l}`,borderDisabledChecked:`1px solid ${l}`,borderChecked:`1px solid ${s}`,borderFocus:`1px solid ${s}`,boxShadowFocus:`0 0 0 2px ${tg(s,{alpha:.3})}`,textColor:c,textColorDisabled:a})}const NO={name:"Checkbox",common:qz,self:$O},jO={name:"Checkbox",common:tz,self(e){const{cardColor:t}=e,n=$O(e);return n.color="#0000",n.checkMarkColor=t,n}},HO={name:"Cascader",common:tz,peers:{InternalSelectMenu:pR,InternalSelection:ZR,Scrollbar:nR,Checkbox:jO,Empty:Xz},self:function(e){const{borderRadius:t,boxShadow2:n,popoverColor:o,textColor2:r,textColor3:i,primaryColor:a,textColorDisabled:l,dividerColor:s,hoverColor:c,fontSizeMedium:u,heightMedium:d}=e;return{menuBorderRadius:t,menuColor:o,menuBoxShadow:n,menuDividerColor:s,menuHeight:"calc(var(--n-option-height) * 6.6)",optionArrowColor:i,optionHeight:d,optionFontSize:u,optionColorHover:c,optionTextColor:r,optionTextColorActive:a,optionTextColorDisabled:l,optionCheckMarkColor:a,loadingColor:a,columnWidth:"180px"}}},WO=li("svg",{viewBox:"0 0 64 64",class:"check-icon"},li("path",{d:"M50.42,16.76L22.34,39.45l-8.1-11.46c-1.12-1.58-3.3-1.96-4.88-0.84c-1.58,1.12-1.95,3.3-0.84,4.88l10.26,14.51 c0.56,0.79,1.42,1.31,2.38,1.45c0.16,0.02,0.32,0.03,0.48,0.03c0.8,0,1.57-0.27,2.2-0.78l30.99-25.03c1.5-1.21,1.74-3.42,0.52-4.92 C54.13,15.78,51.93,15.55,50.42,16.76z"})),UO=li("svg",{viewBox:"0 0 100 100",class:"line-icon"},li("path",{d:"M80.2,55.5H21.4c-2.8,0-5.1-2.5-5.1-5.5l0,0c0-3,2.3-5.5,5.1-5.5h58.7c2.8,0,5.1,2.5,5.1,5.5l0,0C85.2,53.1,82.9,55.5,80.2,55.5z"})),VO="n-checkbox-group",qO=zn({name:"CheckboxGroup",props:{min:Number,max:Number,size:String,value:Array,defaultValue:{type:Array,default:null},disabled:{type:Boolean,default:void 0},"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array],onChange:[Function,Array]},setup(e){const{mergedClsPrefixRef:t}=B_(e),n=eC(e),{mergedSizeRef:o,mergedDisabledRef:r}=n,i=Et(e.defaultValue),a=Db(ai((()=>e.value)),i),l=ai((()=>{var e;return(null===(e=a.value)||void 0===e?void 0:e.length)||0})),s=ai((()=>Array.isArray(a.value)?new Set(a.value):new Set));return _o(VO,{checkedCountRef:l,maxRef:jt(e,"max"),minRef:jt(e,"min"),valueSetRef:s,disabledRef:r,mergedSizeRef:o,toggleCheckbox:function(t,o){const{nTriggerFormInput:r,nTriggerFormChange:l}=n,{onChange:s,"onUpdate:value":c,onUpdateValue:u}=e;if(Array.isArray(a.value)){const e=Array.from(a.value),n=e.findIndex((e=>e===o));t?~n||(e.push(o),u&&dg(u,e,{actionType:"check",value:o}),c&&dg(c,e,{actionType:"check",value:o}),r(),l(),i.value=e,s&&dg(s,e)):~n&&(e.splice(n,1),u&&dg(u,e,{actionType:"uncheck",value:o}),c&&dg(c,e,{actionType:"uncheck",value:o}),s&&dg(s,e),i.value=e,r(),l())}else t?(u&&dg(u,[o],{actionType:"check",value:o}),c&&dg(c,[o],{actionType:"check",value:o}),s&&dg(s,[o]),i.value=[o],r(),l()):(u&&dg(u,[],{actionType:"uncheck",value:o}),c&&dg(c,[],{actionType:"uncheck",value:o}),s&&dg(s,[]),i.value=[],r(),l())}}),{mergedClsPrefix:t}},render(){return li("div",{class:`${this.mergedClsPrefix}-checkbox-group`,role:"group"},this.$slots)}}),KO=eb([nb("checkbox","\n font-size: var(--n-font-size);\n outline: none;\n cursor: pointer;\n display: inline-flex;\n flex-wrap: nowrap;\n align-items: flex-start;\n word-break: break-word;\n line-height: var(--n-size);\n --n-merged-color-table: var(--n-color-table);\n ",[rb("show-label","line-height: var(--n-label-line-height);"),eb("&:hover",[nb("checkbox-box",[ob("border","border: var(--n-border-checked);")])]),eb("&:focus:not(:active)",[nb("checkbox-box",[ob("border","\n border: var(--n-border-focus);\n box-shadow: var(--n-box-shadow-focus);\n ")])]),rb("inside-table",[nb("checkbox-box","\n background-color: var(--n-merged-color-table);\n ")]),rb("checked",[nb("checkbox-box","\n background-color: var(--n-color-checked);\n ",[nb("checkbox-icon",[eb(".check-icon","\n opacity: 1;\n transform: scale(1);\n ")])])]),rb("indeterminate",[nb("checkbox-box",[nb("checkbox-icon",[eb(".check-icon","\n opacity: 0;\n transform: scale(.5);\n "),eb(".line-icon","\n opacity: 1;\n transform: scale(1);\n ")])])]),rb("checked, indeterminate",[eb("&:focus:not(:active)",[nb("checkbox-box",[ob("border","\n border: var(--n-border-checked);\n box-shadow: var(--n-box-shadow-focus);\n ")])]),nb("checkbox-box","\n background-color: var(--n-color-checked);\n border-left: 0;\n border-top: 0;\n ",[ob("border",{border:"var(--n-border-checked)"})])]),rb("disabled",{cursor:"not-allowed"},[rb("checked",[nb("checkbox-box","\n background-color: var(--n-color-disabled-checked);\n ",[ob("border",{border:"var(--n-border-disabled-checked)"}),nb("checkbox-icon",[eb(".check-icon, .line-icon",{fill:"var(--n-check-mark-color-disabled-checked)"})])])]),nb("checkbox-box","\n background-color: var(--n-color-disabled);\n ",[ob("border","\n border: var(--n-border-disabled);\n "),nb("checkbox-icon",[eb(".check-icon, .line-icon","\n fill: var(--n-check-mark-color-disabled);\n ")])]),ob("label","\n color: var(--n-text-color-disabled);\n ")]),nb("checkbox-box-wrapper","\n position: relative;\n width: var(--n-size);\n flex-shrink: 0;\n flex-grow: 0;\n user-select: none;\n -webkit-user-select: none;\n "),nb("checkbox-box","\n position: absolute;\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n height: var(--n-size);\n width: var(--n-size);\n display: inline-block;\n box-sizing: border-box;\n border-radius: var(--n-border-radius);\n background-color: var(--n-color);\n transition: background-color 0.3s var(--n-bezier);\n ",[ob("border","\n transition:\n border-color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier);\n border-radius: inherit;\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n border: var(--n-border);\n "),nb("checkbox-icon","\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n left: 1px;\n right: 1px;\n top: 1px;\n bottom: 1px;\n ",[eb(".check-icon, .line-icon","\n width: 100%;\n fill: var(--n-check-mark-color);\n opacity: 0;\n transform: scale(0.5);\n transform-origin: center;\n transition:\n fill 0.3s var(--n-bezier),\n transform 0.3s var(--n-bezier),\n opacity 0.3s var(--n-bezier),\n border-color 0.3s var(--n-bezier);\n "),PT({left:"1px",top:"1px"})])]),ob("label","\n color: var(--n-text-color);\n transition: color .3s var(--n-bezier);\n user-select: none;\n -webkit-user-select: none;\n padding: var(--n-label-padding);\n font-weight: var(--n-label-font-weight);\n ",[eb("&:empty",{display:"none"})])]),ab(nb("checkbox","\n --n-merged-color-table: var(--n-color-table-modal);\n ")),lb(nb("checkbox","\n --n-merged-color-table: var(--n-color-table-popover);\n "))]),GO=zn({name:"Checkbox",props:Object.assign(Object.assign({},I_.props),{size:String,checked:{type:[Boolean,String,Number],default:void 0},defaultChecked:{type:[Boolean,String,Number],default:!1},value:[String,Number],disabled:{type:Boolean,default:void 0},indeterminate:Boolean,label:String,focusable:{type:Boolean,default:!0},checkedValue:{type:[Boolean,String,Number],default:!0},uncheckedValue:{type:[Boolean,String,Number],default:!1},"onUpdate:checked":[Function,Array],onUpdateChecked:[Function,Array],privateInsideTable:Boolean,onChange:[Function,Array]}),setup(e){const t=Po(VO,null),n=Et(null),{mergedClsPrefixRef:o,inlineThemeDisabled:r,mergedRtlRef:i}=B_(e),a=Et(e.defaultChecked),l=Db(jt(e,"checked"),a),s=mb((()=>{if(t){const n=t.valueSetRef.value;return!(!n||void 0===e.value)&&n.has(e.value)}return l.value===e.checkedValue})),c=eC(e,{mergedSize(n){const{size:o}=e;if(void 0!==o)return o;if(t){const{value:e}=t.mergedSizeRef;if(void 0!==e)return e}if(n){const{mergedSize:e}=n;if(void 0!==e)return e.value}return"medium"},mergedDisabled(n){const{disabled:o}=e;if(void 0!==o)return o;if(t){if(t.disabledRef.value)return!0;const{maxRef:{value:e},checkedCountRef:n}=t;if(void 0!==e&&n.value>=e&&!s.value)return!0;const{minRef:{value:o}}=t;if(void 0!==o&&n.value<=o&&s.value)return!0}return!!n&&n.disabled.value}}),{mergedDisabledRef:u,mergedSizeRef:d}=c,p=I_("Checkbox","-checkbox",KO,NO,e,o);function h(n){if(t&&void 0!==e.value)t.toggleCheckbox(!s.value,e.value);else{const{onChange:t,"onUpdate:checked":o,onUpdateChecked:r}=e,{nTriggerFormInput:i,nTriggerFormChange:l}=c,u=s.value?e.uncheckedValue:e.checkedValue;o&&dg(o,u,n),r&&dg(r,u,n),t&&dg(t,u,n),i(),l(),a.value=u}}const f={focus:()=>{var e;null===(e=n.value)||void 0===e||e.focus()},blur:()=>{var e;null===(e=n.value)||void 0===e||e.blur()}},v=GP("Checkbox",i,o),m=ai((()=>{const{value:e}=d,{common:{cubicBezierEaseInOut:t},self:{borderRadius:n,color:o,colorChecked:r,colorDisabled:i,colorTableHeader:a,colorTableHeaderModal:l,colorTableHeaderPopover:s,checkMarkColor:c,checkMarkColorDisabled:u,border:h,borderFocus:f,borderDisabled:v,borderChecked:m,boxShadowFocus:g,textColor:b,textColorDisabled:y,checkMarkColorDisabledChecked:x,colorDisabledChecked:C,borderDisabledChecked:w,labelPadding:k,labelLineHeight:S,labelFontWeight:_,[ub("fontSize",e)]:P,[ub("size",e)]:T}}=p.value;return{"--n-label-line-height":S,"--n-label-font-weight":_,"--n-size":T,"--n-bezier":t,"--n-border-radius":n,"--n-border":h,"--n-border-checked":m,"--n-border-focus":f,"--n-border-disabled":v,"--n-border-disabled-checked":w,"--n-box-shadow-focus":g,"--n-color":o,"--n-color-checked":r,"--n-color-table":a,"--n-color-table-modal":l,"--n-color-table-popover":s,"--n-color-disabled":i,"--n-color-disabled-checked":C,"--n-text-color":b,"--n-text-color-disabled":y,"--n-check-mark-color":c,"--n-check-mark-color-disabled":u,"--n-check-mark-color-disabled-checked":x,"--n-font-size":P,"--n-label-padding":k}})),g=r?KP("checkbox",ai((()=>d.value[0])),m,e):void 0;return Object.assign(c,f,{rtlEnabled:v,selfRef:n,mergedClsPrefix:o,mergedDisabled:u,renderedChecked:s,mergedTheme:p,labelId:ig(),handleClick:function(e){u.value||h(e)},handleKeyUp:function(e){if(!u.value)switch(e.key){case" ":case"Enter":h(e)}},handleKeyDown:function(e){" "===e.key&&e.preventDefault()},cssVars:r?void 0:m,themeClass:null==g?void 0:g.themeClass,onRender:null==g?void 0:g.onRender})},render(){var e;const{$slots:t,renderedChecked:n,mergedDisabled:o,indeterminate:r,privateInsideTable:i,cssVars:a,labelId:l,label:s,mergedClsPrefix:c,focusable:u,handleKeyUp:d,handleKeyDown:p,handleClick:h}=this;null===(e=this.onRender)||void 0===e||e.call(this);const f=wg(t.default,(e=>s||e?li("span",{class:`${c}-checkbox__label`,id:l},s||e):null));return li("div",{ref:"selfRef",class:[`${c}-checkbox`,this.themeClass,this.rtlEnabled&&`${c}-checkbox--rtl`,n&&`${c}-checkbox--checked`,o&&`${c}-checkbox--disabled`,r&&`${c}-checkbox--indeterminate`,i&&`${c}-checkbox--inside-table`,f&&`${c}-checkbox--show-label`],tabindex:o||!u?void 0:0,role:"checkbox","aria-checked":r?"mixed":n,"aria-labelledby":l,style:a,onKeyup:d,onKeydown:p,onClick:h,onMousedown:()=>{Pb("selectstart",window,(e=>{e.preventDefault()}),{once:!0})}},li("div",{class:`${c}-checkbox-box-wrapper`}," ",li("div",{class:`${c}-checkbox-box`},li(bT,null,{default:()=>this.indeterminate?li("div",{key:"indeterminate",class:`${c}-checkbox-icon`},UO):li("div",{key:"check",class:`${c}-checkbox-icon`},WO)}),li("div",{class:`${c}-checkbox-box__border`}))),f)}}),XO={name:"Code",common:tz,self(e){const{textColor2:t,fontSize:n,fontWeightStrong:o,textColor3:r}=e;return{textColor:t,fontSize:n,fontWeightStrong:o,"mono-3":"#5c6370","hue-1":"#56b6c2","hue-2":"#61aeee","hue-3":"#c678dd","hue-4":"#98c379","hue-5":"#e06c75","hue-5-2":"#be5046","hue-6":"#d19a66","hue-6-2":"#e6c07b",lineNumberTextColor:r}}},YO={name:"Collapse",common:tz,self:function(e){const{fontWeight:t,textColor1:n,textColor2:o,textColorDisabled:r,dividerColor:i,fontSize:a}=e;return{titleFontSize:a,titleFontWeight:t,dividerColor:i,titleTextColor:n,titleTextColorDisabled:r,fontSize:a,textColor:o,arrowColor:o,arrowColorDisabled:r,itemMargin:"16px 0 0 0",titlePadding:"16px 0 0 0"}}},QO={name:"CollapseTransition",common:tz,self:function(e){const{cubicBezierEaseInOut:t}=e;return{bezier:t}}},ZO=zn({name:"ConfigProvider",alias:["App"],props:{abstract:Boolean,bordered:{type:Boolean,default:void 0},clsPrefix:{type:String,default:L_},locale:Object,dateLocale:Object,namespace:String,rtl:Array,tag:{type:String,default:"div"},hljs:Object,katex:Object,theme:Object,themeOverrides:Object,componentOptions:Object,icons:Object,breakpoints:Object,preflightStyleDisabled:Boolean,inlineThemeDisabled:{type:Boolean,default:void 0},as:{type:String,validator:()=>!0,default:void 0}},setup(e){const t=Po(M_,null),n=ai((()=>{const{theme:n}=e;if(null===n)return;const o=null==t?void 0:t.mergedThemeRef.value;return void 0===n?o:void 0===o?n:Object.assign({},o,n)})),o=ai((()=>{const{themeOverrides:n}=e;if(null!==n){if(void 0===n)return null==t?void 0:t.mergedThemeOverridesRef.value;{const e=null==t?void 0:t.mergedThemeOverridesRef.value;return void 0===e?n:__({},e,n)}}})),r=mb((()=>{const{namespace:n}=e;return void 0===n?null==t?void 0:t.mergedNamespaceRef.value:n})),i=mb((()=>{const{bordered:n}=e;return void 0===n?null==t?void 0:t.mergedBorderedRef.value:n})),a=ai((()=>{const{icons:n}=e;return void 0===n?null==t?void 0:t.mergedIconsRef.value:n})),l=ai((()=>{const{componentOptions:n}=e;return void 0!==n?n:null==t?void 0:t.mergedComponentPropsRef.value})),s=ai((()=>{const{clsPrefix:n}=e;return void 0!==n?n:t?t.mergedClsPrefixRef.value:L_})),c=ai((()=>{var n;const{rtl:o}=e;if(void 0===o)return null==t?void 0:t.mergedRtlRef.value;const r={};for(const e of o)r[e.name]=St(e),null===(n=e.peers)||void 0===n||n.forEach((e=>{e.name in r||(r[e.name]=St(e))}));return r})),u=ai((()=>e.breakpoints||(null==t?void 0:t.mergedBreakpointsRef.value))),d=e.inlineThemeDisabled||(null==t?void 0:t.inlineThemeDisabled),p=e.preflightStyleDisabled||(null==t?void 0:t.preflightStyleDisabled),h=ai((()=>{const{value:e}=n,{value:t}=o,r=t&&0!==Object.keys(t).length,i=null==e?void 0:e.name;return i?r?`${i}-${Wg(JSON.stringify(o.value))}`:i:r?Wg(JSON.stringify(o.value)):""}));return _o(M_,{mergedThemeHashRef:h,mergedBreakpointsRef:u,mergedRtlRef:c,mergedIconsRef:a,mergedComponentPropsRef:l,mergedBorderedRef:i,mergedNamespaceRef:r,mergedClsPrefixRef:s,mergedLocaleRef:ai((()=>{const{locale:n}=e;if(null!==n)return void 0===n?null==t?void 0:t.mergedLocaleRef.value:n})),mergedDateLocaleRef:ai((()=>{const{dateLocale:n}=e;if(null!==n)return void 0===n?null==t?void 0:t.mergedDateLocaleRef.value:n})),mergedHljsRef:ai((()=>{const{hljs:n}=e;return void 0===n?null==t?void 0:t.mergedHljsRef.value:n})),mergedKatexRef:ai((()=>{const{katex:n}=e;return void 0===n?null==t?void 0:t.mergedKatexRef.value:n})),mergedThemeRef:n,mergedThemeOverridesRef:o,inlineThemeDisabled:d||!1,preflightStyleDisabled:p||!1}),{mergedClsPrefix:s,mergedBordered:i,mergedNamespace:r,mergedTheme:n,mergedThemeOverrides:o}},render(){var e,t,n,o;return this.abstract?null===(o=(n=this.$slots).default)||void 0===o?void 0:o.call(n):li(this.as||this.tag,{class:`${this.mergedClsPrefix||L_}-config-provider`},null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e))}});function JO(e){const{from:t,to:n,duration:o,onUpdate:r,onFinish:i}=e,a=performance.now(),l=()=>{const e=performance.now(),s=Math.min(e-a,o),c=t+(n-t)*(u=s/o,1-Math.pow(1-u,5));var u;s!==o?(r(c),requestAnimationFrame(l)):i()};l()}const eM=zn({name:"NumberAnimation",props:{to:{type:Number,default:0},precision:{type:Number,default:0},showSeparator:Boolean,locale:String,from:{type:Number,default:0},active:{type:Boolean,default:!0},duration:{type:Number,default:2e3},onFinish:Function},setup(e){const{localeRef:t}=VP("name"),{duration:n}=e,o=Et(e.from),r=ai((()=>{const{locale:n}=e;return void 0!==n?n:t.value}));let i=!1;const a=e=>{o.value=e},l=()=>{var t;o.value=e.to,i=!1,null===(t=e.onFinish)||void 0===t||t.call(e)},s=(t=e.from,r=e.to)=>{i=!0,o.value=e.from,t!==r&&JO({from:t,to:r,duration:n,onUpdate:a,onFinish:l})},c=ai((()=>{var t;const n=T_(o.value,e.precision).toFixed(e.precision).split("."),i=new Intl.NumberFormat(r.value),a=null===(t=i.formatToParts(.5).find((e=>"decimal"===e.type)))||void 0===t?void 0:t.value;return{integer:e.showSeparator?i.format(Number(n[0])):n[0],decimal:n[1],decimalSeparator:a}}));$n((()=>{ir((()=>{e.active&&s()}))}));const u={play:function(){i||s()}};return Object.assign({formattedValue:c},u)},render(){const{formattedValue:{integer:e,decimal:t,decimalSeparator:n}}=this;return[e,t?n:null,t]}}),tM={name:"Popselect",common:tz,peers:{Popover:_R,InternalSelectMenu:pR}},nM={name:"Popselect",common:qz,peers:{Popover:SR,InternalSelectMenu:dR},self:function(e){const{boxShadow2:t}=e;return{menuBoxShadow:t}}},oM="n-popselect",rM=nb("popselect-menu","\n box-shadow: var(--n-menu-box-shadow);\n"),iM={multiple:Boolean,value:{type:[String,Number,Array],default:null},cancelable:Boolean,options:{type:Array,default:()=>[]},size:{type:String,default:"medium"},scrollable:Boolean,"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array],onMouseenter:Function,onMouseleave:Function,renderLabel:Function,showCheckmark:{type:Boolean,default:void 0},nodeProps:Function,virtualScroll:Boolean,onChange:[Function,Array]},aM=pg(iM),lM=zn({name:"PopselectPanel",props:iM,setup(e){const t=Po(oM),{mergedClsPrefixRef:n,inlineThemeDisabled:o}=B_(e),r=I_("Popselect","-pop-select",rM,nM,t.props,n),i=ai((()=>JT(e.options,mE("value","children"))));function a(t,n){const{onUpdateValue:o,"onUpdate:value":r,onChange:i}=e;o&&dg(o,t,n),r&&dg(r,t,n),i&&dg(i,t,n)}lr(jt(e,"options"),(()=>{tn((()=>{t.syncPosition()}))}));const l=ai((()=>{const{self:{menuBoxShadow:e}}=r.value;return{"--n-menu-box-shadow":e}})),s=o?KP("select",void 0,l,t.props):void 0;return{mergedTheme:t.mergedThemeRef,mergedClsPrefix:n,treeMate:i,handleToggle:function(n){!function(n){const{value:{getNode:o}}=i;if(e.multiple)if(Array.isArray(e.value)){const t=[],r=[];let i=!0;e.value.forEach((e=>{if(e===n)return void(i=!1);const a=o(e);a&&(t.push(a.key),r.push(a.rawNode))})),i&&(t.push(n),r.push(o(n).rawNode)),a(t,r)}else{const e=o(n);e&&a([n],[e.rawNode])}else if(e.value===n&&e.cancelable)a(null,null);else{const e=o(n);e&&a(n,e.rawNode);const{"onUpdate:show":r,onUpdateShow:i}=t.props;r&&dg(r,!1),i&&dg(i,!1),t.setShow(!1)}tn((()=>{t.syncPosition()}))}(n.key)},handleMenuMousedown:function(e){Mm(e,"action")||Mm(e,"empty")||Mm(e,"header")||e.preventDefault()},cssVars:o?void 0:l,themeClass:null==s?void 0:s.themeClass,onRender:null==s?void 0:s.onRender}},render(){var e;return null===(e=this.onRender)||void 0===e||e.call(this),li(yR,{clsPrefix:this.mergedClsPrefix,focusable:!0,nodeProps:this.nodeProps,class:[`${this.mergedClsPrefix}-popselect-menu`,this.themeClass],style:this.cssVars,theme:this.mergedTheme.peers.InternalSelectMenu,themeOverrides:this.mergedTheme.peerOverrides.InternalSelectMenu,multiple:this.multiple,treeMate:this.treeMate,size:this.size,value:this.value,virtualScroll:this.virtualScroll,scrollable:this.scrollable,renderLabel:this.renderLabel,onToggle:this.handleToggle,onMouseenter:this.onMouseenter,onMouseleave:this.onMouseenter,onMousedown:this.handleMenuMousedown,showCheckmark:this.showCheckmark},{header:()=>{var e,t;return(null===(t=(e=this.$slots).header)||void 0===t?void 0:t.call(e))||[]},action:()=>{var e,t;return(null===(t=(e=this.$slots).action)||void 0===t?void 0:t.call(e))||[]},empty:()=>{var e,t;return(null===(t=(e=this.$slots).empty)||void 0===t?void 0:t.call(e))||[]}})}}),sM=zn({name:"Popselect",props:Object.assign(Object.assign(Object.assign(Object.assign({},I_.props),cg(DR,["showArrow","arrow"])),{placement:Object.assign(Object.assign({},DR.placement),{default:"bottom"}),trigger:{type:String,default:"hover"}}),iM),inheritAttrs:!1,__popover__:!0,setup(e){const{mergedClsPrefixRef:t}=B_(e),n=I_("Popselect","-popselect",void 0,nM,e,t),o=Et(null);function r(){var e;null===(e=o.value)||void 0===e||e.syncPosition()}function i(e){var t;null===(t=o.value)||void 0===t||t.setShow(e)}_o(oM,{props:e,mergedThemeRef:n,syncPosition:r,setShow:i});const a={syncPosition:r,setShow:i};return Object.assign(Object.assign({},a),{popoverInstRef:o,mergedTheme:n})},render(){const{mergedTheme:e}=this,t={theme:e.peers.Popover,themeOverrides:e.peerOverrides.Popover,builtinThemeOverrides:{padding:"0"},ref:"popoverInstRef",internalRenderBody:(e,t,n,o,r)=>{const{$attrs:i}=this;return li(lM,Object.assign({},i,{class:[i.class,e],style:[i.style,...n]},sg(this.$props,aM),{ref:bg(t),onMouseenter:Sg([o,i.onMouseenter]),onMouseleave:Sg([r,i.onMouseleave])}),{header:()=>{var e,t;return null===(t=(e=this.$slots).header)||void 0===t?void 0:t.call(e)},action:()=>{var e,t;return null===(t=(e=this.$slots).action)||void 0===t?void 0:t.call(e)},empty:()=>{var e,t;return null===(t=(e=this.$slots).empty)||void 0===t?void 0:t.call(e)}})}};return li($R,Object.assign({},cg(this.$props,aM),t,{internalDeactivateImmediately:!0}),{trigger:()=>{var e,t;return null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)}})}});function cM(e){const{boxShadow2:t}=e;return{menuBoxShadow:t}}const uM={name:"Select",common:qz,peers:{InternalSelection:QR,InternalSelectMenu:dR},self:cM},dM={name:"Select",common:tz,peers:{InternalSelection:ZR,InternalSelectMenu:pR},self:cM},pM=eb([nb("select","\n z-index: auto;\n outline: none;\n width: 100%;\n position: relative;\n "),nb("select-menu","\n margin: 4px 0;\n box-shadow: var(--n-menu-box-shadow);\n ",[gR({originalTransition:"background-color .3s var(--n-bezier), box-shadow .3s var(--n-bezier)"})])]),hM=zn({name:"Select",props:Object.assign(Object.assign({},I_.props),{to:Yb.propTo,bordered:{type:Boolean,default:void 0},clearable:Boolean,clearFilterAfterSelect:{type:Boolean,default:!0},options:{type:Array,default:()=>[]},defaultValue:{type:[String,Number,Array],default:null},keyboard:{type:Boolean,default:!0},value:[String,Number,Array],placeholder:String,menuProps:Object,multiple:Boolean,size:String,filterable:Boolean,disabled:{type:Boolean,default:void 0},remote:Boolean,loading:Boolean,filter:Function,placement:{type:String,default:"bottom-start"},widthMode:{type:String,default:"trigger"},tag:Boolean,onCreate:Function,fallbackOption:{type:[Function,Boolean],default:void 0},show:{type:Boolean,default:void 0},showArrow:{type:Boolean,default:!0},maxTagCount:[Number,String],ellipsisTagPopoverProps:Object,consistentMenuWidth:{type:Boolean,default:!0},virtualScroll:{type:Boolean,default:!0},labelField:{type:String,default:"label"},valueField:{type:String,default:"value"},childrenField:{type:String,default:"children"},renderLabel:Function,renderOption:Function,renderTag:Function,"onUpdate:value":[Function,Array],inputProps:Object,nodeProps:Function,ignoreComposition:{type:Boolean,default:!0},showOnFocus:Boolean,onUpdateValue:[Function,Array],onBlur:[Function,Array],onClear:[Function,Array],onFocus:[Function,Array],onScroll:[Function,Array],onSearch:[Function,Array],onUpdateShow:[Function,Array],"onUpdate:show":[Function,Array],displayDirective:{type:String,default:"show"},resetMenuOnOptionsChange:{type:Boolean,default:!0},status:String,showCheckmark:{type:Boolean,default:!0},onChange:[Function,Array],items:Array}),setup(e){const{mergedClsPrefixRef:t,mergedBorderedRef:n,namespaceRef:o,inlineThemeDisabled:r}=B_(e),i=I_("Select","-select",pM,uM,e,t),a=Et(e.defaultValue),l=Db(jt(e,"value"),a),s=Et(!1),c=Et(""),u=Nb(e,["items","options"]),d=Et([]),p=Et([]),h=ai((()=>p.value.concat(d.value).concat(u.value))),f=ai((()=>{const{filter:t}=e;if(t)return t;const{labelField:n,valueField:o}=e;return(e,t)=>{if(!t)return!1;const r=t[n];if("string"==typeof r)return vE(e,r);const i=t[o];return"string"==typeof i?vE(e,i):"number"==typeof i&&vE(e,String(i))}})),v=ai((()=>{if(e.remote)return u.value;{const{value:t}=h,{value:n}=c;return n.length&&e.filterable?function(e,t,n,o){return t?function e(r){if(!Array.isArray(r))return[];const i=[];for(const a of r)if(hE(a)){const t=e(a[o]);t.length&&i.push(Object.assign({},a,{[o]:t}))}else{if(fE(a))continue;t(n,a)&&i.push(a)}return i}(e):e}(t,f.value,n,e.childrenField):t}})),m=ai((()=>{const{valueField:t,childrenField:n}=e,o=mE(t,n);return JT(v.value,o)})),g=ai((()=>function(e,t,n){const o=new Map;return e.forEach((e=>{hE(e)?e[n].forEach((e=>{o.set(e[t],e)})):o.set(e[t],e)})),o}(h.value,e.valueField,e.childrenField))),b=Et(!1),y=Db(jt(e,"show"),b),x=Et(null),C=Et(null),w=Et(null),{localeRef:k}=VP("Select"),S=ai((()=>{var t;return null!==(t=e.placeholder)&&void 0!==t?t:k.value.placeholder})),_=[],P=Et(new Map),T=ai((()=>{const{fallbackOption:t}=e;if(void 0===t){const{labelField:t,valueField:n}=e;return e=>({[t]:String(e),[n]:e})}return!1!==t&&(e=>Object.assign(t(e),{value:e}))}));function A(t){const n=e.remote,{value:o}=P,{value:r}=g,{value:i}=T,a=[];return t.forEach((e=>{if(r.has(e))a.push(r.get(e));else if(n&&o.has(e))a.push(o.get(e));else if(i){const t=i(e);t&&a.push(t)}})),a}const z=ai((()=>{if(e.multiple){const{value:e}=l;return Array.isArray(e)?A(e):[]}return null})),R=ai((()=>{const{value:t}=l;return e.multiple||Array.isArray(t)||null===t?null:A([t])[0]||null})),E=eC(e),{mergedSizeRef:O,mergedDisabledRef:M,mergedStatusRef:F}=E;function I(t,n){const{onChange:o,"onUpdate:value":r,onUpdateValue:i}=e,{nTriggerFormChange:l,nTriggerFormInput:s}=E;o&&dg(o,t,n),i&&dg(i,t,n),r&&dg(r,t,n),a.value=t,l(),s()}function L(t){const{onBlur:n}=e,{nTriggerFormBlur:o}=E;n&&dg(n,t),o()}function B(){var t;const{remote:n,multiple:o}=e;if(n){const{value:n}=P;if(o){const{valueField:o}=e;null===(t=z.value)||void 0===t||t.forEach((e=>{n.set(e[o],e)}))}else{const t=R.value;t&&n.set(t[e.valueField],t)}}}function D(t){const{onUpdateShow:n,"onUpdate:show":o}=e;n&&dg(n,t),o&&dg(o,t),b.value=t}function $(){M.value||(D(!0),b.value=!0,e.filterable&&G())}function N(){D(!1)}function j(){c.value="",p.value=_}const H=Et(!1);function W(e){U(e.rawNode)}function U(t){if(M.value)return;const{tag:n,remote:o,clearFilterAfterSelect:r,valueField:i}=e;if(n&&!o){const{value:e}=p,t=e[0]||null;if(t){const e=d.value;e.length?e.push(t):d.value=[t],p.value=_}}if(o&&P.value.set(t[i],t),e.multiple){const a=function(t){if(!Array.isArray(t))return[];if(T.value)return Array.from(t);{const{remote:n}=e,{value:o}=g;if(n){const{value:e}=P;return t.filter((t=>o.has(t)||e.has(t)))}return t.filter((e=>o.has(e)))}}(l.value),s=a.findIndex((e=>e===t[i]));if(~s){if(a.splice(s,1),n&&!o){const e=V(t[i]);~e&&(d.value.splice(e,1),r&&(c.value=""))}}else a.push(t[i]),r&&(c.value="");I(a,A(a))}else{if(n&&!o){const e=V(t[i]);d.value=~e?[d.value[e]]:_}K(),N(),I(t[i],t)}}function V(t){return d.value.findIndex((n=>n[e.valueField]===t))}function q(t){var n,o,r,i,a,s;if(e.keyboard)switch(t.key){case" ":if(e.filterable)break;t.preventDefault();case"Enter":if(!(null===(n=x.value)||void 0===n?void 0:n.isComposing))if(y.value){const t=null===(o=w.value)||void 0===o?void 0:o.getPendingTmNode();t?W(t):e.filterable||(N(),K())}else if($(),e.tag&&H.value){const t=p.value[0];if(t){const n=t[e.valueField],{value:o}=l;e.multiple&&Array.isArray(o)&&o.includes(n)||U(t)}}t.preventDefault();break;case"ArrowUp":if(t.preventDefault(),e.loading)return;y.value&&(null===(r=w.value)||void 0===r||r.prev());break;case"ArrowDown":if(t.preventDefault(),e.loading)return;y.value?null===(i=w.value)||void 0===i||i.next():$();break;case"Escape":y.value&&(s=t,hb.add(s),N()),null===(a=x.value)||void 0===a||a.focus()}else t.preventDefault()}function K(){var e;null===(e=x.value)||void 0===e||e.focus()}function G(){var e;null===(e=x.value)||void 0===e||e.focusInput()}B(),lr(jt(e,"options"),B);const X={focus:()=>{var e;null===(e=x.value)||void 0===e||e.focus()},focusInput:()=>{var e;null===(e=x.value)||void 0===e||e.focusInput()},blur:()=>{var e;null===(e=x.value)||void 0===e||e.blur()},blurInput:()=>{var e;null===(e=x.value)||void 0===e||e.blurInput()}},Y=ai((()=>{const{self:{menuBoxShadow:e}}=i.value;return{"--n-menu-box-shadow":e}})),Q=r?KP("select",void 0,Y,e):void 0;return Object.assign(Object.assign({},X),{mergedStatus:F,mergedClsPrefix:t,mergedBordered:n,namespace:o,treeMate:m,isMounted:$b(),triggerRef:x,menuRef:w,pattern:c,uncontrolledShow:b,mergedShow:y,adjustedTo:Yb(e),uncontrolledValue:a,mergedValue:l,followerRef:C,localizedPlaceholder:S,selectedOption:R,selectedOptions:z,mergedSize:O,mergedDisabled:M,focused:s,activeWithoutMenuOpen:H,inlineThemeDisabled:r,onTriggerInputFocus:function(){e.filterable&&(H.value=!0)},onTriggerInputBlur:function(){e.filterable&&(H.value=!1,y.value||j())},handleTriggerOrMenuResize:function(){var e;y.value&&(null===(e=C.value)||void 0===e||e.syncPosition())},handleMenuFocus:function(){s.value=!0},handleMenuBlur:function(e){var t;(null===(t=x.value)||void 0===t?void 0:t.$el.contains(e.relatedTarget))||(s.value=!1,L(e),N())},handleMenuTabOut:function(){var e;null===(e=x.value)||void 0===e||e.focus(),N()},handleTriggerClick:function(){M.value||(y.value?e.filterable?G():N():$())},handleToggle:W,handleDeleteOption:U,handlePatternInput:function(t){y.value||$();const{value:n}=t.target;c.value=n;const{tag:o,remote:r}=e;if(function(t){const{onSearch:n}=e;n&&dg(n,t)}(n),o&&!r){if(!n)return void(p.value=_);const{onCreate:t}=e,o=t?t(n):{[e.labelField]:n,[e.valueField]:n},{valueField:r,labelField:i}=e;u.value.some((e=>e[r]===o[r]||e[i]===o[i]))||d.value.some((e=>e[r]===o[r]||e[i]===o[i]))?p.value=_:p.value=[o]}},handleClear:function(t){t.stopPropagation();const{multiple:n}=e;!n&&e.filterable&&N(),function(){const{onClear:t}=e;t&&dg(t)}(),n?I([],[]):I(null,null)},handleTriggerBlur:function(e){var t,n;(null===(n=null===(t=w.value)||void 0===t?void 0:t.selfRef)||void 0===n?void 0:n.contains(e.relatedTarget))||(s.value=!1,L(e),N())},handleTriggerFocus:function(t){!function(t){const{onFocus:n,showOnFocus:o}=e,{nTriggerFormFocus:r}=E;n&&dg(n,t),r(),o&&$()}(t),s.value=!0},handleKeydown:q,handleMenuAfterLeave:j,handleMenuClickOutside:function(e){var t;y.value&&((null===(t=x.value)||void 0===t?void 0:t.$el.contains(Fm(e)))||N())},handleMenuScroll:function(t){!function(t){const{onScroll:n}=e;n&&dg(n,t)}(t)},handleMenuKeydown:q,handleMenuMousedown:function(e){Mm(e,"action")||Mm(e,"empty")||Mm(e,"header")||e.preventDefault()},mergedTheme:i,cssVars:r?void 0:Y,themeClass:null==Q?void 0:Q.themeClass,onRender:null==Q?void 0:Q.onRender})},render(){return li("div",{class:`${this.mergedClsPrefix}-select`},li(ay,null,{default:()=>[li(ly,null,{default:()=>li(eE,{ref:"triggerRef",inlineThemeDisabled:this.inlineThemeDisabled,status:this.mergedStatus,inputProps:this.inputProps,clsPrefix:this.mergedClsPrefix,showArrow:this.showArrow,maxTagCount:this.maxTagCount,ellipsisTagPopoverProps:this.ellipsisTagPopoverProps,bordered:this.mergedBordered,active:this.activeWithoutMenuOpen||this.mergedShow,pattern:this.pattern,placeholder:this.localizedPlaceholder,selectedOption:this.selectedOption,selectedOptions:this.selectedOptions,multiple:this.multiple,renderTag:this.renderTag,renderLabel:this.renderLabel,filterable:this.filterable,clearable:this.clearable,disabled:this.mergedDisabled,size:this.mergedSize,theme:this.mergedTheme.peers.InternalSelection,labelField:this.labelField,valueField:this.valueField,themeOverrides:this.mergedTheme.peerOverrides.InternalSelection,loading:this.loading,focused:this.focused,onClick:this.handleTriggerClick,onDeleteOption:this.handleDeleteOption,onPatternInput:this.handlePatternInput,onClear:this.handleClear,onBlur:this.handleTriggerBlur,onFocus:this.handleTriggerFocus,onKeydown:this.handleKeydown,onPatternBlur:this.onTriggerInputBlur,onPatternFocus:this.onTriggerInputFocus,onResize:this.handleTriggerOrMenuResize,ignoreComposition:this.ignoreComposition},{arrow:()=>{var e,t;return[null===(t=(e=this.$slots).arrow)||void 0===t?void 0:t.call(e)]}})}),li(Fy,{ref:"followerRef",show:this.mergedShow,to:this.adjustedTo,teleportDisabled:this.adjustedTo===Yb.tdkey,containerClass:this.namespace,width:this.consistentMenuWidth?"target":void 0,minWidth:"target",placement:this.placement},{default:()=>li(vi,{name:"fade-in-scale-up-transition",appear:this.isMounted,onAfterLeave:this.handleMenuAfterLeave},{default:()=>{var e,t,n;return this.mergedShow||"show"===this.displayDirective?(null===(e=this.onRender)||void 0===e||e.call(this),fn(li(yR,Object.assign({},this.menuProps,{ref:"menuRef",onResize:this.handleTriggerOrMenuResize,inlineThemeDisabled:this.inlineThemeDisabled,virtualScroll:this.consistentMenuWidth&&this.virtualScroll,class:[`${this.mergedClsPrefix}-select-menu`,this.themeClass,null===(t=this.menuProps)||void 0===t?void 0:t.class],clsPrefix:this.mergedClsPrefix,focusable:!0,labelField:this.labelField,valueField:this.valueField,autoPending:!0,nodeProps:this.nodeProps,theme:this.mergedTheme.peers.InternalSelectMenu,themeOverrides:this.mergedTheme.peerOverrides.InternalSelectMenu,treeMate:this.treeMate,multiple:this.multiple,size:"medium",renderOption:this.renderOption,renderLabel:this.renderLabel,value:this.mergedValue,style:[null===(n=this.menuProps)||void 0===n?void 0:n.style,this.cssVars],onToggle:this.handleToggle,onScroll:this.handleMenuScroll,onFocus:this.handleMenuFocus,onBlur:this.handleMenuBlur,onKeydown:this.handleMenuKeydown,onTabOut:this.handleMenuTabOut,onMousedown:this.handleMenuMousedown,show:this.mergedShow,showCheckmark:this.showCheckmark,resetMenuOnOptionsChange:this.resetMenuOnOptionsChange}),{empty:()=>{var e,t;return[null===(t=(e=this.$slots).empty)||void 0===t?void 0:t.call(e)]},header:()=>{var e,t;return[null===(t=(e=this.$slots).header)||void 0===t?void 0:t.call(e)]},action:()=>{var e,t;return[null===(t=(e=this.$slots).action)||void 0===t?void 0:t.call(e)]}}),"show"===this.displayDirective?[[Mi,this.mergedShow],[dy,this.handleMenuClickOutside,void 0,{capture:!0}]]:[[dy,this.handleMenuClickOutside,void 0,{capture:!0}]])):null}})})]}))}}),fM={itemPaddingSmall:"0 4px",itemMarginSmall:"0 0 0 8px",itemMarginSmallRtl:"0 8px 0 0",itemPaddingMedium:"0 4px",itemMarginMedium:"0 0 0 8px",itemMarginMediumRtl:"0 8px 0 0",itemPaddingLarge:"0 4px",itemMarginLarge:"0 0 0 8px",itemMarginLargeRtl:"0 8px 0 0",buttonIconSizeSmall:"14px",buttonIconSizeMedium:"16px",buttonIconSizeLarge:"18px",inputWidthSmall:"60px",selectWidthSmall:"unset",inputMarginSmall:"0 0 0 8px",inputMarginSmallRtl:"0 8px 0 0",selectMarginSmall:"0 0 0 8px",prefixMarginSmall:"0 8px 0 0",suffixMarginSmall:"0 0 0 8px",inputWidthMedium:"60px",selectWidthMedium:"unset",inputMarginMedium:"0 0 0 8px",inputMarginMediumRtl:"0 8px 0 0",selectMarginMedium:"0 0 0 8px",prefixMarginMedium:"0 8px 0 0",suffixMarginMedium:"0 0 0 8px",inputWidthLarge:"60px",selectWidthLarge:"unset",inputMarginLarge:"0 0 0 8px",inputMarginLargeRtl:"0 8px 0 0",selectMarginLarge:"0 0 0 8px",prefixMarginLarge:"0 8px 0 0",suffixMarginLarge:"0 0 0 8px"};function vM(e){const{textColor2:t,primaryColor:n,primaryColorHover:o,primaryColorPressed:r,inputColorDisabled:i,textColorDisabled:a,borderColor:l,borderRadius:s,fontSizeTiny:c,fontSizeSmall:u,fontSizeMedium:d,heightTiny:p,heightSmall:h,heightMedium:f}=e;return Object.assign(Object.assign({},fM),{buttonColor:"#0000",buttonColorHover:"#0000",buttonColorPressed:"#0000",buttonBorder:`1px solid ${l}`,buttonBorderHover:`1px solid ${l}`,buttonBorderPressed:`1px solid ${l}`,buttonIconColor:t,buttonIconColorHover:t,buttonIconColorPressed:t,itemTextColor:t,itemTextColorHover:o,itemTextColorPressed:r,itemTextColorActive:n,itemTextColorDisabled:a,itemColor:"#0000",itemColorHover:"#0000",itemColorPressed:"#0000",itemColorActive:"#0000",itemColorActiveHover:"#0000",itemColorDisabled:i,itemBorder:"1px solid #0000",itemBorderHover:"1px solid #0000",itemBorderPressed:"1px solid #0000",itemBorderActive:`1px solid ${n}`,itemBorderDisabled:`1px solid ${l}`,itemBorderRadius:s,itemSizeSmall:p,itemSizeMedium:h,itemSizeLarge:f,itemFontSizeSmall:c,itemFontSizeMedium:u,itemFontSizeLarge:d,jumperFontSizeSmall:c,jumperFontSizeMedium:u,jumperFontSizeLarge:d,jumperTextColor:t,jumperTextColorDisabled:a})}const mM={name:"Pagination",common:qz,peers:{Select:uM,Input:CE,Popselect:nM},self:vM},gM={name:"Pagination",common:tz,peers:{Select:dM,Input:xE,Popselect:tM},self(e){const{primaryColor:t,opacity3:n}=e,o=tg(t,{alpha:Number(n)}),r=vM(e);return r.itemBorderActive=`1px solid ${o}`,r.itemBorderDisabled="1px solid #0000",r}},bM="\n background: var(--n-item-color-hover);\n color: var(--n-item-text-color-hover);\n border: var(--n-item-border-hover);\n",yM=[rb("button","\n background: var(--n-button-color-hover);\n border: var(--n-button-border-hover);\n color: var(--n-button-icon-color-hover);\n ")],xM=nb("pagination","\n display: flex;\n vertical-align: middle;\n font-size: var(--n-item-font-size);\n flex-wrap: nowrap;\n",[nb("pagination-prefix","\n display: flex;\n align-items: center;\n margin: var(--n-prefix-margin);\n "),nb("pagination-suffix","\n display: flex;\n align-items: center;\n margin: var(--n-suffix-margin);\n "),eb("> *:not(:first-child)","\n margin: var(--n-item-margin);\n "),nb("select","\n width: var(--n-select-width);\n "),eb("&.transition-disabled",[nb("pagination-item","transition: none!important;")]),nb("pagination-quick-jumper","\n white-space: nowrap;\n display: flex;\n color: var(--n-jumper-text-color);\n transition: color .3s var(--n-bezier);\n align-items: center;\n font-size: var(--n-jumper-font-size);\n ",[nb("input","\n margin: var(--n-input-margin);\n width: var(--n-input-width);\n ")]),nb("pagination-item","\n position: relative;\n cursor: pointer;\n user-select: none;\n -webkit-user-select: none;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n min-width: var(--n-item-size);\n height: var(--n-item-size);\n padding: var(--n-item-padding);\n background-color: var(--n-item-color);\n color: var(--n-item-text-color);\n border-radius: var(--n-item-border-radius);\n border: var(--n-item-border);\n fill: var(--n-button-icon-color);\n transition:\n color .3s var(--n-bezier),\n border-color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n fill .3s var(--n-bezier);\n ",[rb("button","\n background: var(--n-button-color);\n color: var(--n-button-icon-color);\n border: var(--n-button-border);\n padding: 0;\n ",[nb("base-icon","\n font-size: var(--n-button-icon-size);\n ")]),ib("disabled",[rb("hover",bM,yM),eb("&:hover",bM,yM),eb("&:active","\n background: var(--n-item-color-pressed);\n color: var(--n-item-text-color-pressed);\n border: var(--n-item-border-pressed);\n ",[rb("button","\n background: var(--n-button-color-pressed);\n border: var(--n-button-border-pressed);\n color: var(--n-button-icon-color-pressed);\n ")]),rb("active","\n background: var(--n-item-color-active);\n color: var(--n-item-text-color-active);\n border: var(--n-item-border-active);\n ",[eb("&:hover","\n background: var(--n-item-color-active-hover);\n ")])]),rb("disabled","\n cursor: not-allowed;\n color: var(--n-item-text-color-disabled);\n ",[rb("active, button","\n background-color: var(--n-item-color-disabled);\n border: var(--n-item-border-disabled);\n ")])]),rb("disabled","\n cursor: not-allowed;\n ",[nb("pagination-quick-jumper","\n color: var(--n-jumper-text-color-disabled);\n ")]),rb("simple","\n display: flex;\n align-items: center;\n flex-wrap: nowrap;\n ",[nb("pagination-quick-jumper",[nb("input","\n margin: 0;\n ")])])]);function CM(e){var t;if(!e)return 10;const{defaultPageSize:n}=e;if(void 0!==n)return n;const o=null===(t=e.pageSizes)||void 0===t?void 0:t[0];return"number"==typeof o?o:(null==o?void 0:o.value)||10}function wM(e,t){const n=[];for(let o=e;o<=t;++o)n.push({label:`${o}`,value:o});return n}const kM=zn({name:"Pagination",props:Object.assign(Object.assign({},I_.props),{simple:Boolean,page:Number,defaultPage:{type:Number,default:1},itemCount:Number,pageCount:Number,defaultPageCount:{type:Number,default:1},showSizePicker:Boolean,pageSize:Number,defaultPageSize:Number,pageSizes:{type:Array,default:()=>[10]},showQuickJumper:Boolean,size:{type:String,default:"medium"},disabled:Boolean,pageSlot:{type:Number,default:9},selectProps:Object,prev:Function,next:Function,goto:Function,prefix:Function,suffix:Function,label:Function,displayOrder:{type:Array,default:["pages","size-picker","quick-jumper"]},to:Yb.propTo,showQuickJumpDropdown:{type:Boolean,default:!0},"onUpdate:page":[Function,Array],onUpdatePage:[Function,Array],"onUpdate:pageSize":[Function,Array],onUpdatePageSize:[Function,Array],onPageSizeChange:[Function,Array],onChange:[Function,Array]}),setup(e){const{mergedComponentPropsRef:t,mergedClsPrefixRef:n,inlineThemeDisabled:o,mergedRtlRef:r}=B_(e),i=I_("Pagination","-pagination",xM,mM,e,n),{localeRef:a}=VP("Pagination"),l=Et(null),s=Et(e.defaultPage),c=Et(CM(e)),u=Db(jt(e,"page"),s),d=Db(jt(e,"pageSize"),c),p=ai((()=>{const{itemCount:t}=e;if(void 0!==t)return Math.max(1,Math.ceil(t/d.value));const{pageCount:n}=e;return void 0!==n?Math.max(n,1):1})),h=Et("");ir((()=>{e.simple,h.value=String(u.value)}));const f=Et(!1),v=Et(!1),m=Et(!1),g=Et(!1),b=ai((()=>function(e,t,n,o){let r=!1,i=!1,a=1,l=t;if(1===t)return{hasFastBackward:!1,hasFastForward:!1,fastForwardTo:l,fastBackwardTo:a,items:[{type:"page",label:1,active:1===e,mayBeFastBackward:!1,mayBeFastForward:!1}]};if(2===t)return{hasFastBackward:!1,hasFastForward:!1,fastForwardTo:l,fastBackwardTo:a,items:[{type:"page",label:1,active:1===e,mayBeFastBackward:!1,mayBeFastForward:!1},{type:"page",label:2,active:2===e,mayBeFastBackward:!0,mayBeFastForward:!1}]};const s=t;let c=e,u=e;const d=(n-5)/2;u+=Math.ceil(d),u=Math.min(Math.max(u,1+n-3),s-2),c-=Math.floor(d),c=Math.max(Math.min(c,s-n+3),3);let p=!1,h=!1;c>3&&(p=!0),u=2&&f.push({type:"page",label:2,mayBeFastBackward:!0,mayBeFastForward:!1,active:2===e});for(let v=c;v<=u;++v)f.push({type:"page",label:v,mayBeFastBackward:!1,mayBeFastForward:!1,active:e===v});return h?(i=!0,l=u+1,f.push({type:"fast-forward",active:!1,label:void 0,options:o?wM(u+1,s-1):null})):u===s-2&&f[f.length-1].label!==s-1&&f.push({type:"page",mayBeFastForward:!0,mayBeFastBackward:!1,label:s-1,active:e===s-1}),f[f.length-1].label!==s&&f.push({type:"page",mayBeFastForward:!1,mayBeFastBackward:!1,label:s,active:e===s}),{hasFastBackward:r,hasFastForward:i,fastBackwardTo:a,fastForwardTo:l,items:f}}(u.value,p.value,e.pageSlot,e.showQuickJumpDropdown)));ir((()=>{b.value.hasFastBackward?b.value.hasFastForward||(f.value=!1,m.value=!1):(v.value=!1,g.value=!1)}));const y=ai((()=>{const t=a.value.selectionSuffix;return e.pageSizes.map((e=>"number"==typeof e?{label:`${e} / ${t}`,value:e}:e))})),x=ai((()=>{var n,o;return(null===(o=null===(n=null==t?void 0:t.value)||void 0===n?void 0:n.Pagination)||void 0===o?void 0:o.inputSize)||vg(e.size)})),C=ai((()=>{var n,o;return(null===(o=null===(n=null==t?void 0:t.value)||void 0===n?void 0:n.Pagination)||void 0===o?void 0:o.selectSize)||vg(e.size)})),w=ai((()=>(u.value-1)*d.value)),k=ai((()=>{const t=u.value*d.value-1,{itemCount:n}=e;return void 0!==n&&t>n-1?n-1:t})),S=ai((()=>{const{itemCount:t}=e;return void 0!==t?t:(e.pageCount||1)*d.value})),_=GP("Pagination",r,n);function P(){tn((()=>{var e;const{value:t}=l;t&&(t.classList.add("transition-disabled"),null===(e=l.value)||void 0===e||e.offsetWidth,t.classList.remove("transition-disabled"))}))}function T(t){if(t===u.value)return;const{"onUpdate:page":n,onUpdatePage:o,onChange:r,simple:i}=e;n&&dg(n,t),o&&dg(o,t),r&&dg(r,t),s.value=t,i&&(h.value=String(t))}ir((()=>{u.value,d.value,P()}));const A=ai((()=>{const{size:t}=e,{self:{buttonBorder:n,buttonBorderHover:o,buttonBorderPressed:r,buttonIconColor:a,buttonIconColorHover:l,buttonIconColorPressed:s,itemTextColor:c,itemTextColorHover:u,itemTextColorPressed:d,itemTextColorActive:p,itemTextColorDisabled:h,itemColor:f,itemColorHover:v,itemColorPressed:m,itemColorActive:g,itemColorActiveHover:b,itemColorDisabled:y,itemBorder:x,itemBorderHover:C,itemBorderPressed:w,itemBorderActive:k,itemBorderDisabled:S,itemBorderRadius:_,jumperTextColor:P,jumperTextColorDisabled:T,buttonColor:A,buttonColorHover:z,buttonColorPressed:R,[ub("itemPadding",t)]:E,[ub("itemMargin",t)]:O,[ub("inputWidth",t)]:M,[ub("selectWidth",t)]:F,[ub("inputMargin",t)]:I,[ub("selectMargin",t)]:L,[ub("jumperFontSize",t)]:B,[ub("prefixMargin",t)]:D,[ub("suffixMargin",t)]:$,[ub("itemSize",t)]:N,[ub("buttonIconSize",t)]:j,[ub("itemFontSize",t)]:H,[`${ub("itemMargin",t)}Rtl`]:W,[`${ub("inputMargin",t)}Rtl`]:U},common:{cubicBezierEaseInOut:V}}=i.value;return{"--n-prefix-margin":D,"--n-suffix-margin":$,"--n-item-font-size":H,"--n-select-width":F,"--n-select-margin":L,"--n-input-width":M,"--n-input-margin":I,"--n-input-margin-rtl":U,"--n-item-size":N,"--n-item-text-color":c,"--n-item-text-color-disabled":h,"--n-item-text-color-hover":u,"--n-item-text-color-active":p,"--n-item-text-color-pressed":d,"--n-item-color":f,"--n-item-color-hover":v,"--n-item-color-disabled":y,"--n-item-color-active":g,"--n-item-color-active-hover":b,"--n-item-color-pressed":m,"--n-item-border":x,"--n-item-border-hover":C,"--n-item-border-disabled":S,"--n-item-border-active":k,"--n-item-border-pressed":w,"--n-item-padding":E,"--n-item-border-radius":_,"--n-bezier":V,"--n-jumper-font-size":B,"--n-jumper-text-color":P,"--n-jumper-text-color-disabled":T,"--n-item-margin":O,"--n-item-margin-rtl":W,"--n-button-icon-size":j,"--n-button-icon-color":a,"--n-button-icon-color-hover":l,"--n-button-icon-color-pressed":s,"--n-button-color-hover":z,"--n-button-color":A,"--n-button-color-pressed":R,"--n-button-border":n,"--n-button-border-hover":o,"--n-button-border-pressed":r}})),z=o?KP("pagination",ai((()=>{let t="";const{size:n}=e;return t+=n[0],t})),A,e):void 0;return{rtlEnabled:_,mergedClsPrefix:n,locale:a,selfRef:l,mergedPage:u,pageItems:ai((()=>b.value.items)),mergedItemCount:S,jumperValue:h,pageSizeOptions:y,mergedPageSize:d,inputSize:x,selectSize:C,mergedTheme:i,mergedPageCount:p,startIndex:w,endIndex:k,showFastForwardMenu:m,showFastBackwardMenu:g,fastForwardActive:f,fastBackwardActive:v,handleMenuSelect:e=>{T(e)},handleFastForwardMouseenter:()=>{e.disabled||(f.value=!0,P())},handleFastForwardMouseleave:()=>{e.disabled||(f.value=!1,P())},handleFastBackwardMouseenter:()=>{v.value=!0,P()},handleFastBackwardMouseleave:()=>{v.value=!1,P()},handleJumperInput:function(e){h.value=e.replace(/\D+/g,"")},handleBackwardClick:function(){e.disabled||T(Math.max(u.value-1,1))},handleForwardClick:function(){e.disabled||T(Math.min(u.value+1,p.value))},handlePageItemClick:function(t){if(!e.disabled)switch(t.type){case"page":T(t.label);break;case"fast-backward":e.disabled||T(Math.max(b.value.fastBackwardTo,1));break;case"fast-forward":e.disabled||T(Math.min(b.value.fastForwardTo,p.value))}},handleSizePickerChange:function(t){!function(t){if(t===d.value)return;const{"onUpdate:pageSize":n,onUpdatePageSize:o,onPageSizeChange:r}=e;n&&dg(n,t),o&&dg(o,t),r&&dg(r,t),c.value=t,p.value{switch(e){case"pages":return li(yr,null,li("div",{class:[`${t}-pagination-item`,!O&&`${t}-pagination-item--button`,(r<=1||r>i||n)&&`${t}-pagination-item--disabled`],onClick:_},O?O({page:r,pageSize:h,pageCount:i,startIndex:this.startIndex,endIndex:this.endIndex,itemCount:this.mergedItemCount}):li(CT,{clsPrefix:t},{default:()=>this.rtlEnabled?li(cT,null):li(ZP,null)})),m?li(yr,null,li("div",{class:`${t}-pagination-quick-jumper`},li(AE,{value:v,onUpdateValue:k,size:d,placeholder:"",disabled:n,theme:c.peers.Input,themeOverrides:c.peerOverrides.Input,onChange:A}))," /"," ",i):a.map(((e,o)=>{let r,i,a;const{type:l}=e;switch(l){case"page":const n=e.label;r=F?F({type:"page",node:n,active:e.active}):n;break;case"fast-forward":const o=this.fastForwardActive?li(CT,{clsPrefix:t},{default:()=>this.rtlEnabled?li(aT,null):li(lT,null)}):li(CT,{clsPrefix:t},{default:()=>li(dT,null)});r=F?F({type:"fast-forward",node:o,active:this.fastForwardActive||this.showFastForwardMenu}):o,i=this.handleFastForwardMouseenter,a=this.handleFastForwardMouseleave;break;case"fast-backward":const l=this.fastBackwardActive?li(CT,{clsPrefix:t},{default:()=>this.rtlEnabled?li(lT,null):li(aT,null)}):li(CT,{clsPrefix:t},{default:()=>li(dT,null)});r=F?F({type:"fast-backward",node:l,active:this.fastBackwardActive||this.showFastBackwardMenu}):l,i=this.handleFastBackwardMouseenter,a=this.handleFastBackwardMouseleave}const s=li("div",{key:o,class:[`${t}-pagination-item`,e.active&&`${t}-pagination-item--active`,"page"!==l&&("fast-backward"===l&&this.showFastBackwardMenu||"fast-forward"===l&&this.showFastForwardMenu)&&`${t}-pagination-item--hover`,n&&`${t}-pagination-item--disabled`,"page"===l&&`${t}-pagination-item--clickable`],onClick:()=>{P(e)},onMouseenter:i,onMouseleave:a},r);if("page"!==l||e.mayBeFastBackward||e.mayBeFastForward){const t="page"===e.type?e.mayBeFastBackward?"fast-backward":"fast-forward":e.type;return"page"===e.type||e.options?li(sM,{to:this.to,key:t,disabled:n,trigger:"hover",virtualScroll:!0,style:{width:"60px"},theme:c.peers.Popselect,themeOverrides:c.peerOverrides.Popselect,builtinThemeOverrides:{peers:{InternalSelectMenu:{height:"calc(var(--n-option-height) * 4.6)"}}},nodeProps:()=>({style:{justifyContent:"center"}}),show:"page"!==l&&("fast-backward"===l?this.showFastBackwardMenu:this.showFastForwardMenu),onUpdateShow:e=>{"page"!==l&&(e?"fast-backward"===l?this.showFastBackwardMenu=e:this.showFastForwardMenu=e:(this.showFastBackwardMenu=!1,this.showFastForwardMenu=!1))},options:"page"!==e.type&&e.options?e.options:[],onUpdateValue:this.handleMenuSelect,scrollable:!0,showCheckmark:!1},{default:()=>s}):s}return s})),li("div",{class:[`${t}-pagination-item`,!M&&`${t}-pagination-item--button`,{[`${t}-pagination-item--disabled`]:r<1||r>=i||n}],onClick:T},M?M({page:r,pageSize:h,pageCount:i,itemCount:this.mergedItemCount,startIndex:this.startIndex,endIndex:this.endIndex}):li(CT,{clsPrefix:t},{default:()=>this.rtlEnabled?li(ZP,null):li(cT,null)})));case"size-picker":return!m&&l?li(hM,Object.assign({consistentMenuWidth:!1,placeholder:"",showCheckmark:!1,to:this.to},this.selectProps,{size:p,options:f,value:h,disabled:n,theme:c.peers.Select,themeOverrides:c.peerOverrides.Select,onUpdateValue:S})):null;case"quick-jumper":return!m&&s?li("div",{class:`${t}-pagination-quick-jumper`},w?w():xg(this.$slots.goto,(()=>[u.goto])),li(AE,{value:v,onUpdateValue:k,size:d,placeholder:"",disabled:n,theme:c.peers.Input,themeOverrides:c.peerOverrides.Input,onChange:A})):null;default:return null}})),E?li("div",{class:`${t}-pagination-suffix`},E({page:r,pageSize:h,pageCount:i,startIndex:this.startIndex,endIndex:this.endIndex,itemCount:this.mergedItemCount})):null)}}),SM={padding:"8px 14px"},_M={name:"Tooltip",common:tz,peers:{Popover:_R},self(e){const{borderRadius:t,boxShadow2:n,popoverColor:o,textColor2:r}=e;return Object.assign(Object.assign({},SM),{borderRadius:t,boxShadow:n,color:o,textColor:r})}},PM={name:"Tooltip",common:qz,peers:{Popover:SR},self:function(e){const{borderRadius:t,boxShadow2:n,baseColor:o}=e;return Object.assign(Object.assign({},SM),{borderRadius:t,boxShadow:n,color:eg(o,"rgba(0, 0, 0, .85)"),textColor:o})}},TM={name:"Ellipsis",common:tz,peers:{Tooltip:_M}},AM={name:"Ellipsis",common:qz,peers:{Tooltip:PM}},zM={radioSizeSmall:"14px",radioSizeMedium:"16px",radioSizeLarge:"18px",labelPadding:"0 8px",labelFontWeight:"400"},RM={name:"Radio",common:tz,self(e){const{borderColor:t,primaryColor:n,baseColor:o,textColorDisabled:r,inputColorDisabled:i,textColor2:a,opacityDisabled:l,borderRadius:s,fontSizeSmall:c,fontSizeMedium:u,fontSizeLarge:d,heightSmall:p,heightMedium:h,heightLarge:f,lineHeight:v}=e;return Object.assign(Object.assign({},zM),{labelLineHeight:v,buttonHeightSmall:p,buttonHeightMedium:h,buttonHeightLarge:f,fontSizeSmall:c,fontSizeMedium:u,fontSizeLarge:d,boxShadow:`inset 0 0 0 1px ${t}`,boxShadowActive:`inset 0 0 0 1px ${n}`,boxShadowFocus:`inset 0 0 0 1px ${n}, 0 0 0 2px ${tg(n,{alpha:.3})}`,boxShadowHover:`inset 0 0 0 1px ${n}`,boxShadowDisabled:`inset 0 0 0 1px ${t}`,color:"#0000",colorDisabled:i,colorActive:"#0000",textColor:a,textColorDisabled:r,dotColorActive:n,dotColorDisabled:t,buttonBorderColor:t,buttonBorderColorActive:n,buttonBorderColorHover:n,buttonColor:"#0000",buttonColorActive:n,buttonTextColor:a,buttonTextColorActive:o,buttonTextColorHover:n,opacityDisabled:l,buttonBoxShadowFocus:`inset 0 0 0 1px ${n}, 0 0 0 2px ${tg(n,{alpha:.3})}`,buttonBoxShadowHover:`inset 0 0 0 1px ${n}`,buttonBoxShadow:"inset 0 0 0 1px #0000",buttonBorderRadius:s})}},EM={name:"Radio",common:qz,self:function(e){const{borderColor:t,primaryColor:n,baseColor:o,textColorDisabled:r,inputColorDisabled:i,textColor2:a,opacityDisabled:l,borderRadius:s,fontSizeSmall:c,fontSizeMedium:u,fontSizeLarge:d,heightSmall:p,heightMedium:h,heightLarge:f,lineHeight:v}=e;return Object.assign(Object.assign({},zM),{labelLineHeight:v,buttonHeightSmall:p,buttonHeightMedium:h,buttonHeightLarge:f,fontSizeSmall:c,fontSizeMedium:u,fontSizeLarge:d,boxShadow:`inset 0 0 0 1px ${t}`,boxShadowActive:`inset 0 0 0 1px ${n}`,boxShadowFocus:`inset 0 0 0 1px ${n}, 0 0 0 2px ${tg(n,{alpha:.2})}`,boxShadowHover:`inset 0 0 0 1px ${n}`,boxShadowDisabled:`inset 0 0 0 1px ${t}`,color:o,colorDisabled:i,colorActive:"#0000",textColor:a,textColorDisabled:r,dotColorActive:n,dotColorDisabled:t,buttonBorderColor:t,buttonBorderColorActive:n,buttonBorderColorHover:t,buttonColor:o,buttonColorActive:o,buttonTextColor:a,buttonTextColorActive:n,buttonTextColorHover:n,opacityDisabled:l,buttonBoxShadowFocus:`inset 0 0 0 1px ${n}, 0 0 0 2px ${tg(n,{alpha:.3})}`,buttonBoxShadowHover:"inset 0 0 0 1px #0000",buttonBoxShadow:"inset 0 0 0 1px #0000",buttonBorderRadius:s})}},OM={padding:"4px 0",optionIconSizeSmall:"14px",optionIconSizeMedium:"16px",optionIconSizeLarge:"16px",optionIconSizeHuge:"18px",optionSuffixWidthSmall:"14px",optionSuffixWidthMedium:"14px",optionSuffixWidthLarge:"16px",optionSuffixWidthHuge:"16px",optionIconSuffixWidthSmall:"32px",optionIconSuffixWidthMedium:"32px",optionIconSuffixWidthLarge:"36px",optionIconSuffixWidthHuge:"36px",optionPrefixWidthSmall:"14px",optionPrefixWidthMedium:"14px",optionPrefixWidthLarge:"16px",optionPrefixWidthHuge:"16px",optionIconPrefixWidthSmall:"36px",optionIconPrefixWidthMedium:"36px",optionIconPrefixWidthLarge:"40px",optionIconPrefixWidthHuge:"40px"};function MM(e){const{primaryColor:t,textColor2:n,dividerColor:o,hoverColor:r,popoverColor:i,invertedColor:a,borderRadius:l,fontSizeSmall:s,fontSizeMedium:c,fontSizeLarge:u,fontSizeHuge:d,heightSmall:p,heightMedium:h,heightLarge:f,heightHuge:v,textColor3:m,opacityDisabled:g}=e;return Object.assign(Object.assign({},OM),{optionHeightSmall:p,optionHeightMedium:h,optionHeightLarge:f,optionHeightHuge:v,borderRadius:l,fontSizeSmall:s,fontSizeMedium:c,fontSizeLarge:u,fontSizeHuge:d,optionTextColor:n,optionTextColorHover:n,optionTextColorActive:t,optionTextColorChildActive:t,color:i,dividerColor:o,suffixColor:n,prefixColor:n,optionColorHover:r,optionColorActive:tg(t,{alpha:.1}),groupHeaderTextColor:m,optionTextColorInverted:"#BBB",optionTextColorHoverInverted:"#FFF",optionTextColorActiveInverted:"#FFF",optionTextColorChildActiveInverted:"#FFF",colorInverted:a,dividerColorInverted:"#BBB",suffixColorInverted:"#BBB",prefixColorInverted:"#BBB",optionColorHoverInverted:t,optionColorActiveInverted:t,groupHeaderTextColorInverted:"#AAA",optionOpacityDisabled:g})}const FM={name:"Dropdown",common:qz,peers:{Popover:SR},self:MM},IM={name:"Dropdown",common:tz,peers:{Popover:_R},self(e){const{primaryColorSuppl:t,primaryColor:n,popoverColor:o}=e,r=MM(e);return r.colorInverted=o,r.optionColorActive=tg(n,{alpha:.15}),r.optionColorActiveInverted=t,r.optionColorHoverInverted=t,r}},LM={thPaddingSmall:"8px",thPaddingMedium:"12px",thPaddingLarge:"12px",tdPaddingSmall:"8px",tdPaddingMedium:"12px",tdPaddingLarge:"12px",sorterSize:"15px",resizableContainerSize:"8px",resizableSize:"2px",filterSize:"15px",paginationMargin:"12px 0 0 0",emptyPadding:"48px 0",actionPadding:"8px 12px",actionButtonMargin:"0 8px 0 0"};function BM(e){const{cardColor:t,modalColor:n,popoverColor:o,textColor2:r,textColor1:i,tableHeaderColor:a,tableColorHover:l,iconColor:s,primaryColor:c,fontWeightStrong:u,borderRadius:d,lineHeight:p,fontSizeSmall:h,fontSizeMedium:f,fontSizeLarge:v,dividerColor:m,heightSmall:g,opacityDisabled:b,tableColorStriped:y}=e;return Object.assign(Object.assign({},LM),{actionDividerColor:m,lineHeight:p,borderRadius:d,fontSizeSmall:h,fontSizeMedium:f,fontSizeLarge:v,borderColor:eg(t,m),tdColorHover:eg(t,l),tdColorSorting:eg(t,l),tdColorStriped:eg(t,y),thColor:eg(t,a),thColorHover:eg(eg(t,a),l),thColorSorting:eg(eg(t,a),l),tdColor:t,tdTextColor:r,thTextColor:i,thFontWeight:u,thButtonColorHover:l,thIconColor:s,thIconColorActive:c,borderColorModal:eg(n,m),tdColorHoverModal:eg(n,l),tdColorSortingModal:eg(n,l),tdColorStripedModal:eg(n,y),thColorModal:eg(n,a),thColorHoverModal:eg(eg(n,a),l),thColorSortingModal:eg(eg(n,a),l),tdColorModal:n,borderColorPopover:eg(o,m),tdColorHoverPopover:eg(o,l),tdColorSortingPopover:eg(o,l),tdColorStripedPopover:eg(o,y),thColorPopover:eg(o,a),thColorHoverPopover:eg(eg(o,a),l),thColorSortingPopover:eg(eg(o,a),l),tdColorPopover:o,boxShadowBefore:"inset -12px 0 8px -12px rgba(0, 0, 0, .18)",boxShadowAfter:"inset 12px 0 8px -12px rgba(0, 0, 0, .18)",loadingColor:c,loadingSize:g,opacityLoading:b})}const DM={name:"DataTable",common:qz,peers:{Button:JE,Checkbox:NO,Radio:EM,Pagination:mM,Scrollbar:tR,Empty:Xz,Popover:SR,Ellipsis:AM,Dropdown:FM},self:BM},$M={name:"DataTable",common:tz,peers:{Button:eO,Checkbox:jO,Radio:RM,Pagination:gM,Scrollbar:nR,Empty:Yz,Popover:_R,Ellipsis:TM,Dropdown:IM},self(e){const t=BM(e);return t.boxShadowAfter="inset 12px 0 8px -12px rgba(0, 0, 0, .36)",t.boxShadowBefore="inset -12px 0 8px -12px rgba(0, 0, 0, .36)",t}},NM=zn({name:"Tooltip",props:Object.assign(Object.assign({},DR),I_.props),__popover__:!0,setup(e){const{mergedClsPrefixRef:t}=B_(e),n=I_("Tooltip","-tooltip",void 0,PM,e,t),o=Et(null),r={syncPosition(){o.value.syncPosition()},setShow(e){o.value.setShow(e)}};return Object.assign(Object.assign({},r),{popoverRef:o,mergedTheme:n,popoverThemeOverrides:ai((()=>n.value.self))})},render(){const{mergedTheme:e,internalExtraClass:t}=this;return li($R,Object.assign(Object.assign({},this.$props),{theme:e.peers.Popover,themeOverrides:e.peerOverrides.Popover,builtinThemeOverrides:this.popoverThemeOverrides,internalExtraClass:t.concat("tooltip"),ref:"popoverRef"}),this.$slots)}}),jM=nb("ellipsis",{overflow:"hidden"},[ib("line-clamp","\n white-space: nowrap;\n display: inline-block;\n vertical-align: bottom;\n max-width: 100%;\n "),rb("line-clamp","\n display: -webkit-inline-box;\n -webkit-box-orient: vertical;\n "),rb("cursor-pointer","\n cursor: pointer;\n ")]);function HM(e){return`${e}-ellipsis--line-clamp`}function WM(e,t){return`${e}-ellipsis--cursor-${t}`}const UM=Object.assign(Object.assign({},I_.props),{expandTrigger:String,lineClamp:[Number,String],tooltip:{type:[Boolean,Object],default:!0}}),VM=zn({name:"Ellipsis",inheritAttrs:!1,props:UM,setup(e,{slots:t,attrs:n}){const o=D_(),r=I_("Ellipsis","-ellipsis",jM,AM,e,o),i=Et(null),a=Et(null),l=Et(null),s=Et(!1),c=ai((()=>{const{lineClamp:t}=e,{value:n}=s;return void 0!==t?{textOverflow:"","-webkit-line-clamp":n?"":t}:{textOverflow:n?"":"ellipsis","-webkit-line-clamp":""}}));function u(){let t=!1;const{value:n}=s;if(n)return!0;const{value:r}=i;if(r){const{lineClamp:n}=e;if(function(t){if(!t)return;const n=c.value,r=HM(o.value);void 0!==e.lineClamp?p(t,r,"add"):p(t,r,"remove");for(const e in n)t.style[e]!==n[e]&&(t.style[e]=n[e])}(r),void 0!==n)t=r.scrollHeight<=r.offsetHeight;else{const{value:e}=a;e&&(t=e.getBoundingClientRect().width<=r.getBoundingClientRect().width)}!function(t,n){const r=WM(o.value,"pointer");"click"!==e.expandTrigger||n?p(t,r,"remove"):p(t,r,"add")}(r,t)}return t}const d=ai((()=>"click"===e.expandTrigger?()=>{var e;const{value:t}=s;t&&(null===(e=l.value)||void 0===e||e.setShow(!1)),s.value=!t}:void 0));function p(e,t,n){"add"===n?e.classList.contains(t)||e.classList.add(t):e.classList.contains(t)&&e.classList.remove(t)}return Mn((()=>{var t;e.tooltip&&(null===(t=l.value)||void 0===t||t.setShow(!1))})),{mergedTheme:r,triggerRef:i,triggerInnerRef:a,tooltipRef:l,handleClick:d,renderTrigger:()=>li("span",Object.assign({},Wr(n,{class:[`${o.value}-ellipsis`,void 0!==e.lineClamp?HM(o.value):void 0,"click"===e.expandTrigger?WM(o.value,"pointer"):void 0],style:c.value}),{ref:"triggerRef",onClick:d.value,onMouseenter:"click"===e.expandTrigger?u:void 0}),e.lineClamp?t:li("span",{ref:"triggerInnerRef"},t)),getTooltipDisabled:u}},render(){var e;const{tooltip:t,renderTrigger:n,$slots:o}=this;if(t){const{mergedTheme:r}=this;return li(NM,Object.assign({ref:"tooltipRef",placement:"top"},t,{getDisabled:this.getTooltipDisabled,theme:r.peers.Tooltip,themeOverrides:r.peerOverrides.Tooltip}),{trigger:n,default:null!==(e=o.tooltip)&&void 0!==e?e:o.default})}return n()}}),qM=zn({name:"PerformantEllipsis",props:UM,inheritAttrs:!1,setup(e,{attrs:t,slots:n}){const o=Et(!1),r=D_();return qP("-ellipsis",jM,r),{mouseEntered:o,renderTrigger:()=>{const{lineClamp:i}=e,a=r.value;return li("span",Object.assign({},Wr(t,{class:[`${a}-ellipsis`,void 0!==i?HM(a):void 0,"click"===e.expandTrigger?WM(a,"pointer"):void 0],style:void 0===i?{textOverflow:"ellipsis"}:{"-webkit-line-clamp":i}}),{onMouseenter:()=>{o.value=!0}}),i?n:li("span",null,n))}}},render(){return this.mouseEntered?li(VM,Wr({},this.$attrs,this.$props),this.$slots):this.renderTrigger()}}),KM=Object.assign(Object.assign({},I_.props),{onUnstableColumnResize:Function,pagination:{type:[Object,Boolean],default:!1},paginateSinglePage:{type:Boolean,default:!0},minHeight:[Number,String],maxHeight:[Number,String],columns:{type:Array,default:()=>[]},rowClassName:[String,Function],rowProps:Function,rowKey:Function,summary:[Function],data:{type:Array,default:()=>[]},loading:Boolean,bordered:{type:Boolean,default:void 0},bottomBordered:{type:Boolean,default:void 0},striped:Boolean,scrollX:[Number,String],defaultCheckedRowKeys:{type:Array,default:()=>[]},checkedRowKeys:Array,singleLine:{type:Boolean,default:!0},singleColumn:Boolean,size:{type:String,default:"medium"},remote:Boolean,defaultExpandedRowKeys:{type:Array,default:[]},defaultExpandAll:Boolean,expandedRowKeys:Array,stickyExpandedRows:Boolean,virtualScroll:Boolean,tableLayout:{type:String,default:"auto"},allowCheckingNotLoaded:Boolean,cascade:{type:Boolean,default:!0},childrenKey:{type:String,default:"children"},indent:{type:Number,default:16},flexHeight:Boolean,summaryPlacement:{type:String,default:"bottom"},paginationBehaviorOnFilter:{type:String,default:"current"},filterIconPopoverProps:Object,scrollbarProps:Object,renderCell:Function,renderExpandIcon:Function,spinProps:{type:Object,default:{}},onLoad:Function,"onUpdate:page":[Function,Array],onUpdatePage:[Function,Array],"onUpdate:pageSize":[Function,Array],onUpdatePageSize:[Function,Array],"onUpdate:sorter":[Function,Array],onUpdateSorter:[Function,Array],"onUpdate:filters":[Function,Array],onUpdateFilters:[Function,Array],"onUpdate:checkedRowKeys":[Function,Array],onUpdateCheckedRowKeys:[Function,Array],"onUpdate:expandedRowKeys":[Function,Array],onUpdateExpandedRowKeys:[Function,Array],onScroll:Function,onPageChange:[Function,Array],onPageSizeChange:[Function,Array],onSorterChange:[Function,Array],onFiltersChange:[Function,Array],onCheckedRowKeysChange:[Function,Array]}),GM="n-data-table",XM=zn({name:"DataTableRenderSorter",props:{render:{type:Function,required:!0},order:{type:[String,Boolean],default:!1}},render(){const{render:e,order:t}=this;return e({order:t})}}),YM=zn({name:"SortIcon",props:{column:{type:Object,required:!0}},setup(e){const{mergedComponentPropsRef:t}=B_(),{mergedSortStateRef:n,mergedClsPrefixRef:o}=Po(GM),r=ai((()=>n.value.find((t=>t.columnKey===e.column.key)))),i=ai((()=>void 0!==r.value)),a=ai((()=>{const{value:e}=r;return!(!e||!i.value)&&e.order})),l=ai((()=>{var n,o;return(null===(o=null===(n=null==t?void 0:t.value)||void 0===n?void 0:n.DataTable)||void 0===o?void 0:o.renderSorter)||e.column.renderSorter}));return{mergedClsPrefix:o,active:i,mergedSortOrder:a,mergedRenderSorter:l}},render(){const{mergedRenderSorter:e,mergedSortOrder:t,mergedClsPrefix:n}=this,{renderSorterIcon:o}=this.column;return e?li(XM,{render:e,order:t}):li("span",{class:[`${n}-data-table-sorter`,"ascend"===t&&`${n}-data-table-sorter--asc`,"descend"===t&&`${n}-data-table-sorter--desc`]},o?o({order:t}):li(CT,{clsPrefix:n},{default:()=>li(YP,null)}))}}),QM={name:String,value:{type:[String,Number,Boolean],default:"on"},checked:{type:Boolean,default:void 0},defaultChecked:Boolean,disabled:{type:Boolean,default:void 0},label:String,size:String,onUpdateChecked:[Function,Array],"onUpdate:checked":[Function,Array],checkedValue:{type:Boolean,default:void 0}},ZM="n-radio-group";function JM(e){const t=Po(ZM,null),n=eC(e,{mergedSize(n){const{size:o}=e;if(void 0!==o)return o;if(t){const{mergedSizeRef:{value:e}}=t;if(void 0!==e)return e}return n?n.mergedSize.value:"medium"},mergedDisabled:n=>!!e.disabled||!!(null==t?void 0:t.disabledRef.value)||!!(null==n?void 0:n.disabled.value)}),{mergedSizeRef:o,mergedDisabledRef:r}=n,i=Et(null),a=Et(null),l=Et(e.defaultChecked),s=Db(jt(e,"checked"),l),c=mb((()=>t?t.valueRef.value===e.value:s.value)),u=mb((()=>{const{name:n}=e;return void 0!==n?n:t?t.nameRef.value:void 0})),d=Et(!1);function p(){r.value||c.value||function(){if(t){const{doUpdateValue:n}=t,{value:o}=e;dg(n,o)}else{const{onUpdateChecked:t,"onUpdate:checked":o}=e,{nTriggerFormInput:r,nTriggerFormChange:i}=n;t&&dg(t,!0),o&&dg(o,!0),r(),i(),l.value=!0}}()}return{mergedClsPrefix:t?t.mergedClsPrefixRef:B_(e).mergedClsPrefixRef,inputRef:i,labelRef:a,mergedName:u,mergedDisabled:r,renderSafeChecked:c,focus:d,mergedSize:o,handleRadioInputChange:function(){p(),i.value&&(i.value.checked=c.value)},handleRadioInputBlur:function(){d.value=!1},handleRadioInputFocus:function(){d.value=!0}}}const eF=nb("radio","\n line-height: var(--n-label-line-height);\n outline: none;\n position: relative;\n user-select: none;\n -webkit-user-select: none;\n display: inline-flex;\n align-items: flex-start;\n flex-wrap: nowrap;\n font-size: var(--n-font-size);\n word-break: break-word;\n",[rb("checked",[ob("dot","\n background-color: var(--n-color-active);\n ")]),ob("dot-wrapper","\n position: relative;\n flex-shrink: 0;\n flex-grow: 0;\n width: var(--n-radio-size);\n "),nb("radio-input","\n position: absolute;\n border: 0;\n border-radius: inherit;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n opacity: 0;\n z-index: 1;\n cursor: pointer;\n "),ob("dot","\n position: absolute;\n top: 50%;\n left: 0;\n transform: translateY(-50%);\n height: var(--n-radio-size);\n width: var(--n-radio-size);\n background: var(--n-color);\n box-shadow: var(--n-box-shadow);\n border-radius: 50%;\n transition:\n background-color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier);\n ",[eb("&::before",'\n content: "";\n opacity: 0;\n position: absolute;\n left: 4px;\n top: 4px;\n height: calc(100% - 8px);\n width: calc(100% - 8px);\n border-radius: 50%;\n transform: scale(.8);\n background: var(--n-dot-color-active);\n transition: \n opacity .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n transform .3s var(--n-bezier);\n '),rb("checked",{boxShadow:"var(--n-box-shadow-active)"},[eb("&::before","\n opacity: 1;\n transform: scale(1);\n ")])]),ob("label","\n color: var(--n-text-color);\n padding: var(--n-label-padding);\n font-weight: var(--n-label-font-weight);\n display: inline-block;\n transition: color .3s var(--n-bezier);\n "),ib("disabled","\n cursor: pointer;\n ",[eb("&:hover",[ob("dot",{boxShadow:"var(--n-box-shadow-hover)"})]),rb("focus",[eb("&:not(:active)",[ob("dot",{boxShadow:"var(--n-box-shadow-focus)"})])])]),rb("disabled","\n cursor: not-allowed;\n ",[ob("dot",{boxShadow:"var(--n-box-shadow-disabled)",backgroundColor:"var(--n-color-disabled)"},[eb("&::before",{backgroundColor:"var(--n-dot-color-disabled)"}),rb("checked","\n opacity: 1;\n ")]),ob("label",{color:"var(--n-text-color-disabled)"}),nb("radio-input","\n cursor: not-allowed;\n ")])]),tF=zn({name:"Radio",props:Object.assign(Object.assign({},I_.props),QM),setup(e){const t=JM(e),n=I_("Radio","-radio",eF,EM,e,t.mergedClsPrefix),o=ai((()=>{const{mergedSize:{value:e}}=t,{common:{cubicBezierEaseInOut:o},self:{boxShadow:r,boxShadowActive:i,boxShadowDisabled:a,boxShadowFocus:l,boxShadowHover:s,color:c,colorDisabled:u,colorActive:d,textColor:p,textColorDisabled:h,dotColorActive:f,dotColorDisabled:v,labelPadding:m,labelLineHeight:g,labelFontWeight:b,[ub("fontSize",e)]:y,[ub("radioSize",e)]:x}}=n.value;return{"--n-bezier":o,"--n-label-line-height":g,"--n-label-font-weight":b,"--n-box-shadow":r,"--n-box-shadow-active":i,"--n-box-shadow-disabled":a,"--n-box-shadow-focus":l,"--n-box-shadow-hover":s,"--n-color":c,"--n-color-active":d,"--n-color-disabled":u,"--n-dot-color-active":f,"--n-dot-color-disabled":v,"--n-font-size":y,"--n-radio-size":x,"--n-text-color":p,"--n-text-color-disabled":h,"--n-label-padding":m}})),{inlineThemeDisabled:r,mergedClsPrefixRef:i,mergedRtlRef:a}=B_(e),l=GP("Radio",a,i),s=r?KP("radio",ai((()=>t.mergedSize.value[0])),o,e):void 0;return Object.assign(t,{rtlEnabled:l,cssVars:r?void 0:o,themeClass:null==s?void 0:s.themeClass,onRender:null==s?void 0:s.onRender})},render(){const{$slots:e,mergedClsPrefix:t,onRender:n,label:o}=this;return null==n||n(),li("label",{class:[`${t}-radio`,this.themeClass,this.rtlEnabled&&`${t}-radio--rtl`,this.mergedDisabled&&`${t}-radio--disabled`,this.renderSafeChecked&&`${t}-radio--checked`,this.focus&&`${t}-radio--focus`],style:this.cssVars},li("input",{ref:"inputRef",type:"radio",class:`${t}-radio-input`,value:this.value,name:this.mergedName,checked:this.renderSafeChecked,disabled:this.mergedDisabled,onChange:this.handleRadioInputChange,onFocus:this.handleRadioInputFocus,onBlur:this.handleRadioInputBlur}),li("div",{class:`${t}-radio__dot-wrapper`}," ",li("div",{class:[`${t}-radio__dot`,this.renderSafeChecked&&`${t}-radio__dot--checked`]})),wg(e.default,(e=>e||o?li("div",{ref:"labelRef",class:`${t}-radio__label`},e||o):null)))}}),nF=nb("radio-group","\n display: inline-block;\n font-size: var(--n-font-size);\n",[ob("splitor","\n display: inline-block;\n vertical-align: bottom;\n width: 1px;\n transition:\n background-color .3s var(--n-bezier),\n opacity .3s var(--n-bezier);\n background: var(--n-button-border-color);\n ",[rb("checked",{backgroundColor:"var(--n-button-border-color-active)"}),rb("disabled",{opacity:"var(--n-opacity-disabled)"})]),rb("button-group","\n white-space: nowrap;\n height: var(--n-height);\n line-height: var(--n-height);\n ",[nb("radio-button",{height:"var(--n-height)",lineHeight:"var(--n-height)"}),ob("splitor",{height:"var(--n-height)"})]),nb("radio-button","\n vertical-align: bottom;\n outline: none;\n position: relative;\n user-select: none;\n -webkit-user-select: none;\n display: inline-block;\n box-sizing: border-box;\n padding-left: 14px;\n padding-right: 14px;\n white-space: nowrap;\n transition:\n background-color .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n border-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n background: var(--n-button-color);\n color: var(--n-button-text-color);\n border-top: 1px solid var(--n-button-border-color);\n border-bottom: 1px solid var(--n-button-border-color);\n ",[nb("radio-input","\n pointer-events: none;\n position: absolute;\n border: 0;\n border-radius: inherit;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n opacity: 0;\n z-index: 1;\n "),ob("state-border","\n z-index: 1;\n pointer-events: none;\n position: absolute;\n box-shadow: var(--n-button-box-shadow);\n transition: box-shadow .3s var(--n-bezier);\n left: -1px;\n bottom: -1px;\n right: -1px;\n top: -1px;\n "),eb("&:first-child","\n border-top-left-radius: var(--n-button-border-radius);\n border-bottom-left-radius: var(--n-button-border-radius);\n border-left: 1px solid var(--n-button-border-color);\n ",[ob("state-border","\n border-top-left-radius: var(--n-button-border-radius);\n border-bottom-left-radius: var(--n-button-border-radius);\n ")]),eb("&:last-child","\n border-top-right-radius: var(--n-button-border-radius);\n border-bottom-right-radius: var(--n-button-border-radius);\n border-right: 1px solid var(--n-button-border-color);\n ",[ob("state-border","\n border-top-right-radius: var(--n-button-border-radius);\n border-bottom-right-radius: var(--n-button-border-radius);\n ")]),ib("disabled","\n cursor: pointer;\n ",[eb("&:hover",[ob("state-border","\n transition: box-shadow .3s var(--n-bezier);\n box-shadow: var(--n-button-box-shadow-hover);\n "),ib("checked",{color:"var(--n-button-text-color-hover)"})]),rb("focus",[eb("&:not(:active)",[ob("state-border",{boxShadow:"var(--n-button-box-shadow-focus)"})])])]),rb("checked","\n background: var(--n-button-color-active);\n color: var(--n-button-text-color-active);\n border-color: var(--n-button-border-color-active);\n "),rb("disabled","\n cursor: not-allowed;\n opacity: var(--n-opacity-disabled);\n ")])]),oF=zn({name:"RadioGroup",props:Object.assign(Object.assign({},I_.props),{name:String,value:[String,Number,Boolean],defaultValue:{type:[String,Number,Boolean],default:null},size:String,disabled:{type:Boolean,default:void 0},"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array]}),setup(e){const t=Et(null),{mergedSizeRef:n,mergedDisabledRef:o,nTriggerFormChange:r,nTriggerFormInput:i,nTriggerFormBlur:a,nTriggerFormFocus:l}=eC(e),{mergedClsPrefixRef:s,inlineThemeDisabled:c,mergedRtlRef:u}=B_(e),d=I_("Radio","-radio-group",nF,EM,e,s),p=Et(e.defaultValue),h=Db(jt(e,"value"),p);_o(ZM,{mergedClsPrefixRef:s,nameRef:jt(e,"name"),valueRef:h,disabledRef:o,mergedSizeRef:n,doUpdateValue:function(t){const{onUpdateValue:n,"onUpdate:value":o}=e;n&&dg(n,t),o&&dg(o,t),p.value=t,r(),i()}});const f=GP("Radio",u,s),v=ai((()=>{const{value:e}=n,{common:{cubicBezierEaseInOut:t},self:{buttonBorderColor:o,buttonBorderColorActive:r,buttonBorderRadius:i,buttonBoxShadow:a,buttonBoxShadowFocus:l,buttonBoxShadowHover:s,buttonColor:c,buttonColorActive:u,buttonTextColor:p,buttonTextColorActive:h,buttonTextColorHover:f,opacityDisabled:v,[ub("buttonHeight",e)]:m,[ub("fontSize",e)]:g}}=d.value;return{"--n-font-size":g,"--n-bezier":t,"--n-button-border-color":o,"--n-button-border-color-active":r,"--n-button-border-radius":i,"--n-button-box-shadow":a,"--n-button-box-shadow-focus":l,"--n-button-box-shadow-hover":s,"--n-button-color":c,"--n-button-color-active":u,"--n-button-text-color":p,"--n-button-text-color-hover":f,"--n-button-text-color-active":h,"--n-height":m,"--n-opacity-disabled":v}})),m=c?KP("radio-group",ai((()=>n.value[0])),v,e):void 0;return{selfElRef:t,rtlEnabled:f,mergedClsPrefix:s,mergedValue:h,handleFocusout:function(e){const{value:n}=t;n&&(n.contains(e.relatedTarget)||a())},handleFocusin:function(e){const{value:n}=t;n&&(n.contains(e.relatedTarget)||l())},cssVars:c?void 0:v,themeClass:null==m?void 0:m.themeClass,onRender:null==m?void 0:m.onRender}},render(){var e;const{mergedValue:t,mergedClsPrefix:n,handleFocusin:o,handleFocusout:r}=this,{children:i,isButtonGroup:a}=function(e,t,n){var o;const r=[];let i=!1;for(let a=0;at||this.label?li("div",{ref:"labelRef",class:`${e}-radio__label`},t||this.label):null)))}});function iF(e){return"selection"===e.type||"expand"===e.type?void 0===e.width?40:Im(e.width):"children"in e?void 0:"string"==typeof e.width?Im(e.width):e.width}function aF(e){return"selection"===e.type?"__n_selection__":"expand"===e.type?"__n_expand__":e.key}function lF(e){return e&&"object"==typeof e?Object.assign({},e):e}function sF(e,t){if(void 0!==t)return{width:t,minWidth:t,maxWidth:t};const n=function(e){var t,n;return"selection"===e.type?Ag(null!==(t=e.width)&&void 0!==t?t:40):"expand"===e.type?Ag(null!==(n=e.width)&&void 0!==n?n:40):"children"in e?void 0:Ag(e.width)}(e),{minWidth:o,maxWidth:r}=e;return{width:n,minWidth:Ag(o)||n,maxWidth:Ag(r)}}function cF(e){return void 0!==e.filterOptionValues||void 0===e.filterOptionValue&&void 0!==e.defaultFilterOptionValues}function uF(e){return!("children"in e)&&!!e.sorter}function dF(e){return!("children"in e&&e.children.length||!e.resizable)}function pF(e){return!("children"in e)&&!(!e.filter||!e.filterOptions&&!e.renderFilterMenu)}function hF(e){return e?"descend"===e&&"ascend":"descend"}function fF(e,t){return void 0!==t.find((t=>t.columnKey===e.key&&t.order))}const vF=zn({name:"DataTableFilterMenu",props:{column:{type:Object,required:!0},radioGroupName:{type:String,required:!0},multiple:{type:Boolean,required:!0},value:{type:[Array,String,Number],default:null},options:{type:Array,required:!0},onConfirm:{type:Function,required:!0},onClear:{type:Function,required:!0},onChange:{type:Function,required:!0}},setup(e){const{mergedClsPrefixRef:t,mergedRtlRef:n}=B_(e),o=GP("DataTable",n,t),{mergedClsPrefixRef:r,mergedThemeRef:i,localeRef:a}=Po(GM),l=Et(e.value);function s(t){e.onChange(t)}return{mergedClsPrefix:r,rtlEnabled:o,mergedTheme:i,locale:a,checkboxGroupValue:ai((()=>{const{value:e}=l;return Array.isArray(e)?e:null})),radioGroupValue:ai((()=>{const{value:t}=l;return cF(e.column)?Array.isArray(t)&&t.length&&t[0]||null:Array.isArray(t)?null:t})),handleChange:function(t){e.multiple&&Array.isArray(t)?l.value=t:cF(e.column)&&!Array.isArray(t)?l.value=[t]:l.value=t},handleConfirmClick:function(){s(l.value),e.onConfirm()},handleClearClick:function(){e.multiple||cF(e.column)?s([]):s(null),e.onClear()}}},render(){const{mergedTheme:e,locale:t,mergedClsPrefix:n}=this;return li("div",{class:[`${n}-data-table-filter-menu`,this.rtlEnabled&&`${n}-data-table-filter-menu--rtl`]},li(lR,null,{default:()=>{const{checkboxGroupValue:t,handleChange:o}=this;return this.multiple?li(qO,{value:t,class:`${n}-data-table-filter-menu__group`,onUpdateValue:o},{default:()=>this.options.map((t=>li(GO,{key:t.value,theme:e.peers.Checkbox,themeOverrides:e.peerOverrides.Checkbox,value:t.value},{default:()=>t.label})))}):li(oF,{name:this.radioGroupName,class:`${n}-data-table-filter-menu__group`,value:this.radioGroupValue,onUpdateValue:this.handleChange},{default:()=>this.options.map((t=>li(tF,{key:t.value,value:t.value,theme:e.peers.Radio,themeOverrides:e.peerOverrides.Radio},{default:()=>t.label})))})}}),li("div",{class:`${n}-data-table-filter-menu__action`},li(oO,{size:"tiny",theme:e.peers.Button,themeOverrides:e.peerOverrides.Button,onClick:this.handleClearClick},{default:()=>t.clear}),li(oO,{theme:e.peers.Button,themeOverrides:e.peerOverrides.Button,type:"primary",size:"tiny",onClick:this.handleConfirmClick},{default:()=>t.confirm})))}}),mF=zn({name:"DataTableRenderFilter",props:{render:{type:Function,required:!0},active:{type:Boolean,default:!1},show:{type:Boolean,default:!1}},render(){const{render:e,active:t,show:n}=this;return e({active:t,show:n})}}),gF=zn({name:"DataTableFilterButton",props:{column:{type:Object,required:!0},options:{type:Array,default:()=>[]}},setup(e){const{mergedComponentPropsRef:t}=B_(),{mergedThemeRef:n,mergedClsPrefixRef:o,mergedFilterStateRef:r,filterMenuCssVarsRef:i,paginationBehaviorOnFilterRef:a,doUpdatePage:l,doUpdateFilters:s,filterIconPopoverPropsRef:c}=Po(GM),u=Et(!1),d=r,p=ai((()=>!1!==e.column.filterMultiple)),h=ai((()=>{const t=d.value[e.column.key];if(void 0===t){const{value:e}=p;return e?[]:null}return t})),f=ai((()=>{const{value:e}=h;return Array.isArray(e)?e.length>0:null!==e})),v=ai((()=>{var n,o;return(null===(o=null===(n=null==t?void 0:t.value)||void 0===n?void 0:n.DataTable)||void 0===o?void 0:o.renderFilter)||e.column.renderFilter}));return{mergedTheme:n,mergedClsPrefix:o,active:f,showPopover:u,mergedRenderFilter:v,filterIconPopoverProps:c,filterMultiple:p,mergedFilterValue:h,filterMenuCssVars:i,handleFilterChange:function(t){const n=function(e,t,n){const o=Object.assign({},e);return o[t]=n,o}(d.value,e.column.key,t);s(n,e.column),"first"===a.value&&l(1)},handleFilterMenuConfirm:function(){u.value=!1},handleFilterMenuCancel:function(){u.value=!1}}},render(){const{mergedTheme:e,mergedClsPrefix:t,handleFilterMenuCancel:n,filterIconPopoverProps:o}=this;return li($R,Object.assign({show:this.showPopover,onUpdateShow:e=>this.showPopover=e,trigger:"click",theme:e.peers.Popover,themeOverrides:e.peerOverrides.Popover,placement:"bottom"},o,{style:{padding:0}}),{trigger:()=>{const{mergedRenderFilter:e}=this;if(e)return li(mF,{"data-data-table-filter":!0,render:e,active:this.active,show:this.showPopover});const{renderFilterIcon:n}=this.column;return li("div",{"data-data-table-filter":!0,class:[`${t}-data-table-filter`,{[`${t}-data-table-filter--active`]:this.active,[`${t}-data-table-filter--show`]:this.showPopover}]},n?n({active:this.active,show:this.showPopover}):li(CT,{clsPrefix:t},{default:()=>li(sT,null)}))},default:()=>{const{renderFilterMenu:e}=this.column;return e?e({hide:n}):li(vF,{style:this.filterMenuCssVars,radioGroupName:String(this.column.key),multiple:this.filterMultiple,value:this.mergedFilterValue,options:this.options,column:this.column,onChange:this.handleFilterChange,onClear:this.handleFilterMenuCancel,onConfirm:this.handleFilterMenuConfirm})}})}}),bF=zn({name:"ColumnResizeButton",props:{onResizeStart:Function,onResize:Function,onResizeEnd:Function},setup(e){const{mergedClsPrefixRef:t}=Po(GM),n=Et(!1);let o=0;function r(e){return e.clientX}function i(t){var n;null===(n=e.onResize)||void 0===n||n.call(e,r(t)-o)}function a(){var t;n.value=!1,null===(t=e.onResizeEnd)||void 0===t||t.call(e),Tb("mousemove",window,i),Tb("mouseup",window,a)}return Hn((()=>{Tb("mousemove",window,i),Tb("mouseup",window,a)})),{mergedClsPrefix:t,active:n,handleMousedown:function(t){var l;t.preventDefault();const s=n.value;o=r(t),n.value=!0,s||(Pb("mousemove",window,i),Pb("mouseup",window,a),null===(l=e.onResizeStart)||void 0===l||l.call(e))}}},render(){const{mergedClsPrefix:e}=this;return li("span",{"data-data-table-resizable":!0,class:[`${e}-data-table-resize-button`,this.active&&`${e}-data-table-resize-button--active`],onMousedown:this.handleMousedown})}}),yF=zn({name:"DropdownDivider",props:{clsPrefix:{type:String,required:!0}},render(){return li("div",{class:`${this.clsPrefix}-dropdown-divider`})}});function xF(e){const{textColorBase:t,opacity1:n,opacity2:o,opacity3:r,opacity4:i,opacity5:a}=e;return{color:t,opacity1Depth:n,opacity2Depth:o,opacity3Depth:r,opacity4Depth:i,opacity5Depth:a}}const CF={name:"Icon",common:qz,self:xF},wF={name:"Icon",common:tz,self:xF},kF=nb("icon","\n height: 1em;\n width: 1em;\n line-height: 1em;\n text-align: center;\n display: inline-block;\n position: relative;\n fill: currentColor;\n transform: translateZ(0);\n",[rb("color-transition",{transition:"color .3s var(--n-bezier)"}),rb("depth",{color:"var(--n-color)"},[eb("svg",{opacity:"var(--n-opacity)",transition:"opacity .3s var(--n-bezier)"})]),eb("svg",{height:"1em",width:"1em"})]),SF=zn({_n_icon__:!0,name:"Icon",inheritAttrs:!1,props:Object.assign(Object.assign({},I_.props),{depth:[String,Number],size:[Number,String],color:String,component:Object}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Icon","-icon",kF,CF,e,t),r=ai((()=>{const{depth:t}=e,{common:{cubicBezierEaseInOut:n},self:r}=o.value;if(void 0!==t){const{color:e,[`opacity${t}Depth`]:o}=r;return{"--n-bezier":n,"--n-color":e,"--n-opacity":o}}return{"--n-bezier":n,"--n-color":"","--n-opacity":""}})),i=n?KP("icon",ai((()=>`${e.depth||"d"}`)),r,e):void 0;return{mergedClsPrefix:t,mergedStyle:ai((()=>{const{size:t,color:n}=e;return{fontSize:Ag(t),color:n}})),cssVars:n?void 0:r,themeClass:null==i?void 0:i.themeClass,onRender:null==i?void 0:i.onRender}},render(){var e;const{$parent:t,depth:n,mergedClsPrefix:o,component:r,onRender:i,themeClass:a}=this;return null===(e=null==t?void 0:t.$options)||void 0===e||e._n_icon__,null==i||i(),li("i",Wr(this.$attrs,{role:"img",class:[`${o}-icon`,a,{[`${o}-icon--depth`]:n,[`${o}-icon--color-transition`]:void 0!==n}],style:[this.cssVars,this.mergedStyle]}),r?li(r):this.$slots)}}),_F="n-dropdown-menu",PF="n-dropdown",TF="n-dropdown-option";function AF(e,t){return"submenu"===e.type||void 0===e.type&&void 0!==e[t]}function zF(e){return"divider"===e.type}const RF=zn({name:"DropdownOption",props:{clsPrefix:{type:String,required:!0},tmNode:{type:Object,required:!0},parentKey:{type:[String,Number],default:null},placement:{type:String,default:"right-start"},props:Object,scrollable:Boolean},setup(e){const t=Po(PF),{hoverKeyRef:n,keyboardKeyRef:o,lastToggledSubmenuKeyRef:r,pendingKeyPathRef:i,activeKeyPathRef:a,animatedRef:l,mergedShowRef:s,renderLabelRef:c,renderIconRef:u,labelFieldRef:d,childrenFieldRef:p,renderOptionRef:h,nodePropsRef:f,menuPropsRef:v}=t,m=Po(TF,null),g=Po(_F),b=Po(Gb),y=ai((()=>e.tmNode.rawNode)),x=ai((()=>{const{value:t}=p;return AF(e.tmNode.rawNode,t)})),C=ai((()=>{const{disabled:t}=e.tmNode;return t})),w=function(e,t,n){if(!t)return e;const o=Et(e.value);let r=null;return lr(e,(e=>{null!==r&&window.clearTimeout(r),!0===e?n&&!n.value?o.value=!0:r=window.setTimeout((()=>{o.value=!0}),t):o.value=!1})),o}(ai((()=>{if(!x.value)return!1;const{key:t,disabled:a}=e.tmNode;if(a)return!1;const{value:l}=n,{value:s}=o,{value:c}=r,{value:u}=i;return null!==l?u.includes(t):null!==s?u.includes(t)&&u[u.length-1]!==t:null!==c&&u.includes(t)})),300,ai((()=>null===o.value&&!l.value))),k=ai((()=>!!(null==m?void 0:m.enteringSubmenuRef.value))),S=Et(!1);function _(){const{parentKey:t,tmNode:i}=e;i.disabled||s.value&&(r.value=t,o.value=null,n.value=i.key)}return _o(TF,{enteringSubmenuRef:S}),{labelField:d,renderLabel:c,renderIcon:u,siblingHasIcon:g.showIconRef,siblingHasSubmenu:g.hasSubmenuRef,menuProps:v,popoverBody:b,animated:l,mergedShowSubmenu:ai((()=>w.value&&!k.value)),rawNode:y,hasSubmenu:x,pending:mb((()=>{const{value:t}=i,{key:n}=e.tmNode;return t.includes(n)})),childActive:mb((()=>{const{value:t}=a,{key:n}=e.tmNode,o=t.findIndex((e=>n===e));return-1!==o&&o{const{value:t}=a,{key:n}=e.tmNode,o=t.findIndex((e=>n===e));return-1!==o&&o===t.length-1})),mergedDisabled:C,renderOption:h,nodeProps:f,handleClick:function(){const{value:n}=x,{tmNode:o}=e;s.value&&(n||o.disabled||(t.doSelect(o.key,o.rawNode),t.doUpdateShow(!1)))},handleMouseMove:function(){const{tmNode:t}=e;t.disabled||s.value&&n.value!==t.key&&_()},handleMouseEnter:_,handleMouseLeave:function(t){if(e.tmNode.disabled)return;if(!s.value)return;const{relatedTarget:o}=t;!o||Mm({target:o},"dropdownOption")||Mm({target:o},"scrollbarRail")||(n.value=null)},handleSubmenuBeforeEnter:function(){S.value=!0},handleSubmenuAfterEnter:function(){S.value=!1}}},render(){var e,t;const{animated:n,rawNode:o,mergedShowSubmenu:r,clsPrefix:i,siblingHasIcon:a,siblingHasSubmenu:l,renderLabel:s,renderIcon:c,renderOption:u,nodeProps:d,props:p,scrollable:h}=this;let f=null;if(r){const t=null===(e=this.menuProps)||void 0===e?void 0:e.call(this,o,o.children);f=li(FF,Object.assign({},t,{clsPrefix:i,scrollable:this.scrollable,tmNodes:this.tmNode.children,parentKey:this.tmNode.key}))}const v={class:[`${i}-dropdown-option-body`,this.pending&&`${i}-dropdown-option-body--pending`,this.active&&`${i}-dropdown-option-body--active`,this.childActive&&`${i}-dropdown-option-body--child-active`,this.mergedDisabled&&`${i}-dropdown-option-body--disabled`],onMousemove:this.handleMouseMove,onMouseenter:this.handleMouseEnter,onMouseleave:this.handleMouseLeave,onClick:this.handleClick},m=null==d?void 0:d(o),g=li("div",Object.assign({class:[`${i}-dropdown-option`,null==m?void 0:m.class],"data-dropdown-option":!0},m),li("div",Wr(v,p),[li("div",{class:[`${i}-dropdown-option-body__prefix`,a&&`${i}-dropdown-option-body__prefix--show-icon`]},[c?c(o):hg(o.icon)]),li("div",{"data-dropdown-option":!0,class:`${i}-dropdown-option-body__label`},s?s(o):hg(null!==(t=o[this.labelField])&&void 0!==t?t:o.title)),li("div",{"data-dropdown-option":!0,class:[`${i}-dropdown-option-body__suffix`,l&&`${i}-dropdown-option-body__suffix--has-submenu`]},this.hasSubmenu?li(SF,null,{default:()=>li(eT,null)}):null)]),this.hasSubmenu?li(ay,null,{default:()=>[li(ly,null,{default:()=>li("div",{class:`${i}-dropdown-offset-container`},li(Fy,{show:this.mergedShowSubmenu,placement:this.placement,to:h&&this.popoverBody||void 0,teleportDisabled:!h},{default:()=>li("div",{class:`${i}-dropdown-menu-wrapper`},n?li(vi,{onBeforeEnter:this.handleSubmenuBeforeEnter,onAfterEnter:this.handleSubmenuAfterEnter,name:"fade-in-scale-up-transition",appear:!0},{default:()=>f}):f)}))})]}):null);return u?u({node:g,option:o}):g}}),EF=zn({name:"DropdownGroupHeader",props:{clsPrefix:{type:String,required:!0},tmNode:{type:Object,required:!0}},setup(){const{showIconRef:e,hasSubmenuRef:t}=Po(_F),{renderLabelRef:n,labelFieldRef:o,nodePropsRef:r,renderOptionRef:i}=Po(PF);return{labelField:o,showIcon:e,hasSubmenu:t,renderLabel:n,nodeProps:r,renderOption:i}},render(){var e;const{clsPrefix:t,hasSubmenu:n,showIcon:o,nodeProps:r,renderLabel:i,renderOption:a}=this,{rawNode:l}=this.tmNode,s=li("div",Object.assign({class:`${t}-dropdown-option`},null==r?void 0:r(l)),li("div",{class:`${t}-dropdown-option-body ${t}-dropdown-option-body--group`},li("div",{"data-dropdown-option":!0,class:[`${t}-dropdown-option-body__prefix`,o&&`${t}-dropdown-option-body__prefix--show-icon`]},hg(l.icon)),li("div",{class:`${t}-dropdown-option-body__label`,"data-dropdown-option":!0},i?i(l):hg(null!==(e=l.title)&&void 0!==e?e:l[this.labelField])),li("div",{class:[`${t}-dropdown-option-body__suffix`,n&&`${t}-dropdown-option-body__suffix--has-submenu`],"data-dropdown-option":!0})));return a?a({node:s,option:l}):s}}),OF=zn({name:"NDropdownGroup",props:{clsPrefix:{type:String,required:!0},tmNode:{type:Object,required:!0},parentKey:{type:[String,Number],default:null}},render(){const{tmNode:e,parentKey:t,clsPrefix:n}=this,{children:o}=e;return li(yr,null,li(EF,{clsPrefix:n,tmNode:e,key:e.key}),null==o?void 0:o.map((e=>{const{rawNode:o}=e;return!1===o.show?null:zF(o)?li(yF,{clsPrefix:n,key:e.key}):e.isGroup?null:li(RF,{clsPrefix:n,tmNode:e,parentKey:t,key:e.key})})))}}),MF=zn({name:"DropdownRenderOption",props:{tmNode:{type:Object,required:!0}},render(){const{rawNode:{render:e,props:t}}=this.tmNode;return li("div",t,[null==e?void 0:e()])}}),FF=zn({name:"DropdownMenu",props:{scrollable:Boolean,showArrow:Boolean,arrowStyle:[String,Object],clsPrefix:{type:String,required:!0},tmNodes:{type:Array,default:()=>[]},parentKey:{type:[String,Number],default:null}},setup(e){const{renderIconRef:t,childrenFieldRef:n}=Po(PF);_o(_F,{showIconRef:ai((()=>{const n=t.value;return e.tmNodes.some((e=>{var t;if(e.isGroup)return null===(t=e.children)||void 0===t?void 0:t.some((({rawNode:e})=>n?n(e):e.icon));const{rawNode:o}=e;return n?n(o):o.icon}))})),hasSubmenuRef:ai((()=>{const{value:t}=n;return e.tmNodes.some((e=>{var n;if(e.isGroup)return null===(n=e.children)||void 0===n?void 0:n.some((({rawNode:e})=>AF(e,t)));const{rawNode:o}=e;return AF(o,t)}))}))});const o=Et(null);return _o(Ub,null),_o(qb,null),_o(Gb,o),{bodyRef:o}},render(){const{parentKey:e,clsPrefix:t,scrollable:n}=this,o=this.tmNodes.map((o=>{const{rawNode:r}=o;return!1===r.show?null:function(e){return"render"===e.type}(r)?li(MF,{tmNode:o,key:o.key}):zF(r)?li(yF,{clsPrefix:t,key:o.key}):function(e){return"group"===e.type}(r)?li(OF,{clsPrefix:t,tmNode:o,parentKey:e,key:o.key}):li(RF,{clsPrefix:t,tmNode:o,parentKey:e,key:o.key,props:r.props,scrollable:n})}));return li("div",{class:[`${t}-dropdown-menu`,n&&`${t}-dropdown-menu--scrollable`],ref:"bodyRef"},n?li(sR,{contentClass:`${t}-dropdown-menu__content`},{default:()=>o}):o,this.showArrow?FR({clsPrefix:t,arrowStyle:this.arrowStyle,arrowClass:void 0,arrowWrapperClass:void 0,arrowWrapperStyle:void 0}):null)}}),IF=nb("dropdown-menu","\n transform-origin: var(--v-transform-origin);\n background-color: var(--n-color);\n border-radius: var(--n-border-radius);\n box-shadow: var(--n-box-shadow);\n position: relative;\n transition:\n background-color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier);\n",[gR(),nb("dropdown-option","\n position: relative;\n ",[eb("a","\n text-decoration: none;\n color: inherit;\n outline: none;\n ",[eb("&::before",'\n content: "";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ')]),nb("dropdown-option-body","\n display: flex;\n cursor: pointer;\n position: relative;\n height: var(--n-option-height);\n line-height: var(--n-option-height);\n font-size: var(--n-font-size);\n color: var(--n-option-text-color);\n transition: color .3s var(--n-bezier);\n ",[eb("&::before",'\n content: "";\n position: absolute;\n top: 0;\n bottom: 0;\n left: 4px;\n right: 4px;\n transition: background-color .3s var(--n-bezier);\n border-radius: var(--n-border-radius);\n '),ib("disabled",[rb("pending","\n color: var(--n-option-text-color-hover);\n ",[ob("prefix, suffix","\n color: var(--n-option-text-color-hover);\n "),eb("&::before","background-color: var(--n-option-color-hover);")]),rb("active","\n color: var(--n-option-text-color-active);\n ",[ob("prefix, suffix","\n color: var(--n-option-text-color-active);\n "),eb("&::before","background-color: var(--n-option-color-active);")]),rb("child-active","\n color: var(--n-option-text-color-child-active);\n ",[ob("prefix, suffix","\n color: var(--n-option-text-color-child-active);\n ")])]),rb("disabled","\n cursor: not-allowed;\n opacity: var(--n-option-opacity-disabled);\n "),rb("group","\n font-size: calc(var(--n-font-size) - 1px);\n color: var(--n-group-header-text-color);\n ",[ob("prefix","\n width: calc(var(--n-option-prefix-width) / 2);\n ",[rb("show-icon","\n width: calc(var(--n-option-icon-prefix-width) / 2);\n ")])]),ob("prefix","\n width: var(--n-option-prefix-width);\n display: flex;\n justify-content: center;\n align-items: center;\n color: var(--n-prefix-color);\n transition: color .3s var(--n-bezier);\n z-index: 1;\n ",[rb("show-icon","\n width: var(--n-option-icon-prefix-width);\n "),nb("icon","\n font-size: var(--n-option-icon-size);\n ")]),ob("label","\n white-space: nowrap;\n flex: 1;\n z-index: 1;\n "),ob("suffix","\n box-sizing: border-box;\n flex-grow: 0;\n flex-shrink: 0;\n display: flex;\n justify-content: flex-end;\n align-items: center;\n min-width: var(--n-option-suffix-width);\n padding: 0 8px;\n transition: color .3s var(--n-bezier);\n color: var(--n-suffix-color);\n z-index: 1;\n ",[rb("has-submenu","\n width: var(--n-option-icon-suffix-width);\n "),nb("icon","\n font-size: var(--n-option-icon-size);\n ")]),nb("dropdown-menu","pointer-events: all;")]),nb("dropdown-offset-container","\n pointer-events: none;\n position: absolute;\n left: 0;\n right: 0;\n top: -4px;\n bottom: -4px;\n ")]),nb("dropdown-divider","\n transition: background-color .3s var(--n-bezier);\n background-color: var(--n-divider-color);\n height: 1px;\n margin: 4px 0;\n "),nb("dropdown-menu-wrapper","\n transform-origin: var(--v-transform-origin);\n width: fit-content;\n "),eb(">",[nb("scrollbar","\n height: inherit;\n max-height: inherit;\n ")]),ib("scrollable","\n padding: var(--n-padding);\n "),rb("scrollable",[ob("content","\n padding: var(--n-padding);\n ")])]),LF={animated:{type:Boolean,default:!0},keyboard:{type:Boolean,default:!0},size:{type:String,default:"medium"},inverted:Boolean,placement:{type:String,default:"bottom"},onSelect:[Function,Array],options:{type:Array,default:()=>[]},menuProps:Function,showArrow:Boolean,renderLabel:Function,renderIcon:Function,renderOption:Function,nodeProps:Function,labelField:{type:String,default:"label"},keyField:{type:String,default:"key"},childrenField:{type:String,default:"children"},value:[String,Number]},BF=Object.keys(DR),DF=zn({name:"Dropdown",inheritAttrs:!1,props:Object.assign(Object.assign(Object.assign({},DR),LF),I_.props),setup(e){const t=Et(!1),n=Db(jt(e,"show"),t),o=ai((()=>{const{keyField:t,childrenField:n}=e;return JT(e.options,{getKey:e=>e[t],getDisabled:e=>!0===e.disabled,getIgnored:e=>"divider"===e.type||"render"===e.type,getChildren:e=>e[n]})})),r=ai((()=>o.value.treeNodes)),i=Et(null),a=Et(null),l=Et(null),s=ai((()=>{var e,t,n;return null!==(n=null!==(t=null!==(e=i.value)&&void 0!==e?e:a.value)&&void 0!==t?t:l.value)&&void 0!==n?n:null})),c=ai((()=>o.value.getPath(s.value).keyPath)),u=ai((()=>o.value.getPath(e.value).keyPath));!function(e={},t){const n=vt({ctrl:!1,command:!1,win:!1,shift:!1,tab:!1}),{keydown:o,keyup:r}=e,i=e=>{switch(e.key){case"Control":n.ctrl=!0;break;case"Meta":n.command=!0,n.win=!0;break;case"Shift":n.shift=!0;break;case"Tab":n.tab=!0}void 0!==o&&Object.keys(o).forEach((t=>{if(t!==e.key)return;const n=o[t];if("function"==typeof n)n(e);else{const{stop:t=!1,prevent:o=!1}=n;t&&e.stopPropagation(),o&&e.preventDefault(),n.handler(e)}}))},a=e=>{switch(e.key){case"Control":n.ctrl=!1;break;case"Meta":n.command=!1,n.win=!1;break;case"Shift":n.shift=!1;break;case"Tab":n.tab=!1}void 0!==r&&Object.keys(r).forEach((t=>{if(t!==e.key)return;const n=r[t];if("function"==typeof n)n(e);else{const{stop:t=!1,prevent:o=!1}=n;t&&e.stopPropagation(),o&&e.preventDefault(),n.handler(e)}}))},l=()=>{(void 0===t||t.value)&&(Pb("keydown",document,i),Pb("keyup",document,a)),void 0!==t&&lr(t,(e=>{e?(Pb("keydown",document,i),Pb("keyup",document,a)):(Tb("keydown",document,i),Tb("keyup",document,a))}))};gb()?(Dn(l),Hn((()=>{(void 0===t||t.value)&&(Tb("keydown",document,i),Tb("keyup",document,a))}))):l(),gt(n)}({keydown:{ArrowUp:{prevent:!0,handler:function(){b("up")}},ArrowRight:{prevent:!0,handler:function(){b("right")}},ArrowDown:{prevent:!0,handler:function(){b("down")}},ArrowLeft:{prevent:!0,handler:function(){b("left")}},Enter:{prevent:!0,handler:function(){const e=g();(null==e?void 0:e.isLeaf)&&n.value&&(f(e.key,e.rawNode),v(!1))}},Escape:function(){v(!1)}}},mb((()=>e.keyboard&&n.value)));const{mergedClsPrefixRef:d,inlineThemeDisabled:p}=B_(e),h=I_("Dropdown","-dropdown",IF,FM,e,d);function f(t,n){const{onSelect:o}=e;o&&dg(o,t,n)}function v(n){const{"onUpdate:show":o,onUpdateShow:r}=e;o&&dg(o,n),r&&dg(r,n),t.value=n}function m(){i.value=null,a.value=null,l.value=null}function g(){var e;const{value:t}=o,{value:n}=s;return t&&null!==n&&null!==(e=t.getNode(n))&&void 0!==e?e:null}function b(e){const{value:t}=s,{value:{getFirstAvailableNode:n}}=o;let r=null;if(null===t){const e=n();null!==e&&(r=e.key)}else{const t=g();if(t){let n;switch(e){case"down":n=t.getNext();break;case"up":n=t.getPrev();break;case"right":n=t.getChild();break;case"left":n=t.getParent()}n&&(r=n.key)}}null!==r&&(i.value=null,a.value=r)}_o(PF,{labelFieldRef:jt(e,"labelField"),childrenFieldRef:jt(e,"childrenField"),renderLabelRef:jt(e,"renderLabel"),renderIconRef:jt(e,"renderIcon"),hoverKeyRef:i,keyboardKeyRef:a,lastToggledSubmenuKeyRef:l,pendingKeyPathRef:c,activeKeyPathRef:u,animatedRef:jt(e,"animated"),mergedShowRef:n,nodePropsRef:jt(e,"nodeProps"),renderOptionRef:jt(e,"renderOption"),menuPropsRef:jt(e,"menuProps"),doSelect:f,doUpdateShow:v}),lr(n,(t=>{e.animated||t||m()}));const y=ai((()=>{const{size:t,inverted:n}=e,{common:{cubicBezierEaseInOut:o},self:r}=h.value,{padding:i,dividerColor:a,borderRadius:l,optionOpacityDisabled:s,[ub("optionIconSuffixWidth",t)]:c,[ub("optionSuffixWidth",t)]:u,[ub("optionIconPrefixWidth",t)]:d,[ub("optionPrefixWidth",t)]:p,[ub("fontSize",t)]:f,[ub("optionHeight",t)]:v,[ub("optionIconSize",t)]:m}=r,g={"--n-bezier":o,"--n-font-size":f,"--n-padding":i,"--n-border-radius":l,"--n-option-height":v,"--n-option-prefix-width":p,"--n-option-icon-prefix-width":d,"--n-option-suffix-width":u,"--n-option-icon-suffix-width":c,"--n-option-icon-size":m,"--n-divider-color":a,"--n-option-opacity-disabled":s};return n?(g["--n-color"]=r.colorInverted,g["--n-option-color-hover"]=r.optionColorHoverInverted,g["--n-option-color-active"]=r.optionColorActiveInverted,g["--n-option-text-color"]=r.optionTextColorInverted,g["--n-option-text-color-hover"]=r.optionTextColorHoverInverted,g["--n-option-text-color-active"]=r.optionTextColorActiveInverted,g["--n-option-text-color-child-active"]=r.optionTextColorChildActiveInverted,g["--n-prefix-color"]=r.prefixColorInverted,g["--n-suffix-color"]=r.suffixColorInverted,g["--n-group-header-text-color"]=r.groupHeaderTextColorInverted):(g["--n-color"]=r.color,g["--n-option-color-hover"]=r.optionColorHover,g["--n-option-color-active"]=r.optionColorActive,g["--n-option-text-color"]=r.optionTextColor,g["--n-option-text-color-hover"]=r.optionTextColorHover,g["--n-option-text-color-active"]=r.optionTextColorActive,g["--n-option-text-color-child-active"]=r.optionTextColorChildActive,g["--n-prefix-color"]=r.prefixColor,g["--n-suffix-color"]=r.suffixColor,g["--n-group-header-text-color"]=r.groupHeaderTextColor),g})),x=p?KP("dropdown",ai((()=>`${e.size[0]}${e.inverted?"i":""}`)),y,e):void 0;return{mergedClsPrefix:d,mergedTheme:h,tmNodes:r,mergedShow:n,handleAfterLeave:()=>{e.animated&&m()},doUpdateShow:v,cssVars:p?void 0:y,themeClass:null==x?void 0:x.themeClass,onRender:null==x?void 0:x.onRender}},render(){const{mergedTheme:e}=this,t={show:this.mergedShow,theme:e.peers.Popover,themeOverrides:e.peerOverrides.Popover,internalOnAfterLeave:this.handleAfterLeave,internalRenderBody:(e,t,n,o,r)=>{var i;const{mergedClsPrefix:a,menuProps:l}=this;null===(i=this.onRender)||void 0===i||i.call(this);const s=(null==l?void 0:l(void 0,this.tmNodes.map((e=>e.rawNode))))||{},c={ref:bg(t),class:[e,`${a}-dropdown`,this.themeClass],clsPrefix:a,tmNodes:this.tmNodes,style:[...n,this.cssVars],showArrow:this.showArrow,arrowStyle:this.arrowStyle,scrollable:this.scrollable,onMouseenter:o,onMouseleave:r};return li(FF,Wr(this.$attrs,c,s))},onUpdateShow:this.doUpdateShow,"onUpdate:show":void 0};return li($R,Object.assign({},sg(this.$props,BF),t),{trigger:()=>{var e,t;return null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)}})}}),$F="_n_all__",NF="_n_none__",jF=zn({name:"DataTableSelectionMenu",props:{clsPrefix:{type:String,required:!0}},setup(e){const{props:t,localeRef:n,checkOptionsRef:o,rawPaginatedDataRef:r,doCheckAll:i,doUncheckAll:a}=Po(GM),l=ai((()=>function(e,t,n,o){return e?r=>{for(const i of e)switch(r){case $F:return void n(!0);case NF:return void o(!0);default:if("object"==typeof i&&i.key===r)return void i.onSelect(t.value)}}:()=>{}}(o.value,r,i,a))),s=ai((()=>function(e,t){return e?e.map((e=>{switch(e){case"all":return{label:t.checkTableAll,key:$F};case"none":return{label:t.uncheckTableAll,key:NF};default:return e}})):[]}(o.value,n.value)));return()=>{var n,o,r,i;const{clsPrefix:a}=e;return li(DF,{theme:null===(o=null===(n=t.theme)||void 0===n?void 0:n.peers)||void 0===o?void 0:o.Dropdown,themeOverrides:null===(i=null===(r=t.themeOverrides)||void 0===r?void 0:r.peers)||void 0===i?void 0:i.Dropdown,options:s.value,onSelect:l.value},{default:()=>li(CT,{clsPrefix:a,class:`${a}-data-table-check-extra`},{default:()=>li(vT,null)})})}}});function HF(e){return"function"==typeof e.title?e.title(e):e.title}const WF=zn({name:"DataTableHeader",props:{discrete:{type:Boolean,default:!0}},setup(){const{mergedClsPrefixRef:e,scrollXRef:t,fixedColumnLeftMapRef:n,fixedColumnRightMapRef:o,mergedCurrentPageRef:r,allRowsCheckedRef:i,someRowsCheckedRef:a,rowsRef:l,colsRef:s,mergedThemeRef:c,checkOptionsRef:u,mergedSortStateRef:d,componentId:p,mergedTableLayoutRef:h,headerCheckboxDisabledRef:f,onUnstableColumnResize:v,doUpdateResizableWidth:m,handleTableHeaderScroll:g,deriveNextSorter:b,doUncheckAll:y,doCheckAll:x}=Po(GM),C=Et({});function w(e){const t=C.value[e];return null==t?void 0:t.getBoundingClientRect().width}const k=new Map;return{cellElsRef:C,componentId:p,mergedSortState:d,mergedClsPrefix:e,scrollX:t,fixedColumnLeftMap:n,fixedColumnRightMap:o,currentPage:r,allRowsChecked:i,someRowsChecked:a,rows:l,cols:s,mergedTheme:c,checkOptions:u,mergedTableLayout:h,headerCheckboxDisabled:f,handleCheckboxUpdateChecked:function(){i.value?y():x()},handleColHeaderClick:function(e,t){if(Mm(e,"dataTableFilter")||Mm(e,"dataTableResizable"))return;if(!uF(t))return;const n=d.value.find((e=>e.columnKey===t.key))||null,o=function(e,t){return void 0===e.sorter?null:null===t||t.columnKey!==e.key?{columnKey:e.key,sorter:e.sorter,order:hF(!1)}:Object.assign(Object.assign({},t),{order:hF(t.order)})}(t,n);b(o)},handleTableHeaderScroll:g,handleColumnResizeStart:function(e){k.set(e.key,w(e.key))},handleColumnResize:function(e,t){const n=k.get(e.key);if(void 0===n)return;const o=n+t,r=(i=o,a=e.minWidth,void 0!==(l=e.maxWidth)&&(i=Math.min(i,"number"==typeof l?l:Number.parseFloat(l))),void 0!==a&&(i=Math.max(i,"number"==typeof a?a:Number.parseFloat(a))),i);var i,a,l;v(o,r,e,w),m(e,r)}}},render(){const{cellElsRef:e,mergedClsPrefix:t,fixedColumnLeftMap:n,fixedColumnRightMap:o,currentPage:r,allRowsChecked:i,someRowsChecked:a,rows:l,cols:s,mergedTheme:c,checkOptions:u,componentId:d,discrete:p,mergedTableLayout:h,headerCheckboxDisabled:f,mergedSortState:v,handleColHeaderClick:m,handleCheckboxUpdateChecked:g,handleColumnResizeStart:b,handleColumnResize:y}=this,x=li("thead",{class:`${t}-data-table-thead`,"data-n-id":d},l.map((l=>li("tr",{class:`${t}-data-table-tr`},l.map((({column:l,colSpan:s,rowSpan:d,isLast:p})=>{var h,x;const C=aF(l),{ellipsis:w}=l,k=C in n,S=C in o;return li("th",{ref:t=>e[C]=t,key:C,style:{textAlign:l.titleAlign||l.align,left:Lm(null===(h=n[C])||void 0===h?void 0:h.start),right:Lm(null===(x=o[C])||void 0===x?void 0:x.start)},colspan:s,rowspan:d,"data-col-key":C,class:[`${t}-data-table-th`,(k||S)&&`${t}-data-table-th--fixed-${k?"left":"right"}`,{[`${t}-data-table-th--sorting`]:fF(l,v),[`${t}-data-table-th--filterable`]:pF(l),[`${t}-data-table-th--sortable`]:uF(l),[`${t}-data-table-th--selection`]:"selection"===l.type,[`${t}-data-table-th--last`]:p},l.className],onClick:"selection"===l.type||"expand"===l.type||"children"in l?void 0:e=>{m(e,l)}},"selection"===l.type?!1!==l.multiple?li(yr,null,li(GO,{key:r,privateInsideTable:!0,checked:i,indeterminate:a,disabled:f,onUpdateChecked:g}),u?li(jF,{clsPrefix:t}):null):null:li(yr,null,li("div",{class:`${t}-data-table-th__title-wrapper`},li("div",{class:`${t}-data-table-th__title`},!0===w||w&&!w.tooltip?li("div",{class:`${t}-data-table-th__ellipsis`},HF(l)):w&&"object"==typeof w?li(VM,Object.assign({},w,{theme:c.peers.Ellipsis,themeOverrides:c.peerOverrides.Ellipsis}),{default:()=>HF(l)}):HF(l)),uF(l)?li(YM,{column:l}):null),pF(l)?li(gF,{column:l,options:l.filterOptions}):null,dF(l)?li(bF,{onResizeStart:()=>{b(l)},onResize:e=>{y(l,e)}}):null))}))))));if(!p)return x;const{handleTableHeaderScroll:C,scrollX:w}=this;return li("div",{class:`${t}-data-table-base-table-header`,onScroll:C},li("table",{ref:"body",class:`${t}-data-table-table`,style:{minWidth:Ag(w),tableLayout:h}},li("colgroup",null,s.map((e=>li("col",{key:e.key,style:e.style})))),x))}}),UF=zn({name:"DataTableCell",props:{clsPrefix:{type:String,required:!0},row:{type:Object,required:!0},index:{type:Number,required:!0},column:{type:Object,required:!0},isSummary:Boolean,mergedTheme:{type:Object,required:!0},renderCell:Function},render(){var e;const{isSummary:t,column:n,row:o,renderCell:r}=this;let i;const{render:a,key:l,ellipsis:s}=n;if(i=a&&!t?a(o,this.index):t?null===(e=o[l])||void 0===e?void 0:e.value:r?r(dk(o,l),o,n):dk(o,l),s){if("object"==typeof s){const{mergedTheme:e}=this;return"performant-ellipsis"===n.ellipsisComponent?li(qM,Object.assign({},s,{theme:e.peers.Ellipsis,themeOverrides:e.peerOverrides.Ellipsis}),{default:()=>i}):li(VM,Object.assign({},s,{theme:e.peers.Ellipsis,themeOverrides:e.peerOverrides.Ellipsis}),{default:()=>i})}return li("span",{class:`${this.clsPrefix}-data-table-td__ellipsis`},i)}return i}}),VF=zn({name:"DataTableExpandTrigger",props:{clsPrefix:{type:String,required:!0},expanded:Boolean,loading:Boolean,onClick:{type:Function,required:!0},renderExpandIcon:{type:Function}},render(){const{clsPrefix:e}=this;return li("div",{class:[`${e}-data-table-expand-trigger`,this.expanded&&`${e}-data-table-expand-trigger--expanded`],onClick:this.onClick,onMousedown:e=>{e.preventDefault()}},li(bT,null,{default:()=>this.loading?li(RT,{key:"loading",clsPrefix:this.clsPrefix,radius:85,strokeWidth:15,scale:.88}):this.renderExpandIcon?this.renderExpandIcon({expanded:this.expanded}):li(CT,{clsPrefix:e,key:"base-icon"},{default:()=>li(eT,null)})}))}}),qF=zn({name:"DataTableBodyCheckbox",props:{rowKey:{type:[String,Number],required:!0},disabled:{type:Boolean,required:!0},onUpdateChecked:{type:Function,required:!0}},setup(e){const{mergedCheckedRowKeySetRef:t,mergedInderminateRowKeySetRef:n}=Po(GM);return()=>{const{rowKey:o}=e;return li(GO,{privateInsideTable:!0,disabled:e.disabled,indeterminate:n.value.has(o),checked:t.value.has(o),onUpdateChecked:e.onUpdateChecked})}}}),KF=zn({name:"DataTableBodyRadio",props:{rowKey:{type:[String,Number],required:!0},disabled:{type:Boolean,required:!0},onUpdateChecked:{type:Function,required:!0}},setup(e){const{mergedCheckedRowKeySetRef:t,componentId:n}=Po(GM);return()=>{const{rowKey:o}=e;return li(tF,{name:n,disabled:e.disabled,checked:t.value.has(o),onUpdateChecked:e.onUpdateChecked})}}});function GF(e,t){const n=[];function o(e,r){e.forEach((e=>{e.children&&t.has(e.key)?(n.push({tmNode:e,striped:!1,key:e.key,index:r}),o(e.children,r)):n.push({key:e.key,tmNode:e,striped:!1,index:r})}))}return e.forEach((e=>{n.push(e);const{children:r}=e.tmNode;r&&t.has(e.key)&&o(r,e.index)})),n}const XF=zn({props:{clsPrefix:{type:String,required:!0},id:{type:String,required:!0},cols:{type:Array,required:!0},onMouseenter:Function,onMouseleave:Function},render(){const{clsPrefix:e,id:t,cols:n,onMouseenter:o,onMouseleave:r}=this;return li("table",{style:{tableLayout:"fixed"},class:`${e}-data-table-table`,onMouseenter:o,onMouseleave:r},li("colgroup",null,n.map((e=>li("col",{key:e.key,style:e.style})))),li("tbody",{"data-n-id":t,class:`${e}-data-table-tbody`},this.$slots))}}),YF=zn({name:"DataTableBody",props:{onResize:Function,showHeader:Boolean,flexHeight:Boolean,bodyStyle:Object},setup(e){const{slots:t,bodyWidthRef:n,mergedExpandedRowKeysRef:o,mergedClsPrefixRef:r,mergedThemeRef:i,scrollXRef:a,colsRef:l,paginatedDataRef:s,rawPaginatedDataRef:c,fixedColumnLeftMapRef:u,fixedColumnRightMapRef:d,mergedCurrentPageRef:p,rowClassNameRef:h,leftActiveFixedColKeyRef:f,leftActiveFixedChildrenColKeysRef:v,rightActiveFixedColKeyRef:m,rightActiveFixedChildrenColKeysRef:g,renderExpandRef:b,hoverKeyRef:y,summaryRef:x,mergedSortStateRef:C,virtualScrollRef:w,componentId:k,mergedTableLayoutRef:S,childTriggerColIndexRef:_,indentRef:P,rowPropsRef:T,maxHeightRef:A,stripedRef:z,loadingRef:R,onLoadRef:E,loadingKeySetRef:O,expandableRef:M,stickyExpandedRowsRef:F,renderExpandIconRef:I,summaryPlacementRef:L,treeMateRef:B,scrollbarPropsRef:D,setHeaderScrollLeft:$,doUpdateExpandedRowKeys:N,handleTableBodyScroll:j,doCheck:H,doUncheck:W,renderCell:U}=Po(GM),V=Et(null),q=Et(null),K=Et(null),G=mb((()=>0===s.value.length)),X=mb((()=>e.showHeader||!G.value)),Y=mb((()=>e.showHeader||G.value));let Q="";const Z=ai((()=>new Set(o.value)));function J(e){var t;return null===(t=B.value.getNode(e))||void 0===t?void 0:t.rawNode}function ee(){const{value:e}=q;return(null==e?void 0:e.listElRef)||null}const te={getScrollContainer:function(){if(!X.value){const{value:e}=K;return e||null}if(w.value)return ee();const{value:e}=V;return e?e.containerRef:null},scrollTo(e,t){var n,o;w.value?null===(n=q.value)||void 0===n||n.scrollTo(e,t):null===(o=V.value)||void 0===o||o.scrollTo(e,t)}},ne=eb([({props:e})=>{const t=t=>null===t?null:eb(`[data-n-id="${e.componentId}"] [data-col-key="${t}"]::after`,{boxShadow:"var(--n-box-shadow-after)"}),n=t=>null===t?null:eb(`[data-n-id="${e.componentId}"] [data-col-key="${t}"]::before`,{boxShadow:"var(--n-box-shadow-before)"});return eb([t(e.leftActiveFixedColKey),n(e.rightActiveFixedColKey),e.leftActiveFixedChildrenColKeys.map((e=>t(e))),e.rightActiveFixedChildrenColKeys.map((e=>n(e)))])}]);let oe=!1;return ir((()=>{const{value:e}=f,{value:t}=v,{value:n}=m,{value:o}=g;if(!oe&&null===e&&null===n)return;const r={leftActiveFixedColKey:e,leftActiveFixedChildrenColKeys:t,rightActiveFixedColKey:n,rightActiveFixedChildrenColKeys:o,componentId:k};ne.mount({id:`n-${k}`,force:!0,props:r,anchorMetaName:F_}),oe=!0})),Wn((()=>{ne.unmount({id:`n-${k}`})})),Object.assign({bodyWidth:n,summaryPlacement:L,dataTableSlots:t,componentId:k,scrollbarInstRef:V,virtualListRef:q,emptyElRef:K,summary:x,mergedClsPrefix:r,mergedTheme:i,scrollX:a,cols:l,loading:R,bodyShowHeaderOnly:Y,shouldDisplaySomeTablePart:X,empty:G,paginatedDataAndInfo:ai((()=>{const{value:e}=z;let t=!1;return{data:s.value.map(e?(e,n)=>(e.isLeaf||(t=!0),{tmNode:e,key:e.key,striped:n%2==1,index:n}):(e,n)=>(e.isLeaf||(t=!0),{tmNode:e,key:e.key,striped:!1,index:n})),hasChildren:t}})),rawPaginatedData:c,fixedColumnLeftMap:u,fixedColumnRightMap:d,currentPage:p,rowClassName:h,renderExpand:b,mergedExpandedRowKeySet:Z,hoverKey:y,mergedSortState:C,virtualScroll:w,mergedTableLayout:S,childTriggerColIndex:_,indent:P,rowProps:T,maxHeight:A,loadingKeySet:O,expandable:M,stickyExpandedRows:F,renderExpandIcon:I,scrollbarProps:D,setHeaderScrollLeft:$,handleVirtualListScroll:function(e){var t;j(e),null===(t=V.value)||void 0===t||t.sync()},handleVirtualListResize:function(t){var n;const{onResize:o}=e;o&&o(t),null===(n=V.value)||void 0===n||n.sync()},handleMouseleaveTable:function(){y.value=null},virtualListContainer:ee,virtualListContent:function(){const{value:e}=q;return(null==e?void 0:e.itemsElRef)||null},handleTableBodyScroll:j,handleCheckboxUpdateChecked:function(e,t,n){const o=J(e.key);if(o){if(n){const n=s.value.findIndex((e=>e.key===Q));if(-1!==n){const r=s.value.findIndex((t=>t.key===e.key)),i=Math.min(n,r),a=Math.max(n,r),l=[];return s.value.slice(i,a+1).forEach((e=>{e.disabled||l.push(e.key)})),t?H(l,!1,o):W(l,o),void(Q=e.key)}}t?H(e.key,!1,o):W(e.key,o),Q=e.key}else e.key},handleRadioUpdateChecked:function(e){const t=J(e.key);t?H(e.key,!0,t):e.key},handleUpdateExpanded:function(e,t){var n;if(O.value.has(e))return;const{value:r}=o,i=r.indexOf(e),a=Array.from(r);~i?(a.splice(i,1),N(a)):!t||t.isLeaf||t.shallowLoaded?(a.push(e),N(a)):(O.value.add(e),null===(n=E.value)||void 0===n||n.call(E,t.rawNode).then((()=>{const{value:t}=o,n=Array.from(t);~n.indexOf(e)||n.push(e),N(n)})).finally((()=>{O.value.delete(e)})))},renderCell:U},te)},render(){const{mergedTheme:e,scrollX:t,mergedClsPrefix:n,virtualScroll:o,maxHeight:r,mergedTableLayout:i,flexHeight:a,loadingKeySet:l,onResize:s,setHeaderScrollLeft:c}=this,u=void 0!==t||void 0!==r||a,d=!u&&"auto"===i,p=void 0!==t||d,h={minWidth:Ag(t)||"100%"};t&&(h.width="100%");const f=li(lR,Object.assign({},this.scrollbarProps,{ref:"scrollbarInstRef",scrollable:u||d,class:`${n}-data-table-base-table-body`,style:this.empty?void 0:this.bodyStyle,theme:e.peers.Scrollbar,themeOverrides:e.peerOverrides.Scrollbar,contentStyle:h,container:o?this.virtualListContainer:void 0,content:o?this.virtualListContent:void 0,horizontalRailStyle:{zIndex:3},verticalRailStyle:{zIndex:3},xScrollable:p,onScroll:o?void 0:this.handleTableBodyScroll,internalOnUpdateScrollLeft:c,onResize:s}),{default:()=>{const e={},t={},{cols:r,paginatedDataAndInfo:i,mergedTheme:a,fixedColumnLeftMap:s,fixedColumnRightMap:c,currentPage:u,rowClassName:d,mergedSortState:p,mergedExpandedRowKeySet:f,stickyExpandedRows:v,componentId:m,childTriggerColIndex:g,expandable:b,rowProps:y,handleMouseleaveTable:x,renderExpand:C,summary:w,handleCheckboxUpdateChecked:k,handleRadioUpdateChecked:S,handleUpdateExpanded:_}=this,{length:P}=r;let T;const{data:A,hasChildren:z}=i,R=z?GF(A,f):A;if(w){const e=w(this.rawPaginatedData);if(Array.isArray(e)){const t=e.map(((e,t)=>({isSummaryRow:!0,key:`__n_summary__${t}`,tmNode:{rawNode:e,disabled:!0},index:-1})));T="top"===this.summaryPlacement?[...t,...R]:[...R,...t]}else{const t={isSummaryRow:!0,key:"__n_summary__",tmNode:{rawNode:e,disabled:!0},index:-1};T="top"===this.summaryPlacement?[t,...R]:[...R,t]}}else T=R;const E=z?{width:Lm(this.indent)}:void 0,O=[];T.forEach((e=>{C&&f.has(e.key)&&(!b||b(e.tmNode.rawNode))?O.push(e,{isExpandedRow:!0,key:`${e.key}-expand`,tmNode:e.tmNode,index:e.index}):O.push(e)}));const{length:M}=O,F={};A.forEach((({tmNode:e},t)=>{F[t]=e.key}));const I=v?this.bodyWidth:null,L=null===I?void 0:`${I}px`,B=(o,i,h)=>{const{index:m}=o;if("isExpandedRow"in o){const{tmNode:{key:e,rawNode:t}}=o;return li("tr",{class:`${n}-data-table-tr ${n}-data-table-tr--expanded`,key:`${e}__expand`},li("td",{class:[`${n}-data-table-td`,`${n}-data-table-td--last-col`,i+1===M&&`${n}-data-table-td--last-row`],colspan:P},v?li("div",{class:`${n}-data-table-expand`,style:{width:L}},C(t,m)):C(t,m)))}const b="isSummaryRow"in o,x=!b&&o.striped,{tmNode:w,key:T}=o,{rawNode:A}=w,R=f.has(T),O=y?y(A,m):void 0,I="string"==typeof d?d:function(e,t,n){return"function"==typeof n?n(e,t):n||""}(A,m,d),B=li("tr",Object.assign({onMouseenter:()=>{this.hoverKey=T},key:T,class:[`${n}-data-table-tr`,b&&`${n}-data-table-tr--summary`,x&&`${n}-data-table-tr--striped`,R&&`${n}-data-table-tr--expanded`,I]},O),r.map(((r,d)=>{var f,v,y,x,C;if(i in e){const t=e[i],n=t.indexOf(d);if(~n)return t.splice(n,1),null}const{column:w}=r,O=aF(r),{rowSpan:I,colSpan:L}=w,B=b?(null===(f=o.tmNode.rawNode[O])||void 0===f?void 0:f.colSpan)||1:L?L(A,m):1,D=b?(null===(v=o.tmNode.rawNode[O])||void 0===v?void 0:v.rowSpan)||1:I?I(A,m):1,$=d+B===P,N=i+D===M,j=D>1;if(j&&(t[i]={[d]:[]}),B>1||j)for(let n=i;n{_(T,o.tmNode)}})]:null,"selection"===w.type?b?null:!1===w.multiple?li(KF,{key:u,rowKey:T,disabled:o.tmNode.disabled,onUpdateChecked:()=>{S(o.tmNode)}}):li(qF,{key:u,rowKey:T,disabled:o.tmNode.disabled,onUpdateChecked:(e,t)=>{k(o.tmNode,e,t.shiftKey)}}):"expand"===w.type?b?null:!w.expandable||(null===(C=w.expandable)||void 0===C?void 0:C.call(w,A))?li(VF,{clsPrefix:n,expanded:R,renderExpandIcon:this.renderExpandIcon,onClick:()=>{_(T,null)}}):null:li(UF,{clsPrefix:n,index:m,row:A,column:w,isSummary:b,mergedTheme:a,renderCell:this.renderCell}))})));return B};return o?li(Ax,{ref:"virtualListRef",items:O,itemSize:28,visibleItemsTag:XF,visibleItemsProps:{clsPrefix:n,id:m,cols:r,onMouseleave:x},showScrollbar:!1,onResize:this.handleVirtualListResize,onScroll:this.handleVirtualListScroll,itemsStyle:h,itemResizable:!0},{default:({item:e,index:t})=>B(e,t,!0)}):li("table",{class:`${n}-data-table-table`,onMouseleave:x,style:{tableLayout:this.mergedTableLayout}},li("colgroup",null,r.map((e=>li("col",{key:e.key,style:e.style})))),this.showHeader?li(WF,{discrete:!1}):null,this.empty?null:li("tbody",{"data-n-id":m,class:`${n}-data-table-tbody`},O.map(((e,t)=>B(e,t,!1)))))}});if(this.empty){const e=()=>li("div",{class:[`${n}-data-table-empty`,this.loading&&`${n}-data-table-empty--hide`],style:this.bodyStyle,ref:"emptyElRef"},xg(this.dataTableSlots.empty,(()=>[li(Zz,{theme:this.mergedTheme.peers.Empty,themeOverrides:this.mergedTheme.peerOverrides.Empty})])));return this.shouldDisplaySomeTablePart?li(yr,null,f,e()):li(kx,{onResize:this.onResize},{default:e})}return f}}),QF=zn({name:"MainTable",setup(){const{mergedClsPrefixRef:e,rightFixedColumnsRef:t,leftFixedColumnsRef:n,bodyWidthRef:o,maxHeightRef:r,minHeightRef:i,flexHeightRef:a,syncScrollState:l}=Po(GM),s=Et(null),c=Et(null),u=Et(null),d=Et(!(n.value.length||t.value.length)),p=ai((()=>({maxHeight:Ag(r.value),minHeight:Ag(i.value)}))),h={getBodyElement:function(){const{value:e}=c;return e?e.getScrollContainer():null},getHeaderElement:function(){const{value:e}=s;return e?e.$el:null},scrollTo(e,t){var n;null===(n=c.value)||void 0===n||n.scrollTo(e,t)}};return ir((()=>{const{value:t}=u;if(!t)return;const n=`${e.value}-data-table-base-table--transition-disabled`;d.value?setTimeout((()=>{t.classList.remove(n)}),0):t.classList.add(n)})),Object.assign({maxHeight:r,mergedClsPrefix:e,selfElRef:u,headerInstRef:s,bodyInstRef:c,bodyStyle:p,flexHeight:a,handleBodyResize:function(e){o.value=e.contentRect.width,l(),d.value||(d.value=!0)}},h)},render(){const{mergedClsPrefix:e,maxHeight:t,flexHeight:n}=this,o=void 0===t&&!n;return li("div",{class:`${e}-data-table-base-table`,ref:"selfElRef"},o?null:li(WF,{ref:"headerInstRef"}),li(YF,{ref:"bodyInstRef",bodyStyle:this.bodyStyle,showHeader:o,flexHeight:n,onResize:this.handleBodyResize}))}});function ZF(e){return"object"==typeof e&&"number"==typeof e.multiple&&e.multiple}function JF(e,{dataRelatedColsRef:t,filteredDataRef:n}){const o=[];t.value.forEach((e=>{var t;void 0!==e.sorter&&c(o,{columnKey:e.key,sorter:e.sorter,order:null!==(t=e.defaultSortOrder)&&void 0!==t&&t})}));const r=Et(o),i=ai((()=>{const e=t.value.filter((e=>"selection"!==e.type&&void 0!==e.sorter&&("ascend"===e.sortOrder||"descend"===e.sortOrder||!1===e.sortOrder))),n=e.filter((e=>!1!==e.sortOrder));if(n.length)return n.map((e=>({columnKey:e.key,order:e.sortOrder,sorter:e.sorter})));if(e.length)return[];const{value:o}=r;return Array.isArray(o)?o:o?[o]:[]}));function a(e){const t=function(e){let t=i.value.slice();return e&&!1!==ZF(e.sorter)?(t=t.filter((e=>!1!==ZF(e.sorter))),c(t,e),t):e||null}(e);l(t)}function l(t){const{"onUpdate:sorter":n,onUpdateSorter:o,onSorterChange:i}=e;n&&dg(n,t),o&&dg(o,t),i&&dg(i,t),r.value=t}function s(){l(null)}function c(e,t){const n=e.findIndex((e=>(null==t?void 0:t.columnKey)&&e.columnKey===t.columnKey));void 0!==n&&n>=0?e[n]=t:e.push(t)}return{clearSorter:s,sort:function(e,n="ascend"){if(e){const o=t.value.find((t=>"selection"!==t.type&&"expand"!==t.type&&t.key===e));if(!(null==o?void 0:o.sorter))return;const r=o.sorter;a({columnKey:e,sorter:r,order:n})}else s()},sortedDataRef:ai((()=>{const e=i.value.slice().sort(((e,t)=>{const n=ZF(e.sorter)||0;return(ZF(t.sorter)||0)-n}));return e.length?n.value.slice().sort(((t,n)=>{let o=0;return e.some((e=>{const{columnKey:r,sorter:i,order:a}=e,l=function(e,t){return t&&(void 0===e||"default"===e||"object"==typeof e&&"default"===e.compare)?function(e){return(t,n)=>{const o=t[e],r=n[e];return null==o?null==r?0:-1:null==r?1:"number"==typeof o&&"number"==typeof r?o-r:"string"==typeof o&&"string"==typeof r?o.localeCompare(r):0}}(t):"function"==typeof e?e:!(!e||"object"!=typeof e||!e.compare||"default"===e.compare)&&e.compare}(i,r);return!(!l||!a||(o=l(t.rawNode,n.rawNode),0===o)||(o*=function(e){return"ascend"===e?1:"descend"===e?-1:0}(a),0))})),o})):n.value})),mergedSortStateRef:i,deriveNextSorter:a}}function eI(e,{mainTableInstRef:t,mergedCurrentPageRef:n,bodyWidthRef:o}){let r=0;const i=Et(),a=Et(null),l=Et([]),s=Et(null),c=Et([]),u=ai((()=>Ag(e.scrollX))),d=ai((()=>e.columns.filter((e=>"left"===e.fixed)))),p=ai((()=>e.columns.filter((e=>"right"===e.fixed)))),h=ai((()=>{const e={};let t=0;return function n(o){o.forEach((o=>{const r={start:t,end:0};e[aF(o)]=r,"children"in o?(n(o.children),r.end=t):(t+=iF(o)||0,r.end=t)}))}(d.value),e})),f=ai((()=>{const e={};let t=0;return function n(o){for(let r=o.length-1;r>=0;--r){const i=o[r],a={start:t,end:0};e[aF(i)]=a,"children"in i?(n(i.children),a.end=t):(t+=iF(i)||0,a.end=t)}}(p.value),e}));function v(){return{header:t.value?t.value.getHeaderElement():null,body:t.value?t.value.getBodyElement():null}}function m(){const{header:t,body:n}=v();if(!n)return;const{value:u}=o;if(null!==u){if(e.maxHeight||e.flexHeight){if(!t)return;const e=r-t.scrollLeft;i.value=0!==e?"head":"body","head"===i.value?(r=t.scrollLeft,n.scrollLeft=r):(r=n.scrollLeft,t.scrollLeft=r)}else r=n.scrollLeft;!function(){var e,t;const{value:n}=d;let o=0;const{value:i}=h;let l=null;for(let a=0;a((null===(e=i[s])||void 0===e?void 0:e.start)||0)-o))break;l=s,o=(null===(t=i[s])||void 0===t?void 0:t.end)||0}a.value=l}(),function(){l.value=[];let t=e.columns.find((e=>aF(e)===a.value));for(;t&&"children"in t;){const e=t.children.length;if(0===e)break;const n=t.children[e-1];l.value.push(aF(n)),t=n}}(),function(){var t,n;const{value:i}=p,a=Number(e.scrollX),{value:l}=o;if(null===l)return;let c=0,u=null;const{value:d}=f;for(let e=i.length-1;e>=0;--e){const o=aF(i[e]);if(!(Math.round(r+((null===(t=d[o])||void 0===t?void 0:t.start)||0)+l-c)aF(e)===s.value));for(;t&&"children"in t&&t.children.length;){const e=t.children[0];c.value.push(aF(e)),t=e}}()}}return lr(n,(()=>{!function(){const{body:e}=v();e&&(e.scrollTop=0)}()})),{styleScrollXRef:u,fixedColumnLeftMapRef:h,fixedColumnRightMapRef:f,leftFixedColumnsRef:d,rightFixedColumnsRef:p,leftActiveFixedColKeyRef:a,leftActiveFixedChildrenColKeysRef:l,rightActiveFixedColKeyRef:s,rightActiveFixedChildrenColKeysRef:c,syncScrollState:m,handleTableBodyScroll:function(t){var n;null===(n=e.onScroll)||void 0===n||n.call(e,t),"head"!==i.value?Em(m):i.value=void 0},handleTableHeaderScroll:function(){"body"!==i.value?Em(m):i.value=void 0},setHeaderScrollLeft:function(e){const{header:t}=v();t&&(t.scrollLeft=e,m())}}}function tI(e,t){const n=ai((()=>function(e,t){const n=[],o=[],r=[],i=new WeakMap;let a=-1,l=0,s=!1;!function e(i,c){c>a&&(n[c]=[],a=c);for(const n of i)if("children"in n)e(n.children,c+1);else{const e="key"in n?n.key:void 0;o.push({key:aF(n),style:sF(n,void 0!==e?Ag(t(e)):void 0),column:n}),l+=1,s||(s=!!n.ellipsis),r.push(n)}}(e,0);let c=0;return function e(t,o){let r=0;t.forEach((t=>{var s;if("children"in t){const r=c,a={column:t,colSpan:0,rowSpan:1,isLast:!1};e(t.children,o+1),t.children.forEach((e=>{var t,n;a.colSpan+=null!==(n=null===(t=i.get(e))||void 0===t?void 0:t.colSpan)&&void 0!==n?n:0})),r+a.colSpan===l&&(a.isLast=!0),i.set(t,a),n[o].push(a)}else{if(c1&&(r=c+e);const u={column:t,colSpan:e,rowSpan:a-o+1,isLast:c+e===l};i.set(t,u),n[o].push(u),c+=1}}))}(e,0),{hasEllipsis:s,rows:n,cols:o,dataRelatedCols:r}}(e.columns,t)));return{rowsRef:ai((()=>n.value.rows)),colsRef:ai((()=>n.value.cols)),hasEllipsisRef:ai((()=>n.value.hasEllipsis)),dataRelatedColsRef:ai((()=>n.value.dataRelatedCols))}}const nI=[rb("fixed-left","\n left: 0;\n position: sticky;\n z-index: 2;\n ",[eb("&::after",'\n pointer-events: none;\n content: "";\n width: 36px;\n display: inline-block;\n position: absolute;\n top: 0;\n bottom: -1px;\n transition: box-shadow .2s var(--n-bezier);\n right: -36px;\n ')]),rb("fixed-right","\n right: 0;\n position: sticky;\n z-index: 1;\n ",[eb("&::before",'\n pointer-events: none;\n content: "";\n width: 36px;\n display: inline-block;\n position: absolute;\n top: 0;\n bottom: -1px;\n transition: box-shadow .2s var(--n-bezier);\n left: -36px;\n ')])],oI=eb([nb("data-table","\n width: 100%;\n font-size: var(--n-font-size);\n display: flex;\n flex-direction: column;\n position: relative;\n --n-merged-th-color: var(--n-th-color);\n --n-merged-td-color: var(--n-td-color);\n --n-merged-border-color: var(--n-border-color);\n --n-merged-th-color-sorting: var(--n-th-color-sorting);\n --n-merged-td-color-hover: var(--n-td-color-hover);\n --n-merged-td-color-sorting: var(--n-td-color-sorting);\n --n-merged-td-color-striped: var(--n-td-color-striped);\n ",[nb("data-table-wrapper","\n flex-grow: 1;\n display: flex;\n flex-direction: column;\n "),rb("flex-height",[eb(">",[nb("data-table-wrapper",[eb(">",[nb("data-table-base-table","\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n ",[eb(">",[nb("data-table-base-table-body","flex-basis: 0;",[eb("&:last-child","flex-grow: 1;")])])])])])])]),eb(">",[nb("data-table-loading-wrapper","\n color: var(--n-loading-color);\n font-size: var(--n-loading-size);\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translateX(-50%) translateY(-50%);\n transition: color .3s var(--n-bezier);\n display: flex;\n align-items: center;\n justify-content: center;\n ",[gR({originalTransform:"translateX(-50%) translateY(-50%)"})])]),nb("data-table-expand-placeholder","\n margin-right: 8px;\n display: inline-block;\n width: 16px;\n height: 1px;\n "),nb("data-table-indent","\n display: inline-block;\n height: 1px;\n "),nb("data-table-expand-trigger","\n display: inline-flex;\n margin-right: 8px;\n cursor: pointer;\n font-size: 16px;\n vertical-align: -0.2em;\n position: relative;\n width: 16px;\n height: 16px;\n color: var(--n-td-text-color);\n transition: color .3s var(--n-bezier);\n ",[rb("expanded",[nb("icon","transform: rotate(90deg);",[PT({originalTransform:"rotate(90deg)"})]),nb("base-icon","transform: rotate(90deg);",[PT({originalTransform:"rotate(90deg)"})])]),nb("base-loading","\n color: var(--n-loading-color);\n transition: color .3s var(--n-bezier);\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ",[PT()]),nb("icon","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ",[PT()]),nb("base-icon","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ",[PT()])]),nb("data-table-thead","\n transition: background-color .3s var(--n-bezier);\n background-color: var(--n-merged-th-color);\n "),nb("data-table-tr","\n box-sizing: border-box;\n background-clip: padding-box;\n transition: background-color .3s var(--n-bezier);\n ",[nb("data-table-expand","\n position: sticky;\n left: 0;\n overflow: hidden;\n margin: calc(var(--n-th-padding) * -1);\n padding: var(--n-th-padding);\n box-sizing: border-box;\n "),rb("striped","background-color: var(--n-merged-td-color-striped);",[nb("data-table-td","background-color: var(--n-merged-td-color-striped);")]),ib("summary",[eb("&:hover","background-color: var(--n-merged-td-color-hover);",[eb(">",[nb("data-table-td","background-color: var(--n-merged-td-color-hover);")])])])]),nb("data-table-th","\n padding: var(--n-th-padding);\n position: relative;\n text-align: start;\n box-sizing: border-box;\n background-color: var(--n-merged-th-color);\n border-color: var(--n-merged-border-color);\n border-bottom: 1px solid var(--n-merged-border-color);\n color: var(--n-th-text-color);\n transition:\n border-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n font-weight: var(--n-th-font-weight);\n ",[rb("filterable","\n padding-right: 36px;\n ",[rb("sortable","\n padding-right: calc(var(--n-th-padding) + 36px);\n ")]),nI,rb("selection","\n padding: 0;\n text-align: center;\n line-height: 0;\n z-index: 3;\n "),ob("title-wrapper","\n display: flex;\n align-items: center;\n flex-wrap: nowrap;\n max-width: 100%;\n ",[ob("title","\n flex: 1;\n min-width: 0;\n ")]),ob("ellipsis","\n display: inline-block;\n vertical-align: bottom;\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n max-width: 100%;\n "),rb("hover","\n background-color: var(--n-merged-th-color-hover);\n "),rb("sorting","\n background-color: var(--n-merged-th-color-sorting);\n "),rb("sortable","\n cursor: pointer;\n ",[ob("ellipsis","\n max-width: calc(100% - 18px);\n "),eb("&:hover","\n background-color: var(--n-merged-th-color-hover);\n ")]),nb("data-table-sorter","\n height: var(--n-sorter-size);\n width: var(--n-sorter-size);\n margin-left: 4px;\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n vertical-align: -0.2em;\n color: var(--n-th-icon-color);\n transition: color .3s var(--n-bezier);\n ",[nb("base-icon","transition: transform .3s var(--n-bezier)"),rb("desc",[nb("base-icon","\n transform: rotate(0deg);\n ")]),rb("asc",[nb("base-icon","\n transform: rotate(-180deg);\n ")]),rb("asc, desc","\n color: var(--n-th-icon-color-active);\n ")]),nb("data-table-resize-button","\n width: var(--n-resizable-container-size);\n position: absolute;\n top: 0;\n right: calc(var(--n-resizable-container-size) / 2);\n bottom: 0;\n cursor: col-resize;\n user-select: none;\n ",[eb("&::after","\n width: var(--n-resizable-size);\n height: 50%;\n position: absolute;\n top: 50%;\n left: calc(var(--n-resizable-container-size) / 2);\n bottom: 0;\n background-color: var(--n-merged-border-color);\n transform: translateY(-50%);\n transition: background-color .3s var(--n-bezier);\n z-index: 1;\n content: '';\n "),rb("active",[eb("&::after"," \n background-color: var(--n-th-icon-color-active);\n ")]),eb("&:hover::after","\n background-color: var(--n-th-icon-color-active);\n ")]),nb("data-table-filter","\n position: absolute;\n z-index: auto;\n right: 0;\n width: 36px;\n top: 0;\n bottom: 0;\n cursor: pointer;\n display: flex;\n justify-content: center;\n align-items: center;\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n font-size: var(--n-filter-size);\n color: var(--n-th-icon-color);\n ",[eb("&:hover","\n background-color: var(--n-th-button-color-hover);\n "),rb("show","\n background-color: var(--n-th-button-color-hover);\n "),rb("active","\n background-color: var(--n-th-button-color-hover);\n color: var(--n-th-icon-color-active);\n ")])]),nb("data-table-td","\n padding: var(--n-td-padding);\n text-align: start;\n box-sizing: border-box;\n border: none;\n background-color: var(--n-merged-td-color);\n color: var(--n-td-text-color);\n border-bottom: 1px solid var(--n-merged-border-color);\n transition:\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n border-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n ",[rb("expand",[nb("data-table-expand-trigger","\n margin-right: 0;\n ")]),rb("last-row","\n border-bottom: 0 solid var(--n-merged-border-color);\n ",[eb("&::after","\n bottom: 0 !important;\n "),eb("&::before","\n bottom: 0 !important;\n ")]),rb("summary","\n background-color: var(--n-merged-th-color);\n "),rb("hover","\n background-color: var(--n-merged-td-color-hover);\n "),rb("sorting","\n background-color: var(--n-merged-td-color-sorting);\n "),ob("ellipsis","\n display: inline-block;\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n max-width: 100%;\n vertical-align: bottom;\n max-width: calc(100% - var(--indent-offset, -1.5) * 16px - 24px);\n "),rb("selection, expand","\n text-align: center;\n padding: 0;\n line-height: 0;\n "),nI]),nb("data-table-empty","\n box-sizing: border-box;\n padding: var(--n-empty-padding);\n flex-grow: 1;\n flex-shrink: 0;\n opacity: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: opacity .3s var(--n-bezier);\n ",[rb("hide","\n opacity: 0;\n ")]),ob("pagination","\n margin: var(--n-pagination-margin);\n display: flex;\n justify-content: flex-end;\n "),nb("data-table-wrapper","\n position: relative;\n opacity: 1;\n transition: opacity .3s var(--n-bezier), border-color .3s var(--n-bezier);\n border-top-left-radius: var(--n-border-radius);\n border-top-right-radius: var(--n-border-radius);\n line-height: var(--n-line-height);\n "),rb("loading",[nb("data-table-wrapper","\n opacity: var(--n-opacity-loading);\n pointer-events: none;\n ")]),rb("single-column",[nb("data-table-td","\n border-bottom: 0 solid var(--n-merged-border-color);\n ",[eb("&::after, &::before","\n bottom: 0 !important;\n ")])]),ib("single-line",[nb("data-table-th","\n border-right: 1px solid var(--n-merged-border-color);\n ",[rb("last","\n border-right: 0 solid var(--n-merged-border-color);\n ")]),nb("data-table-td","\n border-right: 1px solid var(--n-merged-border-color);\n ",[rb("last-col","\n border-right: 0 solid var(--n-merged-border-color);\n ")])]),rb("bordered",[nb("data-table-wrapper","\n border: 1px solid var(--n-merged-border-color);\n border-bottom-left-radius: var(--n-border-radius);\n border-bottom-right-radius: var(--n-border-radius);\n overflow: hidden;\n ")]),nb("data-table-base-table",[rb("transition-disabled",[nb("data-table-th",[eb("&::after, &::before","transition: none;")]),nb("data-table-td",[eb("&::after, &::before","transition: none;")])])]),rb("bottom-bordered",[nb("data-table-td",[rb("last-row","\n border-bottom: 1px solid var(--n-merged-border-color);\n ")])]),nb("data-table-table","\n font-variant-numeric: tabular-nums;\n width: 100%;\n word-break: break-word;\n transition: background-color .3s var(--n-bezier);\n border-collapse: separate;\n border-spacing: 0;\n background-color: var(--n-merged-td-color);\n "),nb("data-table-base-table-header","\n border-top-left-radius: calc(var(--n-border-radius) - 1px);\n border-top-right-radius: calc(var(--n-border-radius) - 1px);\n z-index: 3;\n overflow: scroll;\n flex-shrink: 0;\n transition: border-color .3s var(--n-bezier);\n scrollbar-width: none;\n ",[eb("&::-webkit-scrollbar","\n width: 0;\n height: 0;\n ")]),nb("data-table-check-extra","\n transition: color .3s var(--n-bezier);\n color: var(--n-th-icon-color);\n position: absolute;\n font-size: 14px;\n right: -4px;\n top: 50%;\n transform: translateY(-50%);\n z-index: 1;\n ")]),nb("data-table-filter-menu",[nb("scrollbar","\n max-height: 240px;\n "),ob("group","\n display: flex;\n flex-direction: column;\n padding: 12px 12px 0 12px;\n ",[nb("checkbox","\n margin-bottom: 12px;\n margin-right: 0;\n "),nb("radio","\n margin-bottom: 12px;\n margin-right: 0;\n ")]),ob("action","\n padding: var(--n-action-padding);\n display: flex;\n flex-wrap: nowrap;\n justify-content: space-evenly;\n border-top: 1px solid var(--n-action-divider-color);\n ",[nb("button",[eb("&:not(:last-child)","\n margin: var(--n-action-button-margin);\n "),eb("&:last-child","\n margin-right: 0;\n ")])]),nb("divider","\n margin: 0 !important;\n ")]),ab(nb("data-table","\n --n-merged-th-color: var(--n-th-color-modal);\n --n-merged-td-color: var(--n-td-color-modal);\n --n-merged-border-color: var(--n-border-color-modal);\n --n-merged-th-color-hover: var(--n-th-color-hover-modal);\n --n-merged-td-color-hover: var(--n-td-color-hover-modal);\n --n-merged-th-color-sorting: var(--n-th-color-hover-modal);\n --n-merged-td-color-sorting: var(--n-td-color-hover-modal);\n --n-merged-td-color-striped: var(--n-td-color-striped-modal);\n ")),lb(nb("data-table","\n --n-merged-th-color: var(--n-th-color-popover);\n --n-merged-td-color: var(--n-td-color-popover);\n --n-merged-border-color: var(--n-border-color-popover);\n --n-merged-th-color-hover: var(--n-th-color-hover-popover);\n --n-merged-td-color-hover: var(--n-td-color-hover-popover);\n --n-merged-th-color-sorting: var(--n-th-color-hover-popover);\n --n-merged-td-color-sorting: var(--n-td-color-hover-popover);\n --n-merged-td-color-striped: var(--n-td-color-striped-popover);\n "))]),rI=zn({name:"DataTable",alias:["AdvancedTable"],props:KM,setup(e,{slots:t}){const{mergedBorderedRef:n,mergedClsPrefixRef:o,inlineThemeDisabled:r,mergedRtlRef:i}=B_(e),a=GP("DataTable",i,o),l=ai((()=>{const{bottomBordered:t}=e;return!n.value&&(void 0===t||t)})),s=I_("DataTable","-data-table",oI,DM,e,o),c=Et(null),u=Et(null),{getResizableWidth:d,clearResizableWidth:p,doUpdateResizableWidth:h}=function(){const e=Et({});return{getResizableWidth:function(t){return e.value[t]},doUpdateResizableWidth:function(t,n){dF(t)&&"key"in t&&(e.value[t.key]=n)},clearResizableWidth:function(){e.value={}}}}(),{rowsRef:f,colsRef:v,dataRelatedColsRef:m,hasEllipsisRef:g}=tI(e,d),{treeMateRef:b,mergedCurrentPageRef:y,paginatedDataRef:x,rawPaginatedDataRef:C,selectionColumnRef:w,hoverKeyRef:k,mergedPaginationRef:S,mergedFilterStateRef:_,mergedSortStateRef:P,childTriggerColIndexRef:T,doUpdatePage:A,doUpdateFilters:z,onUnstableColumnResize:R,deriveNextSorter:E,filter:O,filters:M,clearFilter:F,clearFilters:I,clearSorter:L,page:B,sort:D}=function(e,{dataRelatedColsRef:t}){const n=ai((()=>{const t=e=>{for(let n=0;n{const{childrenKey:t}=e;return JT(e.data,{ignoreEmptyChildren:!0,getKey:e.rowKey,getChildren:e=>e[t],getDisabled:e=>{var t,o;return!!(null===(o=null===(t=n.value)||void 0===t?void 0:t.disabled)||void 0===o?void 0:o.call(t,e))}})})),r=mb((()=>{const{columns:t}=e,{length:n}=t;let o=null;for(let e=0;e{const e=t.value.filter((e=>void 0!==e.filterOptionValues||void 0!==e.filterOptionValue)),n={};return e.forEach((e=>{var t;"selection"!==e.type&&"expand"!==e.type&&(void 0===e.filterOptionValues?n[e.key]=null!==(t=e.filterOptionValue)&&void 0!==t?t:null:n[e.key]=e.filterOptionValues)})),Object.assign(lF(i.value),n)})),u=ai((()=>{const t=c.value,{columns:n}=e;function r(e){return(t,n)=>!!~String(n[e]).indexOf(String(t))}const{value:{treeNodes:i}}=o,a=[];return n.forEach((e=>{"selection"===e.type||"expand"===e.type||"children"in e||a.push([e.key,e])})),i?i.filter((e=>{const{rawNode:n}=e;for(const[o,i]of a){let e=t[o];if(null==e)continue;if(Array.isArray(e)||(e=[e]),!e.length)continue;const a="default"===i.filter?r(o):i.filter;if(i&&"function"==typeof a){if("and"!==i.filterMode){if(e.some((e=>a(e,n))))continue;return!1}if(e.some((e=>!a(e,n))))return!1}}return!0})):[]})),{sortedDataRef:d,deriveNextSorter:p,mergedSortStateRef:h,sort:f,clearSorter:v}=JF(e,{dataRelatedColsRef:t,filteredDataRef:u});t.value.forEach((e=>{var t;if(e.filter){const n=e.defaultFilterOptionValues;e.filterMultiple?i.value[e.key]=n||[]:i.value[e.key]=void 0!==n?null===n?[]:n:null!==(t=e.defaultFilterOptionValue)&&void 0!==t?t:null}}));const m=ai((()=>{const{pagination:t}=e;if(!1!==t)return t.page})),g=ai((()=>{const{pagination:t}=e;if(!1!==t)return t.pageSize})),b=Db(m,l),y=Db(g,s),x=mb((()=>{const t=b.value;return e.remote?t:Math.max(1,Math.min(Math.ceil(u.value.length/y.value),t))})),C=ai((()=>{const{pagination:t}=e;if(t){const{pageCount:e}=t;if(void 0!==e)return e}})),w=ai((()=>{if(e.remote)return o.value.treeNodes;if(!e.pagination)return d.value;const t=y.value,n=(x.value-1)*t;return d.value.slice(n,n+t)})),k=ai((()=>w.value.map((e=>e.rawNode))));function S(t){const{pagination:n}=e;if(n){const{onChange:e,"onUpdate:page":o,onUpdatePage:r}=n;e&&dg(e,t),r&&dg(r,t),o&&dg(o,t),A(t)}}function _(t){const{pagination:n}=e;if(n){const{onPageSizeChange:e,"onUpdate:pageSize":o,onUpdatePageSize:r}=n;e&&dg(e,t),r&&dg(r,t),o&&dg(o,t),z(t)}}const P=ai((()=>{if(!e.remote)return u.value.length;{const{pagination:t}=e;if(t){const{itemCount:e}=t;if(void 0!==e)return e}}})),T=ai((()=>Object.assign(Object.assign({},e.pagination),{onChange:void 0,onUpdatePage:void 0,onUpdatePageSize:void 0,onPageSizeChange:void 0,"onUpdate:page":S,"onUpdate:pageSize":_,page:x.value,pageSize:y.value,pageCount:void 0===P.value?C.value:void 0,itemCount:P.value})));function A(t){const{"onUpdate:page":n,onPageChange:o,onUpdatePage:r}=e;r&&dg(r,t),n&&dg(n,t),o&&dg(o,t),l.value=t}function z(t){const{"onUpdate:pageSize":n,onPageSizeChange:o,onUpdatePageSize:r}=e;o&&dg(o,t),r&&dg(r,t),n&&dg(n,t),s.value=t}function R(){E({})}function E(e){O(e)}function O(e){e?e&&(i.value=lF(e)):i.value={}}return{treeMateRef:o,mergedCurrentPageRef:x,mergedPaginationRef:T,paginatedDataRef:w,rawPaginatedDataRef:k,mergedFilterStateRef:c,mergedSortStateRef:h,hoverKeyRef:Et(null),selectionColumnRef:n,childTriggerColIndexRef:r,doUpdateFilters:function(t,n){const{onUpdateFilters:o,"onUpdate:filters":r,onFiltersChange:a}=e;o&&dg(o,t,n),r&&dg(r,t,n),a&&dg(a,t,n),i.value=t},deriveNextSorter:p,doUpdatePageSize:z,doUpdatePage:A,onUnstableColumnResize:function(t,n,o,r){var i;null===(i=e.onUnstableColumnResize)||void 0===i||i.call(e,t,n,o,r)},filter:O,filters:E,clearFilter:function(){R()},clearFilters:R,clearSorter:v,page:function(e){A(e)},sort:f}}(e,{dataRelatedColsRef:m}),{doCheckAll:$,doUncheckAll:N,doCheck:j,doUncheck:H,headerCheckboxDisabledRef:W,someRowsCheckedRef:U,allRowsCheckedRef:V,mergedCheckedRowKeySetRef:q,mergedInderminateRowKeySetRef:K}=function(e,t){const{paginatedDataRef:n,treeMateRef:o,selectionColumnRef:r}=t,i=Et(e.defaultCheckedRowKeys),a=ai((()=>{var t;const{checkedRowKeys:n}=e,a=void 0===n?i.value:n;return!1===(null===(t=r.value)||void 0===t?void 0:t.multiple)?{checkedKeys:a.slice(0,1),indeterminateKeys:[]}:o.value.getCheckedKeys(a,{cascade:e.cascade,allowNotLoaded:e.allowCheckingNotLoaded})})),l=ai((()=>a.value.checkedKeys)),s=ai((()=>a.value.indeterminateKeys)),c=ai((()=>new Set(l.value))),u=ai((()=>new Set(s.value))),d=ai((()=>{const{value:e}=c;return n.value.reduce(((t,n)=>{const{key:o,disabled:r}=n;return t+(!r&&e.has(o)?1:0)}),0)})),p=ai((()=>n.value.filter((e=>e.disabled)).length)),h=ai((()=>{const{length:e}=n.value,{value:t}=u;return d.value>0&&d.valuet.has(e.key)))})),f=ai((()=>{const{length:e}=n.value;return 0!==d.value&&d.value===e-p.value})),v=ai((()=>0===n.value.length));function m(t,n,r){const{"onUpdate:checkedRowKeys":a,onUpdateCheckedRowKeys:l,onCheckedRowKeysChange:s}=e,c=[],{value:{getNode:u}}=o;t.forEach((e=>{var t;const n=null===(t=u(e))||void 0===t?void 0:t.rawNode;c.push(n)})),a&&dg(a,t,c,{row:n,action:r}),l&&dg(l,t,c,{row:n,action:r}),s&&dg(s,t,c,{row:n,action:r}),i.value=t}return{mergedCheckedRowKeySetRef:c,mergedCheckedRowKeysRef:l,mergedInderminateRowKeySetRef:u,someRowsCheckedRef:h,allRowsCheckedRef:f,headerCheckboxDisabledRef:v,doUpdateCheckedRowKeys:m,doCheckAll:function(t=!1){const{value:i}=r;if(!i||e.loading)return;const a=[];(t?o.value.treeNodes:n.value).forEach((e=>{e.disabled||a.push(e.key)})),m(o.value.check(a,l.value,{cascade:!0,allowNotLoaded:e.allowCheckingNotLoaded}).checkedKeys,void 0,"checkAll")},doUncheckAll:function(t=!1){const{value:i}=r;if(!i||e.loading)return;const a=[];(t?o.value.treeNodes:n.value).forEach((e=>{e.disabled||a.push(e.key)})),m(o.value.uncheck(a,l.value,{cascade:!0,allowNotLoaded:e.allowCheckingNotLoaded}).checkedKeys,void 0,"uncheckAll")},doCheck:function(t,n=!1,r){e.loading||m(n?Array.isArray(t)?t.slice(0,1):[t]:o.value.check(t,l.value,{cascade:e.cascade,allowNotLoaded:e.allowCheckingNotLoaded}).checkedKeys,r,"check")},doUncheck:function(t,n){e.loading||m(o.value.uncheck(t,l.value,{cascade:e.cascade,allowNotLoaded:e.allowCheckingNotLoaded}).checkedKeys,n,"uncheck")}}}(e,{selectionColumnRef:w,treeMateRef:b,paginatedDataRef:x}),{stickyExpandedRowsRef:G,mergedExpandedRowKeysRef:X,renderExpandRef:Y,expandableRef:Q,doUpdateExpandedRowKeys:Z}=function(e,t){const n=mb((()=>{for(const t of e.columns)if("expand"===t.type)return t.renderExpand})),o=mb((()=>{let t;for(const n of e.columns)if("expand"===n.type){t=n.expandable;break}return t})),r=Et(e.defaultExpandAll?(null==n?void 0:n.value)?(()=>{const e=[];return t.value.treeNodes.forEach((t=>{var n;(null===(n=o.value)||void 0===n?void 0:n.call(o,t.rawNode))&&e.push(t.key)})),e})():t.value.getNonLeafKeys():e.defaultExpandedRowKeys),i=jt(e,"expandedRowKeys");return{stickyExpandedRowsRef:jt(e,"stickyExpandedRows"),mergedExpandedRowKeysRef:Db(i,r),renderExpandRef:n,expandableRef:o,doUpdateExpandedRowKeys:function(t){const{onUpdateExpandedRowKeys:n,"onUpdate:expandedRowKeys":o}=e;n&&dg(n,t),o&&dg(o,t),r.value=t}}}(e,b),{handleTableBodyScroll:J,handleTableHeaderScroll:ee,syncScrollState:te,setHeaderScrollLeft:ne,leftActiveFixedColKeyRef:oe,leftActiveFixedChildrenColKeysRef:re,rightActiveFixedColKeyRef:ie,rightActiveFixedChildrenColKeysRef:ae,leftFixedColumnsRef:le,rightFixedColumnsRef:se,fixedColumnLeftMapRef:ce,fixedColumnRightMapRef:ue}=eI(e,{bodyWidthRef:c,mainTableInstRef:u,mergedCurrentPageRef:y}),{localeRef:de}=VP("DataTable"),pe=ai((()=>e.virtualScroll||e.flexHeight||void 0!==e.maxHeight||g.value?"fixed":e.tableLayout));_o(GM,{props:e,treeMateRef:b,renderExpandIconRef:jt(e,"renderExpandIcon"),loadingKeySetRef:Et(new Set),slots:t,indentRef:jt(e,"indent"),childTriggerColIndexRef:T,bodyWidthRef:c,componentId:ig(),hoverKeyRef:k,mergedClsPrefixRef:o,mergedThemeRef:s,scrollXRef:ai((()=>e.scrollX)),rowsRef:f,colsRef:v,paginatedDataRef:x,leftActiveFixedColKeyRef:oe,leftActiveFixedChildrenColKeysRef:re,rightActiveFixedColKeyRef:ie,rightActiveFixedChildrenColKeysRef:ae,leftFixedColumnsRef:le,rightFixedColumnsRef:se,fixedColumnLeftMapRef:ce,fixedColumnRightMapRef:ue,mergedCurrentPageRef:y,someRowsCheckedRef:U,allRowsCheckedRef:V,mergedSortStateRef:P,mergedFilterStateRef:_,loadingRef:jt(e,"loading"),rowClassNameRef:jt(e,"rowClassName"),mergedCheckedRowKeySetRef:q,mergedExpandedRowKeysRef:X,mergedInderminateRowKeySetRef:K,localeRef:de,expandableRef:Q,stickyExpandedRowsRef:G,rowKeyRef:jt(e,"rowKey"),renderExpandRef:Y,summaryRef:jt(e,"summary"),virtualScrollRef:jt(e,"virtualScroll"),rowPropsRef:jt(e,"rowProps"),stripedRef:jt(e,"striped"),checkOptionsRef:ai((()=>{const{value:e}=w;return null==e?void 0:e.options})),rawPaginatedDataRef:C,filterMenuCssVarsRef:ai((()=>{const{self:{actionDividerColor:e,actionPadding:t,actionButtonMargin:n}}=s.value;return{"--n-action-padding":t,"--n-action-button-margin":n,"--n-action-divider-color":e}})),onLoadRef:jt(e,"onLoad"),mergedTableLayoutRef:pe,maxHeightRef:jt(e,"maxHeight"),minHeightRef:jt(e,"minHeight"),flexHeightRef:jt(e,"flexHeight"),headerCheckboxDisabledRef:W,paginationBehaviorOnFilterRef:jt(e,"paginationBehaviorOnFilter"),summaryPlacementRef:jt(e,"summaryPlacement"),filterIconPopoverPropsRef:jt(e,"filterIconPopoverProps"),scrollbarPropsRef:jt(e,"scrollbarProps"),syncScrollState:te,doUpdatePage:A,doUpdateFilters:z,getResizableWidth:d,onUnstableColumnResize:R,clearResizableWidth:p,doUpdateResizableWidth:h,deriveNextSorter:E,doCheck:j,doUncheck:H,doCheckAll:$,doUncheckAll:N,doUpdateExpandedRowKeys:Z,handleTableHeaderScroll:ee,handleTableBodyScroll:J,setHeaderScrollLeft:ne,renderCell:jt(e,"renderCell")});const he={filter:O,filters:M,clearFilters:I,clearSorter:L,page:B,sort:D,clearFilter:F,downloadCsv:t=>{const{fileName:n="data.csv",keepOriginalData:o=!1}=t||{},r=o?e.data:C.value,i=function(e,t){const n=e.filter((e=>"expand"!==e.type&&"selection"!==e.type));return[n.map((e=>e.title)).join(","),...t.map((e=>n.map((t=>{return"string"==typeof(n=e[t.key])?n.replace(/,/g,"\\,"):null==n?"":`${n}`.replace(/,/g,"\\,");var n})).join(",")))].join("\n")}(e.columns,r),a=new Blob([i],{type:"text/csv;charset=utf-8"}),l=URL.createObjectURL(a);!function(e,t){if(!e)return;const n=document.createElement("a");n.href=e,void 0!==t&&(n.download=t),document.body.appendChild(n),n.click(),document.body.removeChild(n)}(l,n.endsWith(".csv")?n:`${n}.csv`),URL.revokeObjectURL(l)},scrollTo:(e,t)=>{var n;null===(n=u.value)||void 0===n||n.scrollTo(e,t)}},fe=ai((()=>{const{size:t}=e,{common:{cubicBezierEaseInOut:n},self:{borderColor:o,tdColorHover:r,tdColorSorting:i,tdColorSortingModal:a,tdColorSortingPopover:l,thColorSorting:c,thColorSortingModal:u,thColorSortingPopover:d,thColor:p,thColorHover:h,tdColor:f,tdTextColor:v,thTextColor:m,thFontWeight:g,thButtonColorHover:b,thIconColor:y,thIconColorActive:x,filterSize:C,borderRadius:w,lineHeight:k,tdColorModal:S,thColorModal:_,borderColorModal:P,thColorHoverModal:T,tdColorHoverModal:A,borderColorPopover:z,thColorPopover:R,tdColorPopover:E,tdColorHoverPopover:O,thColorHoverPopover:M,paginationMargin:F,emptyPadding:I,boxShadowAfter:L,boxShadowBefore:B,sorterSize:D,resizableContainerSize:$,resizableSize:N,loadingColor:j,loadingSize:H,opacityLoading:W,tdColorStriped:U,tdColorStripedModal:V,tdColorStripedPopover:q,[ub("fontSize",t)]:K,[ub("thPadding",t)]:G,[ub("tdPadding",t)]:X}}=s.value;return{"--n-font-size":K,"--n-th-padding":G,"--n-td-padding":X,"--n-bezier":n,"--n-border-radius":w,"--n-line-height":k,"--n-border-color":o,"--n-border-color-modal":P,"--n-border-color-popover":z,"--n-th-color":p,"--n-th-color-hover":h,"--n-th-color-modal":_,"--n-th-color-hover-modal":T,"--n-th-color-popover":R,"--n-th-color-hover-popover":M,"--n-td-color":f,"--n-td-color-hover":r,"--n-td-color-modal":S,"--n-td-color-hover-modal":A,"--n-td-color-popover":E,"--n-td-color-hover-popover":O,"--n-th-text-color":m,"--n-td-text-color":v,"--n-th-font-weight":g,"--n-th-button-color-hover":b,"--n-th-icon-color":y,"--n-th-icon-color-active":x,"--n-filter-size":C,"--n-pagination-margin":F,"--n-empty-padding":I,"--n-box-shadow-before":B,"--n-box-shadow-after":L,"--n-sorter-size":D,"--n-resizable-container-size":$,"--n-resizable-size":N,"--n-loading-size":H,"--n-loading-color":j,"--n-opacity-loading":W,"--n-td-color-striped":U,"--n-td-color-striped-modal":V,"--n-td-color-striped-popover":q,"n-td-color-sorting":i,"n-td-color-sorting-modal":a,"n-td-color-sorting-popover":l,"n-th-color-sorting":c,"n-th-color-sorting-modal":u,"n-th-color-sorting-popover":d}})),ve=r?KP("data-table",ai((()=>e.size[0])),fe,e):void 0,me=ai((()=>{if(!e.pagination)return!1;if(e.paginateSinglePage)return!0;const t=S.value,{pageCount:n}=t;return void 0!==n?n>1:t.itemCount&&t.pageSize&&t.itemCount>t.pageSize}));return Object.assign({mainTableInstRef:u,mergedClsPrefix:o,rtlEnabled:a,mergedTheme:s,paginatedData:x,mergedBordered:n,mergedBottomBordered:l,mergedPagination:S,mergedShowPagination:me,cssVars:r?void 0:fe,themeClass:null==ve?void 0:ve.themeClass,onRender:null==ve?void 0:ve.onRender},he)},render(){const{mergedClsPrefix:e,themeClass:t,onRender:n,$slots:o,spinProps:r}=this;return null==n||n(),li("div",{class:[`${e}-data-table`,this.rtlEnabled&&`${e}-data-table--rtl`,t,{[`${e}-data-table--bordered`]:this.mergedBordered,[`${e}-data-table--bottom-bordered`]:this.mergedBottomBordered,[`${e}-data-table--single-line`]:this.singleLine,[`${e}-data-table--single-column`]:this.singleColumn,[`${e}-data-table--loading`]:this.loading,[`${e}-data-table--flex-height`]:this.flexHeight}],style:this.cssVars},li("div",{class:`${e}-data-table-wrapper`},li(QF,{ref:"mainTableInstRef"})),this.mergedShowPagination?li("div",{class:`${e}-data-table__pagination`},li(kM,Object.assign({theme:this.mergedTheme.peers.Pagination,themeOverrides:this.mergedTheme.peerOverrides.Pagination,disabled:this.loading},this.mergedPagination))):null,li(vi,{name:"fade-in-scale-up-transition"},{default:()=>this.loading?li("div",{class:`${e}-data-table-loading-wrapper`},xg(o.loading,(()=>[li(RT,Object.assign({clsPrefix:e,strokeWidth:20},r))]))):null}))}}),iI={itemFontSize:"12px",itemHeight:"36px",itemWidth:"52px",panelActionPadding:"8px 0"},aI={name:"TimePicker",common:tz,peers:{Scrollbar:nR,Button:eO,Input:xE},self:function(e){const{popoverColor:t,textColor2:n,primaryColor:o,hoverColor:r,dividerColor:i,opacityDisabled:a,boxShadow2:l,borderRadius:s,iconColor:c,iconColorDisabled:u}=e;return Object.assign(Object.assign({},iI),{panelColor:t,panelBoxShadow:l,panelDividerColor:i,itemTextColor:n,itemTextColorActive:o,itemColorHover:r,itemOpacityDisabled:a,itemBorderRadius:s,borderRadius:s,iconColor:c,iconColorDisabled:u})}},lI={itemSize:"24px",itemCellWidth:"38px",itemCellHeight:"32px",scrollItemWidth:"80px",scrollItemHeight:"40px",panelExtraFooterPadding:"8px 12px",panelActionPadding:"8px 12px",calendarTitlePadding:"0",calendarTitleHeight:"28px",arrowSize:"14px",panelHeaderPadding:"8px 12px",calendarDaysHeight:"32px",calendarTitleGridTempateColumns:"28px 28px 1fr 28px 28px",calendarLeftPaddingDate:"6px 12px 4px 12px",calendarLeftPaddingDatetime:"4px 12px",calendarLeftPaddingDaterange:"6px 12px 4px 12px",calendarLeftPaddingDatetimerange:"4px 12px",calendarLeftPaddingMonth:"0",calendarLeftPaddingYear:"0",calendarLeftPaddingQuarter:"0",calendarLeftPaddingMonthrange:"0",calendarLeftPaddingQuarterrange:"0",calendarLeftPaddingYearrange:"0",calendarLeftPaddingWeek:"6px 12px 4px 12px",calendarRightPaddingDate:"6px 12px 4px 12px",calendarRightPaddingDatetime:"4px 12px",calendarRightPaddingDaterange:"6px 12px 4px 12px",calendarRightPaddingDatetimerange:"4px 12px",calendarRightPaddingMonth:"0",calendarRightPaddingYear:"0",calendarRightPaddingQuarter:"0",calendarRightPaddingMonthrange:"0",calendarRightPaddingQuarterrange:"0",calendarRightPaddingYearrange:"0",calendarRightPaddingWeek:"0"},sI={name:"DatePicker",common:tz,peers:{Input:xE,Button:eO,TimePicker:aI,Scrollbar:nR},self(e){const{popoverColor:t,hoverColor:n,primaryColor:o}=e,r=function(e){const{hoverColor:t,fontSize:n,textColor2:o,textColorDisabled:r,popoverColor:i,primaryColor:a,borderRadiusSmall:l,iconColor:s,iconColorDisabled:c,textColor1:u,dividerColor:d,boxShadow2:p,borderRadius:h,fontWeightStrong:f}=e;return Object.assign(Object.assign({},lI),{itemFontSize:n,calendarDaysFontSize:n,calendarTitleFontSize:n,itemTextColor:o,itemTextColorDisabled:r,itemTextColorActive:i,itemTextColorCurrent:a,itemColorIncluded:tg(a,{alpha:.1}),itemColorHover:t,itemColorDisabled:t,itemColorActive:a,itemBorderRadius:l,panelColor:i,panelTextColor:o,arrowColor:s,calendarTitleTextColor:u,calendarTitleColorHover:t,calendarDaysTextColor:o,panelHeaderDividerColor:d,calendarDaysDividerColor:d,calendarDividerColor:d,panelActionDividerColor:d,panelBoxShadow:p,panelBorderRadius:h,calendarTitleFontWeight:f,scrollItemBorderRadius:h,iconColor:s,iconColorDisabled:c})}(e);return r.itemColorDisabled=eg(t,n),r.itemColorIncluded=tg(o,{alpha:.15}),r.itemColorHover=eg(t,n),r}},cI={thPaddingBorderedSmall:"8px 12px",thPaddingBorderedMedium:"12px 16px",thPaddingBorderedLarge:"16px 24px",thPaddingSmall:"0",thPaddingMedium:"0",thPaddingLarge:"0",tdPaddingBorderedSmall:"8px 12px",tdPaddingBorderedMedium:"12px 16px",tdPaddingBorderedLarge:"16px 24px",tdPaddingSmall:"0 0 8px 0",tdPaddingMedium:"0 0 12px 0",tdPaddingLarge:"0 0 16px 0"},uI={name:"Descriptions",common:tz,self:function(e){const{tableHeaderColor:t,textColor2:n,textColor1:o,cardColor:r,modalColor:i,popoverColor:a,dividerColor:l,borderRadius:s,fontWeightStrong:c,lineHeight:u,fontSizeSmall:d,fontSizeMedium:p,fontSizeLarge:h}=e;return Object.assign(Object.assign({},cI),{lineHeight:u,fontSizeSmall:d,fontSizeMedium:p,fontSizeLarge:h,titleTextColor:o,thColor:eg(r,t),thColorModal:eg(i,t),thColorPopover:eg(a,t),thTextColor:o,thFontWeight:c,tdTextColor:n,tdColor:r,tdColorModal:i,tdColorPopover:a,borderColor:eg(r,l),borderColorModal:eg(i,l),borderColorPopover:eg(a,l),borderRadius:s})}},dI={titleFontSize:"18px",padding:"16px 28px 20px 28px",iconSize:"28px",actionSpace:"12px",contentMargin:"8px 0 16px 0",iconMargin:"0 4px 0 0",iconMarginIconTop:"4px 0 8px 0",closeSize:"22px",closeIconSize:"18px",closeMargin:"20px 26px 0 0",closeMarginIconTop:"10px 16px 0 0"};function pI(e){const{textColor1:t,textColor2:n,modalColor:o,closeIconColor:r,closeIconColorHover:i,closeIconColorPressed:a,closeColorHover:l,closeColorPressed:s,infoColor:c,successColor:u,warningColor:d,errorColor:p,primaryColor:h,dividerColor:f,borderRadius:v,fontWeightStrong:m,lineHeight:g,fontSize:b}=e;return Object.assign(Object.assign({},dI),{fontSize:b,lineHeight:g,border:`1px solid ${f}`,titleTextColor:t,textColor:n,color:o,closeColorHover:l,closeColorPressed:s,closeIconColor:r,closeIconColorHover:i,closeIconColorPressed:a,closeBorderRadius:v,iconColor:h,iconColorInfo:c,iconColorSuccess:u,iconColorWarning:d,iconColorError:p,borderRadius:v,titleFontWeight:m})}const hI={name:"Dialog",common:qz,peers:{Button:JE},self:pI},fI={name:"Dialog",common:tz,peers:{Button:eO},self:pI},vI={icon:Function,type:{type:String,default:"default"},title:[String,Function],closable:{type:Boolean,default:!0},negativeText:String,positiveText:String,positiveButtonProps:Object,negativeButtonProps:Object,content:[String,Function],action:Function,showIcon:{type:Boolean,default:!0},loading:Boolean,bordered:Boolean,iconPlacement:String,titleClass:[String,Array],titleStyle:[String,Object],contentClass:[String,Array],contentStyle:[String,Object],actionClass:[String,Array],actionStyle:[String,Object],onPositiveClick:Function,onNegativeClick:Function,onClose:Function},mI=pg(vI),gI=eb([nb("dialog","\n --n-icon-margin: var(--n-icon-margin-top) var(--n-icon-margin-right) var(--n-icon-margin-bottom) var(--n-icon-margin-left);\n word-break: break-word;\n line-height: var(--n-line-height);\n position: relative;\n background: var(--n-color);\n color: var(--n-text-color);\n box-sizing: border-box;\n margin: auto;\n border-radius: var(--n-border-radius);\n padding: var(--n-padding);\n transition: \n border-color .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n ",[ob("icon",{color:"var(--n-icon-color)"}),rb("bordered",{border:"var(--n-border)"}),rb("icon-top",[ob("close",{margin:"var(--n-close-margin)"}),ob("icon",{margin:"var(--n-icon-margin)"}),ob("content",{textAlign:"center"}),ob("title",{justifyContent:"center"}),ob("action",{justifyContent:"center"})]),rb("icon-left",[ob("icon",{margin:"var(--n-icon-margin)"}),rb("closable",[ob("title","\n padding-right: calc(var(--n-close-size) + 6px);\n ")])]),ob("close","\n position: absolute;\n right: 0;\n top: 0;\n margin: var(--n-close-margin);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n z-index: 1;\n "),ob("content","\n font-size: var(--n-font-size);\n margin: var(--n-content-margin);\n position: relative;\n word-break: break-word;\n ",[rb("last","margin-bottom: 0;")]),ob("action","\n display: flex;\n justify-content: flex-end;\n ",[eb("> *:not(:last-child)","\n margin-right: var(--n-action-space);\n ")]),ob("icon","\n font-size: var(--n-icon-size);\n transition: color .3s var(--n-bezier);\n "),ob("title","\n transition: color .3s var(--n-bezier);\n display: flex;\n align-items: center;\n font-size: var(--n-title-font-size);\n font-weight: var(--n-title-font-weight);\n color: var(--n-title-text-color);\n "),nb("dialog-icon-container","\n display: flex;\n justify-content: center;\n ")]),ab(nb("dialog","\n width: 446px;\n max-width: calc(100vw - 32px);\n ")),nb("dialog",[sb("\n width: 446px;\n max-width: calc(100vw - 32px);\n ")])]),bI={default:()=>li(uT,null),info:()=>li(uT,null),success:()=>li(hT,null),warning:()=>li(fT,null),error:()=>li(iT,null)},yI=zn({name:"Dialog",alias:["NimbusConfirmCard","Confirm"],props:Object.assign(Object.assign({},I_.props),vI),setup(e){const{mergedComponentPropsRef:t,mergedClsPrefixRef:n,inlineThemeDisabled:o,mergedRtlRef:r}=B_(e),i=GP("Dialog",r,n),a=ai((()=>{var n,o;const{iconPlacement:r}=e;return r||(null===(o=null===(n=null==t?void 0:t.value)||void 0===n?void 0:n.Dialog)||void 0===o?void 0:o.iconPlacement)||"left"})),l=I_("Dialog","-dialog",gI,hI,e,n),s=ai((()=>{const{type:t}=e,n=a.value,{common:{cubicBezierEaseInOut:o},self:{fontSize:r,lineHeight:i,border:s,titleTextColor:c,textColor:u,color:d,closeBorderRadius:p,closeColorHover:h,closeColorPressed:f,closeIconColor:v,closeIconColorHover:m,closeIconColorPressed:g,closeIconSize:b,borderRadius:y,titleFontWeight:x,titleFontSize:C,padding:w,iconSize:k,actionSpace:S,contentMargin:_,closeSize:P,["top"===n?"iconMarginIconTop":"iconMargin"]:T,["top"===n?"closeMarginIconTop":"closeMargin"]:A,[ub("iconColor",t)]:z}}=l.value,R=Bm(T);return{"--n-font-size":r,"--n-icon-color":z,"--n-bezier":o,"--n-close-margin":A,"--n-icon-margin-top":R.top,"--n-icon-margin-right":R.right,"--n-icon-margin-bottom":R.bottom,"--n-icon-margin-left":R.left,"--n-icon-size":k,"--n-close-size":P,"--n-close-icon-size":b,"--n-close-border-radius":p,"--n-close-color-hover":h,"--n-close-color-pressed":f,"--n-close-icon-color":v,"--n-close-icon-color-hover":m,"--n-close-icon-color-pressed":g,"--n-color":d,"--n-text-color":u,"--n-border-radius":y,"--n-padding":w,"--n-line-height":i,"--n-border":s,"--n-content-margin":_,"--n-title-font-size":C,"--n-title-font-weight":x,"--n-title-text-color":c,"--n-action-space":S}})),c=o?KP("dialog",ai((()=>`${e.type[0]}${a.value[0]}`)),s,e):void 0;return{mergedClsPrefix:n,rtlEnabled:i,mergedIconPlacement:a,mergedTheme:l,handlePositiveClick:function(t){const{onPositiveClick:n}=e;n&&n(t)},handleNegativeClick:function(t){const{onNegativeClick:n}=e;n&&n(t)},handleCloseClick:function(){const{onClose:t}=e;t&&t()},cssVars:o?void 0:s,themeClass:null==c?void 0:c.themeClass,onRender:null==c?void 0:c.onRender}},render(){var e;const{bordered:t,mergedIconPlacement:n,cssVars:o,closable:r,showIcon:i,title:a,content:l,action:s,negativeText:c,positiveText:u,positiveButtonProps:d,negativeButtonProps:p,handlePositiveClick:h,handleNegativeClick:f,mergedTheme:v,loading:m,type:g,mergedClsPrefix:b}=this;null===(e=this.onRender)||void 0===e||e.call(this);const y=i?li(CT,{clsPrefix:b,class:`${b}-dialog__icon`},{default:()=>wg(this.$slots.icon,(e=>e||(this.icon?hg(this.icon):bI[this.type]())))}):null,x=wg(this.$slots.action,(e=>e||u||c||s?li("div",{class:[`${b}-dialog__action`,this.actionClass],style:this.actionStyle},e||(s?[hg(s)]:[this.negativeText&&li(oO,Object.assign({theme:v.peers.Button,themeOverrides:v.peerOverrides.Button,ghost:!0,size:"small",onClick:f},p),{default:()=>hg(this.negativeText)}),this.positiveText&&li(oO,Object.assign({theme:v.peers.Button,themeOverrides:v.peerOverrides.Button,size:"small",type:"default"===g?"primary":g,disabled:m,loading:m,onClick:h},d),{default:()=>hg(this.positiveText)})])):null));return li("div",{class:[`${b}-dialog`,this.themeClass,this.closable&&`${b}-dialog--closable`,`${b}-dialog--icon-${n}`,t&&`${b}-dialog--bordered`,this.rtlEnabled&&`${b}-dialog--rtl`],style:o,role:"dialog"},r?wg(this.$slots.close,(e=>{const t=[`${b}-dialog__close`,this.rtlEnabled&&`${b}-dialog--rtl`];return e?li("div",{class:t},e):li(kT,{clsPrefix:b,class:t,onClick:this.handleCloseClick})})):null,i&&"top"===n?li("div",{class:`${b}-dialog-icon-container`},y):null,li("div",{class:[`${b}-dialog__title`,this.titleClass],style:this.titleStyle},i&&"left"===n?y:null,xg(this.$slots.header,(()=>[hg(a)]))),li("div",{class:[`${b}-dialog__content`,x?"":`${b}-dialog__content--last`,this.contentClass],style:this.contentStyle},xg(this.$slots.default,(()=>[hg(l)]))),x)}}),xI="n-dialog-provider",CI="n-dialog-api";function wI(e){const{modalColor:t,textColor2:n,boxShadow3:o}=e;return{color:t,textColor:n,boxShadow:o}}const kI={name:"Modal",common:qz,peers:{Scrollbar:tR,Dialog:hI,Card:uO},self:wI},SI={name:"Modal",common:tz,peers:{Scrollbar:nR,Dialog:fI,Card:dO},self:wI},_I=Object.assign(Object.assign({},hO),vI),PI=pg(_I),TI=zn({name:"ModalBody",inheritAttrs:!1,props:Object.assign(Object.assign({show:{type:Boolean,required:!0},preset:String,displayDirective:{type:String,required:!0},trapFocus:{type:Boolean,default:!0},autoFocus:{type:Boolean,default:!0},blockScroll:Boolean},_I),{renderMask:Function,onClickoutside:Function,onBeforeLeave:{type:Function,required:!0},onAfterLeave:{type:Function,required:!0},onPositiveClick:{type:Function,required:!0},onNegativeClick:{type:Function,required:!0},onClose:{type:Function,required:!0},onAfterEnter:Function,onEsc:Function}),setup(e){const t=Et(null),n=Et(null),o=Et(e.show),r=Et(null),i=Et(null);lr(jt(e,"show"),(e=>{e&&(o.value=!0)})),Vx(ai((()=>e.blockScroll&&o.value)));const a=Po(Vb);function l(){if("center"===a.transformOriginRef.value)return"";const{value:e}=r,{value:t}=i;return null===e||null===t?"":n.value?`${e}px ${t+n.value.containerScrollTop}px`:""}const s=Et(null);return lr(s,(e=>{e&&tn((()=>{const n=e.el;n&&t.value!==n&&(t.value=n)}))})),_o(Ub,t),_o(qb,null),_o(Gb,null),{mergedTheme:a.mergedThemeRef,appear:a.appearRef,isMounted:a.isMountedRef,mergedClsPrefix:a.mergedClsPrefixRef,bodyRef:t,scrollbarRef:n,displayed:o,childNodeRef:s,handlePositiveClick:function(){e.onPositiveClick()},handleNegativeClick:function(){e.onNegativeClick()},handleCloseClick:function(){const{onClose:t}=e;t&&t()},handleAfterLeave:function(){o.value=!1,r.value=null,i.value=null,e.onAfterLeave()},handleBeforeLeave:function(t){t.style.transformOrigin=l(),e.onBeforeLeave()},handleEnter:function(e){tn((()=>{!function(e){if("center"===a.transformOriginRef.value)return;const t=a.getMousePosition();if(!t)return;if(!n.value)return;const o=n.value.containerScrollTop,{offsetLeft:s,offsetTop:c}=e;if(t){const e=t.y,n=t.x;r.value=-(s-n),i.value=-(c-e-o)}e.style.transformOrigin=l()}(e)}))}}},render(){const{$slots:e,$attrs:t,handleEnter:n,handleAfterLeave:o,handleBeforeLeave:r,preset:i,mergedClsPrefix:a}=this;let l=null;if(!i){if(l=gg(e),!l)return;l=Br(l),l.props=Wr({class:`${a}-modal`},t,l.props||{})}return"show"===this.displayDirective||this.displayed||this.show?fn(li("div",{role:"none",class:`${a}-modal-body-wrapper`},li(lR,{ref:"scrollbarRef",theme:this.mergedTheme.peers.Scrollbar,themeOverrides:this.mergedTheme.peerOverrides.Scrollbar,contentClass:`${a}-modal-scroll-content`},{default:()=>{var t;return[null===(t=this.renderMask)||void 0===t?void 0:t.call(this),li(Bx,{disabled:!this.trapFocus,active:this.show,onEsc:this.onEsc,autoFocus:this.autoFocus},{default:()=>{var t;return li(vi,{name:"fade-in-scale-up-transition",appear:null!==(t=this.appear)&&void 0!==t?t:this.isMounted,onEnter:n,onAfterEnter:this.onAfterEnter,onAfterLeave:o,onBeforeLeave:r},{default:()=>{const t=[[Mi,this.show]],{onClickoutside:n}=this;return n&&t.push([dy,this.onClickoutside,void 0,{capture:!0}]),fn("confirm"===this.preset||"dialog"===this.preset?li(yI,Object.assign({},this.$attrs,{class:[`${a}-modal`,this.$attrs.class],ref:"bodyRef",theme:this.mergedTheme.peers.Dialog,themeOverrides:this.mergedTheme.peerOverrides.Dialog},sg(this.$props,mI),{"aria-modal":"true"}),e):"card"===this.preset?li(vO,Object.assign({},this.$attrs,{ref:"bodyRef",class:[`${a}-modal`,this.$attrs.class],theme:this.mergedTheme.peers.Card,themeOverrides:this.mergedTheme.peerOverrides.Card},sg(this.$props,fO),{"aria-modal":"true",role:"dialog"}),e):this.childNodeRef=l,t)}})}})]}})),[[Mi,"if"===this.displayDirective||this.displayed||this.show]]):null}}),AI=eb([nb("modal-container","\n position: fixed;\n left: 0;\n top: 0;\n height: 0;\n width: 0;\n display: flex;\n "),nb("modal-mask","\n position: fixed;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, .4);\n ",[rR({enterDuration:".25s",leaveDuration:".25s",enterCubicBezier:"var(--n-bezier-ease-out)",leaveCubicBezier:"var(--n-bezier-ease-out)"})]),nb("modal-body-wrapper","\n position: fixed;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n overflow: visible;\n ",[nb("modal-scroll-content","\n min-height: 100%;\n display: flex;\n position: relative;\n ")]),nb("modal","\n position: relative;\n align-self: center;\n color: var(--n-text-color);\n margin: auto;\n box-shadow: var(--n-box-shadow);\n ",[gR({duration:".25s",enterScale:".5"})])]),zI=Object.assign(Object.assign(Object.assign(Object.assign({},I_.props),{show:Boolean,unstableShowMask:{type:Boolean,default:!0},maskClosable:{type:Boolean,default:!0},preset:String,to:[String,Object],displayDirective:{type:String,default:"if"},transformOrigin:{type:String,default:"mouse"},zIndex:Number,autoFocus:{type:Boolean,default:!0},trapFocus:{type:Boolean,default:!0},closeOnEsc:{type:Boolean,default:!0},blockScroll:{type:Boolean,default:!0}}),_I),{onEsc:Function,"onUpdate:show":[Function,Array],onUpdateShow:[Function,Array],onAfterEnter:Function,onBeforeLeave:Function,onAfterLeave:Function,onClose:Function,onPositiveClick:Function,onNegativeClick:Function,onMaskClick:Function,internalDialog:Boolean,internalModal:Boolean,internalAppear:{type:Boolean,default:void 0},overlayStyle:[String,Object],onBeforeHide:Function,onAfterHide:Function,onHide:Function}),RI=zn({name:"Modal",inheritAttrs:!1,props:zI,setup(e){const t=Et(null),{mergedClsPrefixRef:n,namespaceRef:o,inlineThemeDisabled:r}=B_(e),i=I_("Modal","-modal",AI,kI,e,n),a=Bb(64),l=Ob(),s=$b(),c=e.internalDialog?Po(xI,null):null,u=e.internalModal?Po("n-modal-provider",null):null,d=Yx();function p(t){const{onUpdateShow:n,"onUpdate:show":o,onHide:r}=e;n&&dg(n,t),o&&dg(o,t),r&&!t&&r(t)}_o(Vb,{getMousePosition:()=>{const e=c||u;if(e){const{clickedRef:t,clickedPositionRef:n}=e;if(t.value&&n.value)return n.value}return a.value?l.value:null},mergedClsPrefixRef:n,mergedThemeRef:i,isMountedRef:s,appearRef:jt(e,"internalAppear"),transformOriginRef:jt(e,"transformOrigin")});const h=ai((()=>{const{common:{cubicBezierEaseOut:e},self:{boxShadow:t,color:n,textColor:o}}=i.value;return{"--n-bezier-ease-out":e,"--n-box-shadow":t,"--n-color":n,"--n-text-color":o}})),f=r?KP("theme-class",void 0,h,e):void 0;return{mergedClsPrefix:n,namespace:o,isMounted:s,containerRef:t,presetProps:ai((()=>sg(e,PI))),handleEsc:function(t){var n;null===(n=e.onEsc)||void 0===n||n.call(e),e.show&&e.closeOnEsc&&fb(t)&&(d.value||p(!1))},handleAfterLeave:function(){const{onAfterLeave:t,onAfterHide:n}=e;t&&dg(t),n&&n()},handleClickoutside:function(n){var o;const{onMaskClick:r}=e;r&&r(n),e.maskClosable&&(null===(o=t.value)||void 0===o?void 0:o.contains(Fm(n)))&&p(!1)},handleBeforeLeave:function(){const{onBeforeLeave:t,onBeforeHide:n}=e;t&&dg(t),n&&n()},doUpdateShow:p,handleNegativeClick:function(){const{onNegativeClick:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&p(!1)})):p(!1)},handlePositiveClick:function(){const{onPositiveClick:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&p(!1)})):p(!1)},handleCloseClick:function(){const{onClose:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&p(!1)})):p(!1)},cssVars:r?void 0:h,themeClass:null==f?void 0:f.themeClass,onRender:null==f?void 0:f.onRender}},render(){const{mergedClsPrefix:e}=this;return li(Sy,{to:this.to,show:this.show},{default:()=>{var t;null===(t=this.onRender)||void 0===t||t.call(this);const{unstableShowMask:n}=this;return fn(li("div",{role:"none",ref:"containerRef",class:[`${e}-modal-container`,this.themeClass,this.namespace],style:this.cssVars},li(TI,Object.assign({style:this.overlayStyle},this.$attrs,{ref:"bodyWrapper",displayDirective:this.displayDirective,show:this.show,preset:this.preset,autoFocus:this.autoFocus,trapFocus:this.trapFocus,blockScroll:this.blockScroll},this.presetProps,{onEsc:this.handleEsc,onClose:this.handleCloseClick,onNegativeClick:this.handleNegativeClick,onPositiveClick:this.handlePositiveClick,onBeforeLeave:this.handleBeforeLeave,onAfterEnter:this.onAfterEnter,onAfterLeave:this.handleAfterLeave,onClickoutside:n?void 0:this.handleClickoutside,renderMask:n?()=>{var t;return li(vi,{name:"fade-in-transition",key:"mask",appear:null!==(t=this.internalAppear)&&void 0!==t?t:this.isMounted},{default:()=>this.show?li("div",{"aria-hidden":!0,ref:"containerRef",class:`${e}-modal-mask`,onClick:this.handleClickoutside}):null})}:void 0}),this.$slots)),[[fy,{zIndex:this.zIndex,enabled:this.show}]])}})}}),EI=Object.assign(Object.assign({},vI),{onAfterEnter:Function,onAfterLeave:Function,transformOrigin:String,blockScroll:{type:Boolean,default:!0},closeOnEsc:{type:Boolean,default:!0},onEsc:Function,autoFocus:{type:Boolean,default:!0},internalStyle:[String,Object],maskClosable:{type:Boolean,default:!0},onPositiveClick:Function,onNegativeClick:Function,onClose:Function,onMaskClick:Function}),OI=zn({name:"DialogEnvironment",props:Object.assign(Object.assign({},EI),{internalKey:{type:String,required:!0},to:[String,Object],onInternalAfterLeave:{type:Function,required:!0}}),setup(e){const t=Et(!0);function n(){t.value=!1}return{show:t,hide:n,handleUpdateShow:function(e){t.value=e},handleAfterLeave:function(){const{onInternalAfterLeave:t,internalKey:n,onAfterLeave:o}=e;t&&t(n),o&&o()},handleCloseClick:function(){const{onClose:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&n()})):n()},handleNegativeClick:function(t){const{onNegativeClick:o}=e;o?Promise.resolve(o(t)).then((e=>{!1!==e&&n()})):n()},handlePositiveClick:function(t){const{onPositiveClick:o}=e;o?Promise.resolve(o(t)).then((e=>{!1!==e&&n()})):n()},handleMaskClick:function(t){const{onMaskClick:o,maskClosable:r}=e;o&&(o(t),r&&n())},handleEsc:function(){const{onEsc:t}=e;t&&t()}}},render(){const{handlePositiveClick:e,handleUpdateShow:t,handleNegativeClick:n,handleCloseClick:o,handleAfterLeave:r,handleMaskClick:i,handleEsc:a,to:l,maskClosable:s,show:c}=this;return li(RI,{show:c,onUpdateShow:t,onMaskClick:i,onEsc:a,to:l,maskClosable:s,onAfterEnter:this.onAfterEnter,onAfterLeave:r,closeOnEsc:this.closeOnEsc,blockScroll:this.blockScroll,autoFocus:this.autoFocus,transformOrigin:this.transformOrigin,internalAppear:!0,internalDialog:!0},{default:()=>li(yI,Object.assign({},sg(this.$props,mI),{style:this.internalStyle,onClose:o,onNegativeClick:n,onPositiveClick:e}))})}}),MI=zn({name:"DialogProvider",props:{injectionKey:String,to:[String,Object]},setup(){const e=Et([]),t={};function n(n={}){const o=ig(),r=vt(Object.assign(Object.assign({},n),{key:o,destroy:()=>{var e;null===(e=t[`n-dialog-${o}`])||void 0===e||e.hide()}}));return e.value.push(r),r}const o=["info","success","warning","error"].map((e=>t=>n(Object.assign(Object.assign({},t),{type:e})))),r={create:n,destroyAll:function(){Object.values(t).forEach((e=>{null==e||e.hide()}))},info:o[0],success:o[1],warning:o[2],error:o[3]};return _o(CI,r),_o(xI,{clickedRef:Bb(64),clickedPositionRef:Ob()}),_o("n-dialog-reactive-list",e),Object.assign(Object.assign({},r),{dialogList:e,dialogInstRefs:t,handleAfterLeave:function(t){const{value:n}=e;n.splice(n.findIndex((e=>e.key===t)),1)}})},render(){var e,t;return li(yr,null,[this.dialogList.map((e=>li(OI,cg(e,["destroy","style"],{internalStyle:e.style,to:this.to,ref:t=>{null===t?delete this.dialogInstRefs[`n-dialog-${e.key}`]:this.dialogInstRefs[`n-dialog-${e.key}`]=t},internalKey:e.key,onInternalAfterLeave:this.handleAfterLeave})))),null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)])}});function FI(e){const{textColor1:t,dividerColor:n,fontWeightStrong:o}=e;return{textColor:t,color:n,fontWeight:o}}const II={name:"Divider",common:qz,self:FI},LI={name:"Divider",common:tz,self:FI},BI=nb("divider","\n position: relative;\n display: flex;\n width: 100%;\n box-sizing: border-box;\n font-size: 16px;\n color: var(--n-text-color);\n transition:\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n",[ib("vertical","\n margin-top: 24px;\n margin-bottom: 24px;\n ",[ib("no-title","\n display: flex;\n align-items: center;\n ")]),ob("title","\n display: flex;\n align-items: center;\n margin-left: 12px;\n margin-right: 12px;\n white-space: nowrap;\n font-weight: var(--n-font-weight);\n "),rb("title-position-left",[ob("line",[rb("left",{width:"28px"})])]),rb("title-position-right",[ob("line",[rb("right",{width:"28px"})])]),rb("dashed",[ob("line","\n background-color: #0000;\n height: 0px;\n width: 100%;\n border-style: dashed;\n border-width: 1px 0 0;\n ")]),rb("vertical","\n display: inline-block;\n height: 1em;\n margin: 0 8px;\n vertical-align: middle;\n width: 1px;\n "),ob("line","\n border: none;\n transition: background-color .3s var(--n-bezier), border-color .3s var(--n-bezier);\n height: 1px;\n width: 100%;\n margin: 0;\n "),ib("dashed",[ob("line",{backgroundColor:"var(--n-color)"})]),rb("dashed",[ob("line",{borderColor:"var(--n-color)"})]),rb("vertical",{backgroundColor:"var(--n-color)"})]),DI=zn({name:"Divider",props:Object.assign(Object.assign({},I_.props),{titlePlacement:{type:String,default:"center"},dashed:Boolean,vertical:Boolean}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Divider","-divider",BI,II,e,t),r=ai((()=>{const{common:{cubicBezierEaseInOut:e},self:{color:t,textColor:n,fontWeight:r}}=o.value;return{"--n-bezier":e,"--n-color":t,"--n-text-color":n,"--n-font-weight":r}})),i=n?KP("divider",void 0,r,e):void 0;return{mergedClsPrefix:t,cssVars:n?void 0:r,themeClass:null==i?void 0:i.themeClass,onRender:null==i?void 0:i.onRender}},render(){var e;const{$slots:t,titlePlacement:n,vertical:o,dashed:r,cssVars:i,mergedClsPrefix:a}=this;return null===(e=this.onRender)||void 0===e||e.call(this),li("div",{role:"separator",class:[`${a}-divider`,this.themeClass,{[`${a}-divider--vertical`]:o,[`${a}-divider--no-title`]:!t.default,[`${a}-divider--dashed`]:r,[`${a}-divider--title-position-${n}`]:t.default&&n}],style:i},o?null:li("div",{class:`${a}-divider__line ${a}-divider__line--left`}),!o&&t.default?li(yr,null,li("div",{class:`${a}-divider__title`},this.$slots),li("div",{class:`${a}-divider__line ${a}-divider__line--right`})):null)}});function $I(e){const{modalColor:t,textColor1:n,textColor2:o,boxShadow3:r,lineHeight:i,fontWeightStrong:a,dividerColor:l,closeColorHover:s,closeColorPressed:c,closeIconColor:u,closeIconColorHover:d,closeIconColorPressed:p,borderRadius:h,primaryColorHover:f}=e;return{bodyPadding:"16px 24px",borderRadius:h,headerPadding:"16px 24px",footerPadding:"16px 24px",color:t,textColor:o,titleTextColor:n,titleFontSize:"18px",titleFontWeight:a,boxShadow:r,lineHeight:i,headerBorderBottom:`1px solid ${l}`,footerBorderTop:`1px solid ${l}`,closeIconColor:u,closeIconColorHover:d,closeIconColorPressed:p,closeSize:"22px",closeIconSize:"18px",closeColorHover:s,closeColorPressed:c,closeBorderRadius:h,resizableTriggerColorHover:f}}const NI={name:"Drawer",common:qz,peers:{Scrollbar:tR},self:$I},jI={name:"Drawer",common:tz,peers:{Scrollbar:nR},self:$I},HI=zn({name:"NDrawerContent",inheritAttrs:!1,props:{blockScroll:Boolean,show:{type:Boolean,default:void 0},displayDirective:{type:String,required:!0},placement:{type:String,required:!0},contentClass:String,contentStyle:[Object,String],nativeScrollbar:{type:Boolean,required:!0},scrollbarProps:Object,trapFocus:{type:Boolean,default:!0},autoFocus:{type:Boolean,default:!0},showMask:{type:[Boolean,String],required:!0},maxWidth:Number,maxHeight:Number,minWidth:Number,minHeight:Number,resizable:Boolean,onClickoutside:Function,onAfterLeave:Function,onAfterEnter:Function,onEsc:Function},setup(e){const t=Et(!!e.show),n=Et(null),o=Po(Kb);let r=0,i="",a=null;const l=Et(!1),s=Et(!1),c=ai((()=>"top"===e.placement||"bottom"===e.placement)),{mergedClsPrefixRef:u,mergedRtlRef:d}=B_(e),p=GP("Drawer",d,u),h=g,{doUpdateHeight:f,doUpdateWidth:v}=o;function m(t){var o,i;if(s.value)if(c.value){let i=(null===(o=n.value)||void 0===o?void 0:o.offsetHeight)||0;const a=r-t.clientY;i+="bottom"===e.placement?a:-a,i=(t=>{const{maxHeight:n}=e;if(n&&t>n)return n;const{minHeight:o}=e;return o&&t{const{maxWidth:n}=e;if(n&&t>n)return n;const{minWidth:o}=e;return o&&t{e.show&&(t.value=!0)})),lr((()=>e.show),(e=>{e||g()})),Hn((()=>{g()}));const b=ai((()=>{const{show:t}=e,n=[[Mi,t]];return e.showMask||n.push([dy,e.onClickoutside,void 0,{capture:!0}]),n}));return Vx(ai((()=>e.blockScroll&&t.value))),_o(qb,n),_o(Gb,null),_o(Ub,null),{bodyRef:n,rtlEnabled:p,mergedClsPrefix:o.mergedClsPrefixRef,isMounted:o.isMountedRef,mergedTheme:o.mergedThemeRef,displayed:t,transitionName:ai((()=>({right:"slide-in-from-right-transition",left:"slide-in-from-left-transition",top:"slide-in-from-top-transition",bottom:"slide-in-from-bottom-transition"}[e.placement]))),handleAfterLeave:function(){var n;t.value=!1,null===(n=e.onAfterLeave)||void 0===n||n.call(e)},bodyDirectives:b,handleMousedownResizeTrigger:e=>{s.value=!0,r=c.value?e.clientY:e.clientX,i=document.body.style.cursor,document.body.style.cursor=c.value?"ns-resize":"ew-resize",document.body.addEventListener("mousemove",m),document.body.addEventListener("mouseleave",h),document.body.addEventListener("mouseup",g)},handleMouseenterResizeTrigger:()=>{null!==a&&(window.clearTimeout(a),a=null),s.value?l.value=!0:a=window.setTimeout((()=>{l.value=!0}),300)},handleMouseleaveResizeTrigger:()=>{null!==a&&(window.clearTimeout(a),a=null),l.value=!1},isDragging:s,isHoverOnResizeTrigger:l}},render(){const{$slots:e,mergedClsPrefix:t}=this;return"show"===this.displayDirective||this.displayed||this.show?fn(li("div",{role:"none"},li(Bx,{disabled:!this.showMask||!this.trapFocus,active:this.show,autoFocus:this.autoFocus,onEsc:this.onEsc},{default:()=>li(vi,{name:this.transitionName,appear:this.isMounted,onAfterEnter:this.onAfterEnter,onAfterLeave:this.handleAfterLeave},{default:()=>fn(li("div",Wr(this.$attrs,{role:"dialog",ref:"bodyRef","aria-modal":"true",class:[`${t}-drawer`,this.rtlEnabled&&`${t}-drawer--rtl`,`${t}-drawer--${this.placement}-placement`,this.isDragging&&`${t}-drawer--unselectable`,this.nativeScrollbar&&`${t}-drawer--native-scrollbar`]}),[this.resizable?li("div",{class:[`${t}-drawer__resize-trigger`,(this.isDragging||this.isHoverOnResizeTrigger)&&`${t}-drawer__resize-trigger--hover`],onMouseenter:this.handleMouseenterResizeTrigger,onMouseleave:this.handleMouseleaveResizeTrigger,onMousedown:this.handleMousedownResizeTrigger}):null,this.nativeScrollbar?li("div",{class:[`${t}-drawer-content-wrapper`,this.contentClass],style:this.contentStyle,role:"none"},e):li(lR,Object.assign({},this.scrollbarProps,{contentStyle:this.contentStyle,contentClass:[`${t}-drawer-content-wrapper`,this.contentClass],theme:this.mergedTheme.peers.Scrollbar,themeOverrides:this.mergedTheme.peerOverrides.Scrollbar}),e)]),this.bodyDirectives)})})),[[Mi,"if"===this.displayDirective||this.displayed||this.show]]):null}}),{cubicBezierEaseIn:WI,cubicBezierEaseOut:UI}=A_,{cubicBezierEaseIn:VI,cubicBezierEaseOut:qI}=A_,{cubicBezierEaseIn:KI,cubicBezierEaseOut:GI}=A_,{cubicBezierEaseIn:XI,cubicBezierEaseOut:YI}=A_,QI=eb([nb("drawer","\n word-break: break-word;\n line-height: var(--n-line-height);\n position: absolute;\n pointer-events: all;\n box-shadow: var(--n-box-shadow);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n background-color: var(--n-color);\n color: var(--n-text-color);\n box-sizing: border-box;\n ",[function({duration:e="0.3s",leaveDuration:t="0.2s",name:n="slide-in-from-right"}={}){return[eb(`&.${n}-transition-leave-active`,{transition:`transform ${t} ${WI}`}),eb(`&.${n}-transition-enter-active`,{transition:`transform ${e} ${UI}`}),eb(`&.${n}-transition-enter-to`,{transform:"translateX(0)"}),eb(`&.${n}-transition-enter-from`,{transform:"translateX(100%)"}),eb(`&.${n}-transition-leave-from`,{transform:"translateX(0)"}),eb(`&.${n}-transition-leave-to`,{transform:"translateX(100%)"})]}(),function({duration:e="0.3s",leaveDuration:t="0.2s",name:n="slide-in-from-left"}={}){return[eb(`&.${n}-transition-leave-active`,{transition:`transform ${t} ${VI}`}),eb(`&.${n}-transition-enter-active`,{transition:`transform ${e} ${qI}`}),eb(`&.${n}-transition-enter-to`,{transform:"translateX(0)"}),eb(`&.${n}-transition-enter-from`,{transform:"translateX(-100%)"}),eb(`&.${n}-transition-leave-from`,{transform:"translateX(0)"}),eb(`&.${n}-transition-leave-to`,{transform:"translateX(-100%)"})]}(),function({duration:e="0.3s",leaveDuration:t="0.2s",name:n="slide-in-from-top"}={}){return[eb(`&.${n}-transition-leave-active`,{transition:`transform ${t} ${KI}`}),eb(`&.${n}-transition-enter-active`,{transition:`transform ${e} ${GI}`}),eb(`&.${n}-transition-enter-to`,{transform:"translateY(0)"}),eb(`&.${n}-transition-enter-from`,{transform:"translateY(-100%)"}),eb(`&.${n}-transition-leave-from`,{transform:"translateY(0)"}),eb(`&.${n}-transition-leave-to`,{transform:"translateY(-100%)"})]}(),function({duration:e="0.3s",leaveDuration:t="0.2s",name:n="slide-in-from-bottom"}={}){return[eb(`&.${n}-transition-leave-active`,{transition:`transform ${t} ${XI}`}),eb(`&.${n}-transition-enter-active`,{transition:`transform ${e} ${YI}`}),eb(`&.${n}-transition-enter-to`,{transform:"translateY(0)"}),eb(`&.${n}-transition-enter-from`,{transform:"translateY(100%)"}),eb(`&.${n}-transition-leave-from`,{transform:"translateY(0)"}),eb(`&.${n}-transition-leave-to`,{transform:"translateY(100%)"})]}(),rb("unselectable","\n user-select: none; \n -webkit-user-select: none;\n "),rb("native-scrollbar",[nb("drawer-content-wrapper","\n overflow: auto;\n height: 100%;\n ")]),ob("resize-trigger","\n position: absolute;\n background-color: #0000;\n transition: background-color .3s var(--n-bezier);\n ",[rb("hover","\n background-color: var(--n-resize-trigger-color-hover);\n ")]),nb("drawer-content-wrapper","\n box-sizing: border-box;\n "),nb("drawer-content","\n height: 100%;\n display: flex;\n flex-direction: column;\n ",[rb("native-scrollbar",[nb("drawer-body-content-wrapper","\n height: 100%;\n overflow: auto;\n ")]),nb("drawer-body","\n flex: 1 0 0;\n overflow: hidden;\n "),nb("drawer-body-content-wrapper","\n box-sizing: border-box;\n padding: var(--n-body-padding);\n "),nb("drawer-header","\n font-weight: var(--n-title-font-weight);\n line-height: 1;\n font-size: var(--n-title-font-size);\n color: var(--n-title-text-color);\n padding: var(--n-header-padding);\n transition: border .3s var(--n-bezier);\n border-bottom: 1px solid var(--n-divider-color);\n border-bottom: var(--n-header-border-bottom);\n display: flex;\n justify-content: space-between;\n align-items: center;\n ",[ob("close","\n margin-left: 6px;\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n ")]),nb("drawer-footer","\n display: flex;\n justify-content: flex-end;\n border-top: var(--n-footer-border-top);\n transition: border .3s var(--n-bezier);\n padding: var(--n-footer-padding);\n ")]),rb("right-placement","\n top: 0;\n bottom: 0;\n right: 0;\n border-top-left-radius: var(--n-border-radius);\n border-bottom-left-radius: var(--n-border-radius);\n ",[ob("resize-trigger","\n width: 3px;\n height: 100%;\n top: 0;\n left: 0;\n transform: translateX(-1.5px);\n cursor: ew-resize;\n ")]),rb("left-placement","\n top: 0;\n bottom: 0;\n left: 0;\n border-top-right-radius: var(--n-border-radius);\n border-bottom-right-radius: var(--n-border-radius);\n ",[ob("resize-trigger","\n width: 3px;\n height: 100%;\n top: 0;\n right: 0;\n transform: translateX(1.5px);\n cursor: ew-resize;\n ")]),rb("top-placement","\n top: 0;\n left: 0;\n right: 0;\n border-bottom-left-radius: var(--n-border-radius);\n border-bottom-right-radius: var(--n-border-radius);\n ",[ob("resize-trigger","\n width: 100%;\n height: 3px;\n bottom: 0;\n left: 0;\n transform: translateY(1.5px);\n cursor: ns-resize;\n ")]),rb("bottom-placement","\n left: 0;\n bottom: 0;\n right: 0;\n border-top-left-radius: var(--n-border-radius);\n border-top-right-radius: var(--n-border-radius);\n ",[ob("resize-trigger","\n width: 100%;\n height: 3px;\n top: 0;\n left: 0;\n transform: translateY(-1.5px);\n cursor: ns-resize;\n ")])]),eb("body",[eb(">",[nb("drawer-container","\n position: fixed;\n ")])]),nb("drawer-container","\n position: relative;\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n pointer-events: none;\n ",[eb("> *","\n pointer-events: all;\n ")]),nb("drawer-mask","\n background-color: rgba(0, 0, 0, .3);\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ",[rb("invisible","\n background-color: rgba(0, 0, 0, 0)\n "),rR({enterDuration:"0.2s",leaveDuration:"0.2s",enterCubicBezier:"var(--n-bezier-in)",leaveCubicBezier:"var(--n-bezier-out)"})])]),ZI=zn({name:"Drawer",inheritAttrs:!1,props:Object.assign(Object.assign({},I_.props),{show:Boolean,width:[Number,String],height:[Number,String],placement:{type:String,default:"right"},maskClosable:{type:Boolean,default:!0},showMask:{type:[Boolean,String],default:!0},to:[String,Object],displayDirective:{type:String,default:"if"},nativeScrollbar:{type:Boolean,default:!0},zIndex:Number,onMaskClick:Function,scrollbarProps:Object,contentClass:String,contentStyle:[Object,String],trapFocus:{type:Boolean,default:!0},onEsc:Function,autoFocus:{type:Boolean,default:!0},closeOnEsc:{type:Boolean,default:!0},blockScroll:{type:Boolean,default:!0},maxWidth:Number,maxHeight:Number,minWidth:Number,minHeight:Number,resizable:Boolean,defaultWidth:{type:[Number,String],default:251},defaultHeight:{type:[Number,String],default:251},onUpdateWidth:[Function,Array],onUpdateHeight:[Function,Array],"onUpdate:width":[Function,Array],"onUpdate:height":[Function,Array],"onUpdate:show":[Function,Array],onUpdateShow:[Function,Array],onAfterEnter:Function,onAfterLeave:Function,drawerStyle:[String,Object],drawerClass:String,target:null,onShow:Function,onHide:Function}),setup(e){const{mergedClsPrefixRef:t,namespaceRef:n,inlineThemeDisabled:o}=B_(e),r=$b(),i=I_("Drawer","-drawer",QI,NI,e,t),a=Et(e.defaultWidth),l=Et(e.defaultHeight),s=Db(jt(e,"width"),a),c=Db(jt(e,"height"),l),u=ai((()=>{const{placement:t}=e;return"top"===t||"bottom"===t?"":Ag(s.value)})),d=ai((()=>{const{placement:t}=e;return"left"===t||"right"===t?"":Ag(c.value)})),p=ai((()=>[{width:u.value,height:d.value},e.drawerStyle||""]));function h(t){const{onMaskClick:n,maskClosable:o}=e;o&&v(!1),n&&n(t)}const f=Yx();function v(t){const{onHide:n,onUpdateShow:o,"onUpdate:show":r}=e;o&&dg(o,t),r&&dg(r,t),n&&!t&&dg(n,t)}_o(Kb,{isMountedRef:r,mergedThemeRef:i,mergedClsPrefixRef:t,doUpdateShow:v,doUpdateHeight:t=>{const{onUpdateHeight:n,"onUpdate:width":o}=e;n&&dg(n,t),o&&dg(o,t),l.value=t},doUpdateWidth:t=>{const{onUpdateWidth:n,"onUpdate:width":o}=e;n&&dg(n,t),o&&dg(o,t),a.value=t}});const m=ai((()=>{const{common:{cubicBezierEaseInOut:e,cubicBezierEaseIn:t,cubicBezierEaseOut:n},self:{color:o,textColor:r,boxShadow:a,lineHeight:l,headerPadding:s,footerPadding:c,borderRadius:u,bodyPadding:d,titleFontSize:p,titleTextColor:h,titleFontWeight:f,headerBorderBottom:v,footerBorderTop:m,closeIconColor:g,closeIconColorHover:b,closeIconColorPressed:y,closeColorHover:x,closeColorPressed:C,closeIconSize:w,closeSize:k,closeBorderRadius:S,resizableTriggerColorHover:_}}=i.value;return{"--n-line-height":l,"--n-color":o,"--n-border-radius":u,"--n-text-color":r,"--n-box-shadow":a,"--n-bezier":e,"--n-bezier-out":n,"--n-bezier-in":t,"--n-header-padding":s,"--n-body-padding":d,"--n-footer-padding":c,"--n-title-text-color":h,"--n-title-font-size":p,"--n-title-font-weight":f,"--n-header-border-bottom":v,"--n-footer-border-top":m,"--n-close-icon-color":g,"--n-close-icon-color-hover":b,"--n-close-icon-color-pressed":y,"--n-close-size":k,"--n-close-color-hover":x,"--n-close-color-pressed":C,"--n-close-icon-size":w,"--n-close-border-radius":S,"--n-resize-trigger-color-hover":_}})),g=o?KP("drawer",void 0,m,e):void 0;return{mergedClsPrefix:t,namespace:n,mergedBodyStyle:p,handleOutsideClick:function(e){h(e)},handleMaskClick:h,handleEsc:function(t){var n;null===(n=e.onEsc)||void 0===n||n.call(e),e.show&&e.closeOnEsc&&fb(t)&&(f.value||v(!1))},mergedTheme:i,cssVars:o?void 0:m,themeClass:null==g?void 0:g.themeClass,onRender:null==g?void 0:g.onRender,isMounted:r}},render(){const{mergedClsPrefix:e}=this;return li(Sy,{to:this.to,show:this.show},{default:()=>{var t;return null===(t=this.onRender)||void 0===t||t.call(this),fn(li("div",{class:[`${e}-drawer-container`,this.namespace,this.themeClass],style:this.cssVars,role:"none"},this.showMask?li(vi,{name:"fade-in-transition",appear:this.isMounted},{default:()=>this.show?li("div",{"aria-hidden":!0,class:[`${e}-drawer-mask`,"transparent"===this.showMask&&`${e}-drawer-mask--invisible`],onClick:this.handleMaskClick}):null}):null,li(HI,Object.assign({},this.$attrs,{class:[this.drawerClass,this.$attrs.class],style:[this.mergedBodyStyle,this.$attrs.style],blockScroll:this.blockScroll,contentStyle:this.contentStyle,contentClass:this.contentClass,placement:this.placement,scrollbarProps:this.scrollbarProps,show:this.show,displayDirective:this.displayDirective,nativeScrollbar:this.nativeScrollbar,onAfterEnter:this.onAfterEnter,onAfterLeave:this.onAfterLeave,trapFocus:this.trapFocus,autoFocus:this.autoFocus,resizable:this.resizable,maxHeight:this.maxHeight,minHeight:this.minHeight,maxWidth:this.maxWidth,minWidth:this.minWidth,showMask:this.showMask,onEsc:this.handleEsc,onClickoutside:this.handleOutsideClick}),this.$slots)),[[fy,{zIndex:this.zIndex,enabled:this.show}]])}})}}),JI=zn({name:"DrawerContent",props:{title:String,headerClass:String,headerStyle:[Object,String],footerClass:String,footerStyle:[Object,String],bodyClass:String,bodyStyle:[Object,String],bodyContentClass:String,bodyContentStyle:[Object,String],nativeScrollbar:{type:Boolean,default:!0},scrollbarProps:Object,closable:Boolean},setup(){const e=Po(Kb,null);e||fg("drawer-content","`n-drawer-content` must be placed inside `n-drawer`.");const{doUpdateShow:t}=e;return{handleCloseClick:function(){t(!1)},mergedTheme:e.mergedThemeRef,mergedClsPrefix:e.mergedClsPrefixRef}},render(){const{title:e,mergedClsPrefix:t,nativeScrollbar:n,mergedTheme:o,bodyClass:r,bodyStyle:i,bodyContentClass:a,bodyContentStyle:l,headerClass:s,headerStyle:c,footerClass:u,footerStyle:d,scrollbarProps:p,closable:h,$slots:f}=this;return li("div",{role:"none",class:[`${t}-drawer-content`,n&&`${t}-drawer-content--native-scrollbar`]},f.header||e||h?li("div",{class:[`${t}-drawer-header`,s],style:c,role:"none"},li("div",{class:`${t}-drawer-header__main`,role:"heading","aria-level":"1"},void 0!==f.header?f.header():e),h&&li(kT,{onClick:this.handleCloseClick,clsPrefix:t,class:`${t}-drawer-header__close`,absolute:!0})):null,n?li("div",{class:[`${t}-drawer-body`,r],style:i,role:"none"},li("div",{class:[`${t}-drawer-body-content-wrapper`,a],style:l,role:"none"},f)):li(lR,Object.assign({themeOverrides:o.peerOverrides.Scrollbar,theme:o.peers.Scrollbar},p,{class:`${t}-drawer-body`,contentClass:[`${t}-drawer-body-content-wrapper`,a],contentStyle:l}),f),f.footer?li("div",{class:[`${t}-drawer-footer`,u],style:d,role:"none"},f.footer()):null)}}),eL={actionMargin:"0 0 0 20px",actionMarginRtl:"0 20px 0 0"},tL={name:"DynamicInput",common:tz,peers:{Input:xE,Button:eO},self:()=>eL},nL={gapSmall:"4px 8px",gapMedium:"8px 12px",gapLarge:"12px 16px"},oL={name:"Space",self:()=>nL},rL={name:"Space",self:function(){return nL}};let iL;function aL(){if(!pb)return!0;if(void 0===iL){const e=document.createElement("div");e.style.display="flex",e.style.flexDirection="column",e.style.rowGap="1px",e.appendChild(document.createElement("div")),e.appendChild(document.createElement("div")),document.body.appendChild(e);const t=1===e.scrollHeight;return document.body.removeChild(e),iL=t}return iL}const lL=zn({name:"Space",props:Object.assign(Object.assign({},I_.props),{align:String,justify:{type:String,default:"start"},inline:Boolean,vertical:Boolean,reverse:Boolean,size:{type:[String,Number,Array],default:"medium"},wrapItem:{type:Boolean,default:!0},itemClass:String,itemStyle:[String,Object],wrap:{type:Boolean,default:!0},internalUseGap:{type:Boolean,default:void 0}}),setup(e){const{mergedClsPrefixRef:t,mergedRtlRef:n}=B_(e),o=I_("Space","-space",void 0,rL,e,t),r=GP("Space",n,t);return{useGap:aL(),rtlEnabled:r,mergedClsPrefix:t,margin:ai((()=>{const{size:t}=e;if(Array.isArray(t))return{horizontal:t[0],vertical:t[1]};if("number"==typeof t)return{horizontal:t,vertical:t};const{self:{[ub("gap",t)]:n}}=o.value,{row:r,col:i}=function(e,t){const[n,o]=e.split(" ");return t?"row"===t?n:o:{row:n,col:o||n}}(n);return{horizontal:Im(i),vertical:Im(r)}}))}},render(){const{vertical:e,reverse:t,align:n,inline:o,justify:r,itemClass:i,itemStyle:a,margin:l,wrap:s,mergedClsPrefix:c,rtlEnabled:u,useGap:d,wrapItem:p,internalUseGap:h}=this,f=ug(lg(this),!1);if(!f.length)return null;const v=`${l.horizontal}px`,m=l.horizontal/2+"px",g=`${l.vertical}px`,b=l.vertical/2+"px",y=f.length-1,x=r.startsWith("space-");return li("div",{role:"none",class:[`${c}-space`,u&&`${c}-space--rtl`],style:{display:o?"inline-flex":"flex",flexDirection:e&&!t?"column":e&&t?"column-reverse":!e&&t?"row-reverse":"row",justifyContent:["start","end"].includes(r)?`flex-${r}`:r,flexWrap:!s||e?"nowrap":"wrap",marginTop:d||e?"":`-${b}`,marginBottom:d||e?"":`-${b}`,alignItems:n,gap:d?`${l.vertical}px ${l.horizontal}px`:""}},p||!d&&!h?f.map(((t,n)=>t.type===Cr?t:li("div",{role:"none",class:i,style:[a,{maxWidth:"100%"},d?"":e?{marginBottom:n!==y?g:""}:u?{marginLeft:x?"space-between"===r&&n===y?"":m:n!==y?v:"",marginRight:x?"space-between"===r&&0===n?"":m:"",paddingTop:b,paddingBottom:b}:{marginRight:x?"space-between"===r&&n===y?"":m:n!==y?v:"",marginLeft:x?"space-between"===r&&0===n?"":m:"",paddingTop:b,paddingBottom:b}]},t))):f)}}),sL={name:"DynamicTags",common:tz,peers:{Input:xE,Button:eO,Tag:jR,Space:oL},self:()=>({inputWidth:"64px"})},cL={name:"Element",common:tz},uL={gapSmall:"4px 8px",gapMedium:"8px 12px",gapLarge:"12px 16px"},dL={name:"Flex",self:()=>uL},pL={feedbackPadding:"4px 0 0 2px",feedbackHeightSmall:"24px",feedbackHeightMedium:"24px",feedbackHeightLarge:"26px",feedbackFontSizeSmall:"13px",feedbackFontSizeMedium:"14px",feedbackFontSizeLarge:"14px",labelFontSizeLeftSmall:"14px",labelFontSizeLeftMedium:"14px",labelFontSizeLeftLarge:"15px",labelFontSizeTopSmall:"13px",labelFontSizeTopMedium:"14px",labelFontSizeTopLarge:"14px",labelHeightSmall:"24px",labelHeightMedium:"26px",labelHeightLarge:"28px",labelPaddingVertical:"0 0 6px 2px",labelPaddingHorizontal:"0 12px 0 0",labelTextAlignVertical:"left",labelTextAlignHorizontal:"right",labelFontWeight:"400"},hL={name:"Form",common:tz,self:function(e){const{heightSmall:t,heightMedium:n,heightLarge:o,textColor1:r,errorColor:i,warningColor:a,lineHeight:l,textColor3:s}=e;return Object.assign(Object.assign({},pL),{blankHeightSmall:t,blankHeightMedium:n,blankHeightLarge:o,lineHeight:l,labelTextColor:r,asteriskColor:i,feedbackTextColorError:i,feedbackTextColorWarning:a,feedbackTextColor:s})}},fL={closeMargin:"16px 12px",closeSize:"20px",closeIconSize:"16px",width:"365px",padding:"16px",titleFontSize:"16px",metaFontSize:"12px",descriptionFontSize:"12px"};function vL(e){const{textColor2:t,successColor:n,infoColor:o,warningColor:r,errorColor:i,popoverColor:a,closeIconColor:l,closeIconColorHover:s,closeIconColorPressed:c,closeColorHover:u,closeColorPressed:d,textColor1:p,textColor3:h,borderRadius:f,fontWeightStrong:v,boxShadow2:m,lineHeight:g,fontSize:b}=e;return Object.assign(Object.assign({},fL),{borderRadius:f,lineHeight:g,fontSize:b,headerFontWeight:v,iconColor:t,iconColorSuccess:n,iconColorInfo:o,iconColorWarning:r,iconColorError:i,color:a,textColor:t,closeIconColor:l,closeIconColorHover:s,closeIconColorPressed:c,closeBorderRadius:f,closeColorHover:u,closeColorPressed:d,headerTextColor:p,descriptionTextColor:h,actionTextColor:t,boxShadow:m})}const mL={name:"Notification",common:qz,peers:{Scrollbar:tR},self:vL},gL={name:"Notification",common:tz,peers:{Scrollbar:nR},self:vL},bL={margin:"0 0 8px 0",padding:"10px 20px",maxWidth:"720px",minWidth:"420px",iconMargin:"0 10px 0 0",closeMargin:"0 0 0 10px",closeSize:"20px",closeIconSize:"16px",iconSize:"20px",fontSize:"14px"};function yL(e){const{textColor2:t,closeIconColor:n,closeIconColorHover:o,closeIconColorPressed:r,infoColor:i,successColor:a,errorColor:l,warningColor:s,popoverColor:c,boxShadow2:u,primaryColor:d,lineHeight:p,borderRadius:h,closeColorHover:f,closeColorPressed:v}=e;return Object.assign(Object.assign({},bL),{closeBorderRadius:h,textColor:t,textColorInfo:t,textColorSuccess:t,textColorError:t,textColorWarning:t,textColorLoading:t,color:c,colorInfo:c,colorSuccess:c,colorError:c,colorWarning:c,colorLoading:c,boxShadow:u,boxShadowInfo:u,boxShadowSuccess:u,boxShadowError:u,boxShadowWarning:u,boxShadowLoading:u,iconColor:t,iconColorInfo:i,iconColorSuccess:a,iconColorWarning:s,iconColorError:l,iconColorLoading:d,closeColorHover:f,closeColorPressed:v,closeIconColor:n,closeIconColorHover:o,closeIconColorPressed:r,closeColorHoverInfo:f,closeColorPressedInfo:v,closeIconColorInfo:n,closeIconColorHoverInfo:o,closeIconColorPressedInfo:r,closeColorHoverSuccess:f,closeColorPressedSuccess:v,closeIconColorSuccess:n,closeIconColorHoverSuccess:o,closeIconColorPressedSuccess:r,closeColorHoverError:f,closeColorPressedError:v,closeIconColorError:n,closeIconColorHoverError:o,closeIconColorPressedError:r,closeColorHoverWarning:f,closeColorPressedWarning:v,closeIconColorWarning:n,closeIconColorHoverWarning:o,closeIconColorPressedWarning:r,closeColorHoverLoading:f,closeColorPressedLoading:v,closeIconColorLoading:n,closeIconColorHoverLoading:o,closeIconColorPressedLoading:r,loadingColor:d,lineHeight:p,borderRadius:h})}const xL={name:"Message",common:qz,self:yL},CL={name:"Message",common:tz,self:yL},wL={name:"ButtonGroup",common:tz},kL={name:"GradientText",common:tz,self(e){const{primaryColor:t,successColor:n,warningColor:o,errorColor:r,infoColor:i,primaryColorSuppl:a,successColorSuppl:l,warningColorSuppl:s,errorColorSuppl:c,infoColorSuppl:u,fontWeightStrong:d}=e;return{fontWeight:d,rotate:"252deg",colorStartPrimary:t,colorEndPrimary:a,colorStartInfo:i,colorEndInfo:u,colorStartWarning:o,colorEndWarning:s,colorStartError:r,colorEndError:c,colorStartSuccess:n,colorEndSuccess:l}}},SL={name:"InputNumber",common:tz,peers:{Button:eO,Input:xE},self(e){const{textColorDisabled:t}=e;return{iconColorDisabled:t}}},_L={name:"InputNumber",common:qz,peers:{Button:JE,Input:CE},self:function(e){const{textColorDisabled:t}=e;return{iconColorDisabled:t}}},PL={name:"Layout",common:tz,peers:{Scrollbar:nR},self(e){const{textColor2:t,bodyColor:n,popoverColor:o,cardColor:r,dividerColor:i,scrollbarColor:a,scrollbarColorHover:l}=e;return{textColor:t,textColorInverted:t,color:n,colorEmbedded:n,headerColor:r,headerColorInverted:r,footerColor:r,footerColorInverted:r,headerBorderColor:i,headerBorderColorInverted:i,footerBorderColor:i,footerBorderColorInverted:i,siderBorderColor:i,siderBorderColorInverted:i,siderColor:r,siderColorInverted:r,siderToggleButtonBorder:"1px solid transparent",siderToggleButtonColor:o,siderToggleButtonIconColor:t,siderToggleButtonIconColorInverted:t,siderToggleBarColor:eg(n,a),siderToggleBarColorHover:eg(n,l),__invertScrollbar:"false"}}},TL={name:"Layout",common:qz,peers:{Scrollbar:tR},self:function(e){const{baseColor:t,textColor2:n,bodyColor:o,cardColor:r,dividerColor:i,actionColor:a,scrollbarColor:l,scrollbarColorHover:s,invertedColor:c}=e;return{textColor:n,textColorInverted:"#FFF",color:o,colorEmbedded:a,headerColor:r,headerColorInverted:c,footerColor:a,footerColorInverted:c,headerBorderColor:i,headerBorderColorInverted:c,footerBorderColor:i,footerBorderColorInverted:c,siderBorderColor:i,siderBorderColorInverted:c,siderColor:r,siderColorInverted:c,siderToggleButtonBorder:`1px solid ${i}`,siderToggleButtonColor:t,siderToggleButtonIconColor:n,siderToggleButtonIconColorInverted:n,siderToggleBarColor:eg(o,l),siderToggleBarColorHover:eg(o,s),__invertScrollbar:"true"}}};function AL(e){const{textColor2:t,cardColor:n,modalColor:o,popoverColor:r,dividerColor:i,borderRadius:a,fontSize:l,hoverColor:s}=e;return{textColor:t,color:n,colorHover:s,colorModal:o,colorHoverModal:eg(o,s),colorPopover:r,colorHoverPopover:eg(r,s),borderColor:i,borderColorModal:eg(o,i),borderColorPopover:eg(r,i),borderRadius:a,fontSize:l}}const zL={name:"List",common:qz,self:AL},RL={name:"List",common:tz,self:AL},EL={name:"LoadingBar",common:tz,self(e){const{primaryColor:t}=e;return{colorError:"red",colorLoading:t,height:"2px"}}},OL={name:"LoadingBar",common:qz,self:function(e){const{primaryColor:t,errorColor:n}=e;return{colorError:n,colorLoading:t,height:"2px"}}},ML={name:"Log",common:tz,peers:{Scrollbar:nR,Code:XO},self(e){const{textColor2:t,inputColor:n,fontSize:o,primaryColor:r}=e;return{loaderFontSize:o,loaderTextColor:t,loaderColor:n,loaderBorder:"1px solid #0000",loadingColor:r}}},FL={name:"Mention",common:tz,peers:{InternalSelectMenu:pR,Input:xE},self(e){const{boxShadow2:t}=e;return{menuBoxShadow:t}}};function IL(e){const{borderRadius:t,textColor3:n,primaryColor:o,textColor2:r,textColor1:i,fontSize:a,dividerColor:l,hoverColor:s,primaryColorHover:c}=e;return Object.assign({borderRadius:t,color:"#0000",groupTextColor:n,itemColorHover:s,itemColorActive:tg(o,{alpha:.1}),itemColorActiveHover:tg(o,{alpha:.1}),itemColorActiveCollapsed:tg(o,{alpha:.1}),itemTextColor:r,itemTextColorHover:r,itemTextColorActive:o,itemTextColorActiveHover:o,itemTextColorChildActive:o,itemTextColorChildActiveHover:o,itemTextColorHorizontal:r,itemTextColorHoverHorizontal:c,itemTextColorActiveHorizontal:o,itemTextColorActiveHoverHorizontal:o,itemTextColorChildActiveHorizontal:o,itemTextColorChildActiveHoverHorizontal:o,itemIconColor:i,itemIconColorHover:i,itemIconColorActive:o,itemIconColorActiveHover:o,itemIconColorChildActive:o,itemIconColorChildActiveHover:o,itemIconColorCollapsed:i,itemIconColorHorizontal:i,itemIconColorHoverHorizontal:c,itemIconColorActiveHorizontal:o,itemIconColorActiveHoverHorizontal:o,itemIconColorChildActiveHorizontal:o,itemIconColorChildActiveHoverHorizontal:o,itemHeight:"42px",arrowColor:r,arrowColorHover:r,arrowColorActive:o,arrowColorActiveHover:o,arrowColorChildActive:o,arrowColorChildActiveHover:o,colorInverted:"#0000",borderColorHorizontal:"#0000",fontSize:a,dividerColor:l},{itemColorHoverInverted:"#0000",itemColorActiveInverted:d=o,itemColorActiveHoverInverted:d,itemColorActiveCollapsedInverted:d,itemTextColorInverted:u="#BBB",itemTextColorHoverInverted:p="#FFF",itemTextColorChildActiveInverted:p,itemTextColorChildActiveHoverInverted:p,itemTextColorActiveInverted:p,itemTextColorActiveHoverInverted:p,itemTextColorHorizontalInverted:u,itemTextColorHoverHorizontalInverted:p,itemTextColorChildActiveHorizontalInverted:p,itemTextColorChildActiveHoverHorizontalInverted:p,itemTextColorActiveHorizontalInverted:p,itemTextColorActiveHoverHorizontalInverted:p,itemIconColorInverted:u,itemIconColorHoverInverted:p,itemIconColorActiveInverted:p,itemIconColorActiveHoverInverted:p,itemIconColorChildActiveInverted:p,itemIconColorChildActiveHoverInverted:p,itemIconColorCollapsedInverted:u,itemIconColorHorizontalInverted:u,itemIconColorHoverHorizontalInverted:p,itemIconColorActiveHorizontalInverted:p,itemIconColorActiveHoverHorizontalInverted:p,itemIconColorChildActiveHorizontalInverted:p,itemIconColorChildActiveHoverHorizontalInverted:p,arrowColorInverted:u,arrowColorHoverInverted:p,arrowColorActiveInverted:p,arrowColorActiveHoverInverted:p,arrowColorChildActiveInverted:p,arrowColorChildActiveHoverInverted:p,groupTextColorInverted:"#AAA"});var u,d,p}const LL={name:"Menu",common:qz,peers:{Tooltip:PM,Dropdown:FM},self:IL},BL={name:"Menu",common:tz,peers:{Tooltip:_M,Dropdown:IM},self(e){const{primaryColor:t,primaryColorSuppl:n}=e,o=IL(e);return o.itemColorActive=tg(t,{alpha:.15}),o.itemColorActiveHover=tg(t,{alpha:.15}),o.itemColorActiveCollapsed=tg(t,{alpha:.15}),o.itemColorActiveInverted=n,o.itemColorActiveHoverInverted=n,o.itemColorActiveCollapsedInverted=n,o}},DL={titleFontSize:"18px",backSize:"22px"},$L={name:"PageHeader",common:tz,self:function(e){const{textColor1:t,textColor2:n,textColor3:o,fontSize:r,fontWeightStrong:i,primaryColorHover:a,primaryColorPressed:l}=e;return Object.assign(Object.assign({},DL),{titleFontWeight:i,fontSize:r,titleTextColor:t,backColor:n,backColorHover:a,backColorPressed:l,subtitleTextColor:o})}},NL={iconSize:"22px"},jL={name:"Popconfirm",common:tz,peers:{Button:eO,Popover:_R},self:function(e){const{fontSize:t,warningColor:n}=e;return Object.assign(Object.assign({},NL),{fontSize:t,iconColor:n})}};function HL(e){const{infoColor:t,successColor:n,warningColor:o,errorColor:r,textColor2:i,progressRailColor:a,fontSize:l,fontWeight:s}=e;return{fontSize:l,fontSizeCircle:"28px",fontWeightCircle:s,railColor:a,railHeight:"8px",iconSizeCircle:"36px",iconSizeLine:"18px",iconColor:t,iconColorInfo:t,iconColorSuccess:n,iconColorWarning:o,iconColorError:r,textColorCircle:i,textColorLineInner:"rgb(255, 255, 255)",textColorLineOuter:i,fillColor:t,fillColorInfo:t,fillColorSuccess:n,fillColorWarning:o,fillColorError:r,lineBgProcessing:"linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .5) 100%)"}}const WL={name:"Progress",common:qz,self:HL},UL={name:"Progress",common:tz,self(e){const t=HL(e);return t.textColorLineInner="rgb(0, 0, 0)",t.lineBgProcessing="linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .5) 100%)",t}},VL={name:"Rate",common:tz,self(e){const{railColor:t}=e;return{itemColor:t,itemColorActive:"#CCAA33",itemSize:"20px",sizeSmall:"16px",sizeMedium:"20px",sizeLarge:"24px"}}},qL={titleFontSizeSmall:"26px",titleFontSizeMedium:"32px",titleFontSizeLarge:"40px",titleFontSizeHuge:"48px",fontSizeSmall:"14px",fontSizeMedium:"14px",fontSizeLarge:"15px",fontSizeHuge:"16px",iconSizeSmall:"64px",iconSizeMedium:"80px",iconSizeLarge:"100px",iconSizeHuge:"125px",iconColor418:void 0,iconColor404:void 0,iconColor403:void 0,iconColor500:void 0};function KL(e){const{textColor2:t,textColor1:n,errorColor:o,successColor:r,infoColor:i,warningColor:a,lineHeight:l,fontWeightStrong:s}=e;return Object.assign(Object.assign({},qL),{lineHeight:l,titleFontWeight:s,titleTextColor:n,textColor:t,iconColorError:o,iconColorSuccess:r,iconColorInfo:i,iconColorWarning:a})}const GL={name:"Result",common:qz,self:KL},XL={name:"Result",common:tz,self:KL},YL={railHeight:"4px",railWidthVertical:"4px",handleSize:"18px",dotHeight:"8px",dotWidth:"8px",dotBorderRadius:"4px"},QL={name:"Slider",common:tz,self(e){const{railColor:t,modalColor:n,primaryColorSuppl:o,popoverColor:r,textColor2:i,cardColor:a,borderRadius:l,fontSize:s,opacityDisabled:c}=e;return Object.assign(Object.assign({},YL),{fontSize:s,markFontSize:s,railColor:t,railColorHover:t,fillColor:o,fillColorHover:o,opacityDisabled:c,handleColor:"#FFF",dotColor:a,dotColorModal:n,dotColorPopover:r,handleBoxShadow:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",handleBoxShadowHover:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",handleBoxShadowActive:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",handleBoxShadowFocus:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",indicatorColor:r,indicatorBoxShadow:"0 2px 8px 0 rgba(0, 0, 0, 0.12)",indicatorTextColor:i,indicatorBorderRadius:l,dotBorder:`2px solid ${t}`,dotBorderActive:`2px solid ${o}`,dotBoxShadow:""})}};function ZL(e){const{opacityDisabled:t,heightTiny:n,heightSmall:o,heightMedium:r,heightLarge:i,heightHuge:a,primaryColor:l,fontSize:s}=e;return{fontSize:s,textColor:l,sizeTiny:n,sizeSmall:o,sizeMedium:r,sizeLarge:i,sizeHuge:a,color:l,opacitySpinning:t}}const JL={name:"Spin",common:qz,self:ZL},eB={name:"Spin",common:tz,self:ZL},tB={name:"Statistic",common:tz,self:function(e){const{textColor2:t,textColor3:n,fontSize:o,fontWeight:r}=e;return{labelFontSize:o,labelFontWeight:r,valueFontWeight:r,valueFontSize:"24px",labelTextColor:n,valuePrefixTextColor:t,valueSuffixTextColor:t,valueTextColor:t}}},nB={stepHeaderFontSizeSmall:"14px",stepHeaderFontSizeMedium:"16px",indicatorIndexFontSizeSmall:"14px",indicatorIndexFontSizeMedium:"16px",indicatorSizeSmall:"22px",indicatorSizeMedium:"28px",indicatorIconSizeSmall:"14px",indicatorIconSizeMedium:"18px"},oB={name:"Steps",common:tz,self:function(e){const{fontWeightStrong:t,baseColor:n,textColorDisabled:o,primaryColor:r,errorColor:i,textColor1:a,textColor2:l}=e;return Object.assign(Object.assign({},nB),{stepHeaderFontWeight:t,indicatorTextColorProcess:n,indicatorTextColorWait:o,indicatorTextColorFinish:r,indicatorTextColorError:i,indicatorBorderColorProcess:r,indicatorBorderColorWait:o,indicatorBorderColorFinish:r,indicatorBorderColorError:i,indicatorColorProcess:r,indicatorColorWait:"#0000",indicatorColorFinish:"#0000",indicatorColorError:"#0000",splitorColorProcess:o,splitorColorWait:o,splitorColorFinish:r,splitorColorError:o,headerTextColorProcess:a,headerTextColorWait:o,headerTextColorFinish:o,headerTextColorError:i,descriptionTextColorProcess:l,descriptionTextColorWait:o,descriptionTextColorFinish:o,descriptionTextColorError:i})}},rB={buttonHeightSmall:"14px",buttonHeightMedium:"18px",buttonHeightLarge:"22px",buttonWidthSmall:"14px",buttonWidthMedium:"18px",buttonWidthLarge:"22px",buttonWidthPressedSmall:"20px",buttonWidthPressedMedium:"24px",buttonWidthPressedLarge:"28px",railHeightSmall:"18px",railHeightMedium:"22px",railHeightLarge:"26px",railWidthSmall:"32px",railWidthMedium:"40px",railWidthLarge:"48px"},iB={name:"Switch",common:tz,self(e){const{primaryColorSuppl:t,opacityDisabled:n,borderRadius:o,primaryColor:r,textColor2:i,baseColor:a}=e;return Object.assign(Object.assign({},rB),{iconColor:a,textColor:i,loadingColor:t,opacityDisabled:n,railColor:"rgba(255, 255, 255, .20)",railColorActive:t,buttonBoxShadow:"0px 2px 4px 0 rgba(0, 0, 0, 0.4)",buttonColor:"#FFF",railBorderRadiusSmall:o,railBorderRadiusMedium:o,railBorderRadiusLarge:o,buttonBorderRadiusSmall:o,buttonBorderRadiusMedium:o,buttonBorderRadiusLarge:o,boxShadowFocus:`0 0 8px 0 ${tg(r,{alpha:.3})}`})}},aB={name:"Switch",common:qz,self:function(e){const{primaryColor:t,opacityDisabled:n,borderRadius:o,textColor3:r}=e;return Object.assign(Object.assign({},rB),{iconColor:r,textColor:"white",loadingColor:t,opacityDisabled:n,railColor:"rgba(0, 0, 0, .14)",railColorActive:t,buttonBoxShadow:"0 1px 4px 0 rgba(0, 0, 0, 0.3), inset 0 0 1px 0 rgba(0, 0, 0, 0.05)",buttonColor:"#FFF",railBorderRadiusSmall:o,railBorderRadiusMedium:o,railBorderRadiusLarge:o,buttonBorderRadiusSmall:o,buttonBorderRadiusMedium:o,buttonBorderRadiusLarge:o,boxShadowFocus:`0 0 0 2px ${tg(t,{alpha:.2})}`})}},lB={thPaddingSmall:"6px",thPaddingMedium:"12px",thPaddingLarge:"12px",tdPaddingSmall:"6px",tdPaddingMedium:"12px",tdPaddingLarge:"12px"},sB={name:"Table",common:tz,self:function(e){const{dividerColor:t,cardColor:n,modalColor:o,popoverColor:r,tableHeaderColor:i,tableColorStriped:a,textColor1:l,textColor2:s,borderRadius:c,fontWeightStrong:u,lineHeight:d,fontSizeSmall:p,fontSizeMedium:h,fontSizeLarge:f}=e;return Object.assign(Object.assign({},lB),{fontSizeSmall:p,fontSizeMedium:h,fontSizeLarge:f,lineHeight:d,borderRadius:c,borderColor:eg(n,t),borderColorModal:eg(o,t),borderColorPopover:eg(r,t),tdColor:n,tdColorModal:o,tdColorPopover:r,tdColorStriped:eg(n,a),tdColorStripedModal:eg(o,a),tdColorStripedPopover:eg(r,a),thColor:eg(n,i),thColorModal:eg(o,i),thColorPopover:eg(r,i),thTextColor:l,tdTextColor:s,thFontWeight:u})}},cB={tabFontSizeSmall:"14px",tabFontSizeMedium:"14px",tabFontSizeLarge:"16px",tabGapSmallLine:"36px",tabGapMediumLine:"36px",tabGapLargeLine:"36px",tabGapSmallLineVertical:"8px",tabGapMediumLineVertical:"8px",tabGapLargeLineVertical:"8px",tabPaddingSmallLine:"6px 0",tabPaddingMediumLine:"10px 0",tabPaddingLargeLine:"14px 0",tabPaddingVerticalSmallLine:"6px 12px",tabPaddingVerticalMediumLine:"8px 16px",tabPaddingVerticalLargeLine:"10px 20px",tabGapSmallBar:"36px",tabGapMediumBar:"36px",tabGapLargeBar:"36px",tabGapSmallBarVertical:"8px",tabGapMediumBarVertical:"8px",tabGapLargeBarVertical:"8px",tabPaddingSmallBar:"4px 0",tabPaddingMediumBar:"6px 0",tabPaddingLargeBar:"10px 0",tabPaddingVerticalSmallBar:"6px 12px",tabPaddingVerticalMediumBar:"8px 16px",tabPaddingVerticalLargeBar:"10px 20px",tabGapSmallCard:"4px",tabGapMediumCard:"4px",tabGapLargeCard:"4px",tabGapSmallCardVertical:"4px",tabGapMediumCardVertical:"4px",tabGapLargeCardVertical:"4px",tabPaddingSmallCard:"8px 16px",tabPaddingMediumCard:"10px 20px",tabPaddingLargeCard:"12px 24px",tabPaddingSmallSegment:"4px 0",tabPaddingMediumSegment:"6px 0",tabPaddingLargeSegment:"8px 0",tabPaddingVerticalLargeSegment:"0 8px",tabPaddingVerticalSmallCard:"8px 12px",tabPaddingVerticalMediumCard:"10px 16px",tabPaddingVerticalLargeCard:"12px 20px",tabPaddingVerticalSmallSegment:"0 4px",tabPaddingVerticalMediumSegment:"0 6px",tabGapSmallSegment:"0",tabGapMediumSegment:"0",tabGapLargeSegment:"0",tabGapSmallSegmentVertical:"0",tabGapMediumSegmentVertical:"0",tabGapLargeSegmentVertical:"0",panePaddingSmall:"8px 0 0 0",panePaddingMedium:"12px 0 0 0",panePaddingLarge:"16px 0 0 0",closeSize:"18px",closeIconSize:"14px"},uB={name:"Tabs",common:tz,self(e){const t=function(e){const{textColor2:t,primaryColor:n,textColorDisabled:o,closeIconColor:r,closeIconColorHover:i,closeIconColorPressed:a,closeColorHover:l,closeColorPressed:s,tabColor:c,baseColor:u,dividerColor:d,fontWeight:p,textColor1:h,borderRadius:f,fontSize:v,fontWeightStrong:m}=e;return Object.assign(Object.assign({},cB),{colorSegment:c,tabFontSizeCard:v,tabTextColorLine:h,tabTextColorActiveLine:n,tabTextColorHoverLine:n,tabTextColorDisabledLine:o,tabTextColorSegment:h,tabTextColorActiveSegment:t,tabTextColorHoverSegment:t,tabTextColorDisabledSegment:o,tabTextColorBar:h,tabTextColorActiveBar:n,tabTextColorHoverBar:n,tabTextColorDisabledBar:o,tabTextColorCard:h,tabTextColorHoverCard:h,tabTextColorActiveCard:n,tabTextColorDisabledCard:o,barColor:n,closeIconColor:r,closeIconColorHover:i,closeIconColorPressed:a,closeColorHover:l,closeColorPressed:s,closeBorderRadius:f,tabColor:c,tabColorSegment:u,tabBorderColor:d,tabFontWeightActive:p,tabFontWeight:p,tabBorderRadius:f,paneTextColor:t,fontWeightStrong:m})}(e),{inputColor:n}=e;return t.colorSegment=n,t.tabColorSegment=n,t}},dB={name:"Thing",common:tz,self:function(e){const{textColor1:t,textColor2:n,fontWeightStrong:o,fontSize:r}=e;return{fontSize:r,titleTextColor:t,textColor:n,titleFontWeight:o}}},pB={titleMarginMedium:"0 0 6px 0",titleMarginLarge:"-2px 0 6px 0",titleFontSizeMedium:"14px",titleFontSizeLarge:"16px",iconSizeMedium:"14px",iconSizeLarge:"14px"},hB={name:"Timeline",common:tz,self(e){const{textColor3:t,infoColorSuppl:n,errorColorSuppl:o,successColorSuppl:r,warningColorSuppl:i,textColor1:a,textColor2:l,railColor:s,fontWeightStrong:c,fontSize:u}=e;return Object.assign(Object.assign({},pB),{contentFontSize:u,titleFontWeight:c,circleBorder:`2px solid ${t}`,circleBorderInfo:`2px solid ${n}`,circleBorderError:`2px solid ${o}`,circleBorderSuccess:`2px solid ${r}`,circleBorderWarning:`2px solid ${i}`,iconColor:t,iconColorInfo:n,iconColorError:o,iconColorSuccess:r,iconColorWarning:i,titleTextColor:a,contentTextColor:l,metaTextColor:t,lineColor:s})}},fB={extraFontSizeSmall:"12px",extraFontSizeMedium:"12px",extraFontSizeLarge:"14px",titleFontSizeSmall:"14px",titleFontSizeMedium:"16px",titleFontSizeLarge:"16px",closeSize:"20px",closeIconSize:"16px",headerHeightSmall:"44px",headerHeightMedium:"44px",headerHeightLarge:"50px"},vB={name:"Transfer",common:tz,peers:{Checkbox:jO,Scrollbar:nR,Input:xE,Empty:Yz,Button:eO},self(e){const{fontWeight:t,fontSizeLarge:n,fontSizeMedium:o,fontSizeSmall:r,heightLarge:i,heightMedium:a,borderRadius:l,inputColor:s,tableHeaderColor:c,textColor1:u,textColorDisabled:d,textColor2:p,textColor3:h,hoverColor:f,closeColorHover:v,closeColorPressed:m,closeIconColor:g,closeIconColorHover:b,closeIconColorPressed:y,dividerColor:x}=e;return Object.assign(Object.assign({},fB),{itemHeightSmall:a,itemHeightMedium:a,itemHeightLarge:i,fontSizeSmall:r,fontSizeMedium:o,fontSizeLarge:n,borderRadius:l,dividerColor:x,borderColor:"#0000",listColor:s,headerColor:c,titleTextColor:u,titleTextColorDisabled:d,extraTextColor:h,extraTextColorDisabled:d,itemTextColor:p,itemTextColorDisabled:d,itemColorPending:f,titleFontWeight:t,closeColorHover:v,closeColorPressed:m,closeIconColor:g,closeIconColorHover:b,closeIconColorPressed:y})}},mB={name:"Tree",common:tz,peers:{Checkbox:jO,Scrollbar:nR,Empty:Yz},self(e){const{primaryColor:t}=e,n=function(e){const{borderRadiusSmall:t,dividerColor:n,hoverColor:o,pressedColor:r,primaryColor:i,textColor3:a,textColor2:l,textColorDisabled:s,fontSize:c}=e;return{fontSize:c,lineHeight:"1.5",nodeHeight:"30px",nodeWrapperPadding:"3px 0",nodeBorderRadius:t,nodeColorHover:o,nodeColorPressed:r,nodeColorActive:tg(i,{alpha:.1}),arrowColor:a,nodeTextColor:l,nodeTextColorDisabled:s,loadingColor:i,dropMarkColor:i,lineColor:n}}(e);return n.nodeColorActive=tg(t,{alpha:.15}),n}},gB={name:"TreeSelect",common:tz,peers:{Tree:mB,Empty:Yz,InternalSelection:ZR}},bB={headerFontSize1:"30px",headerFontSize2:"22px",headerFontSize3:"18px",headerFontSize4:"16px",headerFontSize5:"16px",headerFontSize6:"16px",headerMargin1:"28px 0 20px 0",headerMargin2:"28px 0 20px 0",headerMargin3:"28px 0 20px 0",headerMargin4:"28px 0 18px 0",headerMargin5:"28px 0 18px 0",headerMargin6:"28px 0 18px 0",headerPrefixWidth1:"16px",headerPrefixWidth2:"16px",headerPrefixWidth3:"12px",headerPrefixWidth4:"12px",headerPrefixWidth5:"12px",headerPrefixWidth6:"12px",headerBarWidth1:"4px",headerBarWidth2:"4px",headerBarWidth3:"3px",headerBarWidth4:"3px",headerBarWidth5:"3px",headerBarWidth6:"3px",pMargin:"16px 0 16px 0",liMargin:".25em 0 0 0",olPadding:"0 0 0 2em",ulPadding:"0 0 0 2em"},yB={name:"Typography",common:tz,self:function(e){const{primaryColor:t,textColor2:n,borderColor:o,lineHeight:r,fontSize:i,borderRadiusSmall:a,dividerColor:l,fontWeightStrong:s,textColor1:c,textColor3:u,infoColor:d,warningColor:p,errorColor:h,successColor:f,codeColor:v}=e;return Object.assign(Object.assign({},bB),{aTextColor:t,blockquoteTextColor:n,blockquotePrefixColor:o,blockquoteLineHeight:r,blockquoteFontSize:i,codeBorderRadius:a,liTextColor:n,liLineHeight:r,liFontSize:i,hrColor:l,headerFontWeight:s,headerTextColor:c,pTextColor:n,pTextColor1Depth:c,pTextColor2Depth:n,pTextColor3Depth:u,pLineHeight:r,pFontSize:i,headerBarColor:t,headerBarColorPrimary:t,headerBarColorInfo:d,headerBarColorError:h,headerBarColorWarning:p,headerBarColorSuccess:f,textColor:n,textColor1Depth:c,textColor2Depth:n,textColor3Depth:u,textColorPrimary:t,textColorInfo:d,textColorSuccess:f,textColorWarning:p,textColorError:h,codeTextColor:n,codeColor:v,codeBorder:"1px solid #0000"})}},xB={name:"Upload",common:tz,peers:{Button:eO,Progress:UL},self(e){const{errorColor:t}=e,n=function(e){const{iconColor:t,primaryColor:n,errorColor:o,textColor2:r,successColor:i,opacityDisabled:a,actionColor:l,borderColor:s,hoverColor:c,lineHeight:u,borderRadius:d,fontSize:p}=e;return{fontSize:p,lineHeight:u,borderRadius:d,draggerColor:l,draggerBorder:`1px dashed ${s}`,draggerBorderHover:`1px dashed ${n}`,itemColorHover:c,itemColorHoverError:tg(o,{alpha:.06}),itemTextColor:r,itemTextColorError:o,itemTextColorSuccess:i,itemIconColor:t,itemDisabledOpacity:a,itemBorderImageCardError:`1px solid ${o}`,itemBorderImageCard:`1px solid ${s}`}}(e);return n.itemColorHoverError=tg(t,{alpha:.09}),n}},CB={name:"Watermark",common:tz,self(e){const{fontFamily:t}=e;return{fontFamily:t}}},wB={name:"Row",common:tz},kB={name:"FloatButton",common:tz,self(e){const{popoverColor:t,textColor2:n,buttonColor2Hover:o,buttonColor2Pressed:r,primaryColor:i,primaryColorHover:a,primaryColorPressed:l,baseColor:s,borderRadius:c}=e;return{color:t,textColor:n,boxShadow:"0 2px 8px 0px rgba(0, 0, 0, .12)",boxShadowHover:"0 2px 12px 0px rgba(0, 0, 0, .18)",boxShadowPressed:"0 2px 12px 0px rgba(0, 0, 0, .18)",colorHover:o,colorPressed:r,colorPrimary:i,colorPrimaryHover:a,colorPrimaryPressed:l,textColorPrimary:s,borderRadiusSquare:c}}},SB={name:"IconWrapper",common:tz,self:function(e){const{primaryColor:t,baseColor:n}=e;return{color:t,iconColor:n}}},_B={name:"Image",common:tz,peers:{Tooltip:_M},self:e=>{const{textColor2:t}=e;return{toolbarIconColor:t,toolbarColor:"rgba(0, 0, 0, .35)",toolbarBoxShadow:"none",toolbarBorderRadius:"24px"}}};function PB(e){return null==e||"string"==typeof e&&""===e.trim()?null:Number(e)}function TB(e){return null==e||!Number.isNaN(e)}function AB(e,t){return"number"!=typeof e?"":void 0===t?String(e):e.toFixed(t)}function zB(e){if(null===e)return null;if("number"==typeof e)return e;{const t=Number(e);return Number.isNaN(t)?null:t}}const RB=eb([nb("input-number-suffix","\n display: inline-block;\n margin-right: 10px;\n "),nb("input-number-prefix","\n display: inline-block;\n margin-left: 10px;\n ")]),EB=zn({name:"InputNumber",props:Object.assign(Object.assign({},I_.props),{autofocus:Boolean,loading:{type:Boolean,default:void 0},placeholder:String,defaultValue:{type:Number,default:null},value:Number,step:{type:[Number,String],default:1},min:[Number,String],max:[Number,String],size:String,disabled:{type:Boolean,default:void 0},validator:Function,bordered:{type:Boolean,default:void 0},showButton:{type:Boolean,default:!0},buttonPlacement:{type:String,default:"right"},inputProps:Object,readonly:Boolean,clearable:Boolean,keyboard:{type:Object,default:{}},updateValueOnInput:{type:Boolean,default:!0},round:{type:Boolean,default:void 0},parse:Function,format:Function,precision:Number,status:String,"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array],onFocus:[Function,Array],onBlur:[Function,Array],onClear:[Function,Array],onChange:[Function,Array]}),setup(e){const{mergedBorderedRef:t,mergedClsPrefixRef:n,mergedRtlRef:o}=B_(e),r=I_("InputNumber","-input-number",RB,_L,e,n),{localeRef:i}=VP("InputNumber"),a=eC(e),{mergedSizeRef:l,mergedDisabledRef:s,mergedStatusRef:c}=a,u=Et(null),d=Et(null),p=Et(null),h=Et(e.defaultValue),f=Db(jt(e,"value"),h),v=Et(""),m=e=>{const t=String(e).split(".")[1];return t?t.length:0},g=mb((()=>{const{placeholder:t}=e;return void 0!==t?t:i.value.placeholder})),b=mb((()=>{const t=zB(e.step);return null!==t?0===t?1:Math.abs(t):1})),y=mb((()=>{const t=zB(e.min);return null!==t?t:null})),x=mb((()=>{const t=zB(e.max);return null!==t?t:null})),C=()=>{const{value:t}=f;if(TB(t)){const{format:n,precision:o}=e;n?v.value=n(t):null===t||void 0===o||m(t)>o?v.value=AB(t,void 0):v.value=AB(t,o)}else v.value=String(t)};C();const w=t=>{const{value:n}=f;if(t===n)return void C();const{"onUpdate:value":o,onUpdateValue:r,onChange:i}=e,{nTriggerFormInput:l,nTriggerFormChange:s}=a;i&&dg(i,t),r&&dg(r,t),o&&dg(o,t),h.value=t,l(),s()},k=({offset:t,doUpdateIfValid:n,fixPrecision:o,isInputing:r})=>{const{value:i}=v;if(r&&(a=i).includes(".")&&(/^(-)?\d+.*(\.|0)$/.test(a)||/^\.\d+$/.test(a)))return!1;var a;const l=(e.parse||PB)(i);if(null===l)return n&&w(null),null;if(TB(l)){const i=m(l),{precision:a}=e;if(void 0!==a&&a{const n=[e.min,e.max,e.step,t].map((e=>void 0===e?0:m(e)));return Math.max(...n)})(l)));if(TB(s)){const{value:t}=x,{value:o}=y;if(null!==t&&s>t){if(!n||r)return!1;s=t}if(null!==o&&s!1===k({offset:0,doUpdateIfValid:!1,isInputing:!1,fixPrecision:!1}))),_=mb((()=>{const{value:t}=f;if(e.validator&&null===t)return!1;const{value:n}=b;return!1!==k({offset:-n,doUpdateIfValid:!1,isInputing:!1,fixPrecision:!1})})),P=mb((()=>{const{value:t}=f;if(e.validator&&null===t)return!1;const{value:n}=b;return!1!==k({offset:+n,doUpdateIfValid:!1,isInputing:!1,fixPrecision:!1})}));function T(){const{value:t}=P;if(!t)return void B();const{value:n}=f;if(null===n)e.validator||w(E());else{const{value:e}=b;k({offset:e,doUpdateIfValid:!0,isInputing:!1,fixPrecision:!0})}}function A(){const{value:t}=_;if(!t)return void I();const{value:n}=f;if(null===n)e.validator||w(E());else{const{value:e}=b;k({offset:-e,doUpdateIfValid:!0,isInputing:!1,fixPrecision:!0})}}const z=function(t){const{onFocus:n}=e,{nTriggerFormFocus:o}=a;n&&dg(n,t),o()},R=function(t){var n,o;if(t.target===(null===(n=u.value)||void 0===n?void 0:n.wrapperElRef))return;const r=k({offset:0,doUpdateIfValid:!0,isInputing:!1,fixPrecision:!0});if(!1!==r){const e=null===(o=u.value)||void 0===o?void 0:o.inputElRef;e&&(e.value=String(r||"")),f.value===r&&C()}else C();const{onBlur:i}=e,{nTriggerFormBlur:l}=a;i&&dg(i,t),l(),tn((()=>{C()}))};function E(){if(e.validator)return null;const{value:t}=y,{value:n}=x;return null!==t?Math.max(0,t):null!==n?Math.min(0,n):0}let O=null,M=null,F=null;function I(){F&&(window.clearTimeout(F),F=null),O&&(window.clearInterval(O),O=null)}let L=null;function B(){L&&(window.clearTimeout(L),L=null),M&&(window.clearInterval(M),M=null)}lr(f,(()=>{C()}));const D={focus:()=>{var e;return null===(e=u.value)||void 0===e?void 0:e.focus()},blur:()=>{var e;return null===(e=u.value)||void 0===e?void 0:e.blur()},select:()=>{var e;return null===(e=u.value)||void 0===e?void 0:e.select()}},$=GP("InputNumber",o,n);return Object.assign(Object.assign({},D),{rtlEnabled:$,inputInstRef:u,minusButtonInstRef:d,addButtonInstRef:p,mergedClsPrefix:n,mergedBordered:t,uncontrolledValue:h,mergedValue:f,mergedPlaceholder:g,displayedValueInvalid:S,mergedSize:l,mergedDisabled:s,displayedValue:v,addable:P,minusable:_,mergedStatus:c,handleFocus:z,handleBlur:R,handleClear:function(t){!function(t){const{onClear:n}=e;n&&dg(n,t)}(t),w(null)},handleMouseDown:function(e){var t,n,o;(null===(t=p.value)||void 0===t?void 0:t.$el.contains(e.target))&&e.preventDefault(),(null===(n=d.value)||void 0===n?void 0:n.$el.contains(e.target))&&e.preventDefault(),null===(o=u.value)||void 0===o||o.activate()},handleAddClick:()=>{M||T()},handleMinusClick:()=>{O||A()},handleAddMousedown:function(){B(),L=window.setTimeout((()=>{M=window.setInterval((()=>{T()}),100)}),800),Pb("mouseup",document,B,{once:!0})},handleMinusMousedown:function(){I(),F=window.setTimeout((()=>{O=window.setInterval((()=>{A()}),100)}),800),Pb("mouseup",document,I,{once:!0})},handleKeyDown:function(t){var n,o;if("Enter"===t.key){if(t.target===(null===(n=u.value)||void 0===n?void 0:n.wrapperElRef))return;!1!==k({offset:0,doUpdateIfValid:!0,isInputing:!1,fixPrecision:!0})&&(null===(o=u.value)||void 0===o||o.deactivate())}else if("ArrowUp"===t.key){if(!P.value)return;if(!1===e.keyboard.ArrowUp)return;t.preventDefault(),!1!==k({offset:0,doUpdateIfValid:!0,isInputing:!1,fixPrecision:!0})&&T()}else if("ArrowDown"===t.key){if(!_.value)return;if(!1===e.keyboard.ArrowDown)return;t.preventDefault(),!1!==k({offset:0,doUpdateIfValid:!0,isInputing:!1,fixPrecision:!0})&&A()}},handleUpdateDisplayedValue:function(t){v.value=t,!e.updateValueOnInput||e.format||e.parse||void 0!==e.precision||k({offset:0,doUpdateIfValid:!0,isInputing:!0,fixPrecision:!1})},mergedTheme:r,inputThemeOverrides:{paddingSmall:"0 8px 0 10px",paddingMedium:"0 8px 0 12px",paddingLarge:"0 8px 0 14px"},buttonThemeOverrides:ai((()=>{const{self:{iconColorDisabled:e}}=r.value,[t,n,o,i]=Qm(e);return{textColorTextDisabled:`rgb(${t}, ${n}, ${o})`,opacityDisabled:`${i}`}}))})},render(){const{mergedClsPrefix:e,$slots:t}=this,n=()=>li(rO,{text:!0,disabled:!this.minusable||this.mergedDisabled||this.readonly,focusable:!1,theme:this.mergedTheme.peers.Button,themeOverrides:this.mergedTheme.peerOverrides.Button,builtinThemeOverrides:this.buttonThemeOverrides,onClick:this.handleMinusClick,onMousedown:this.handleMinusMousedown,ref:"minusButtonInstRef"},{icon:()=>xg(t["minus-icon"],(()=>[li(CT,{clsPrefix:e},{default:()=>li(pT,null)})]))}),o=()=>li(rO,{text:!0,disabled:!this.addable||this.mergedDisabled||this.readonly,focusable:!1,theme:this.mergedTheme.peers.Button,themeOverrides:this.mergedTheme.peerOverrides.Button,builtinThemeOverrides:this.buttonThemeOverrides,onClick:this.handleAddClick,onMousedown:this.handleAddMousedown,ref:"addButtonInstRef"},{icon:()=>xg(t["add-icon"],(()=>[li(CT,{clsPrefix:e},{default:()=>li(XP,null)})]))});return li("div",{class:[`${e}-input-number`,this.rtlEnabled&&`${e}-input-number--rtl`]},li(AE,{ref:"inputInstRef",autofocus:this.autofocus,status:this.mergedStatus,bordered:this.mergedBordered,loading:this.loading,value:this.displayedValue,onUpdateValue:this.handleUpdateDisplayedValue,theme:this.mergedTheme.peers.Input,themeOverrides:this.mergedTheme.peerOverrides.Input,builtinThemeOverrides:this.inputThemeOverrides,size:this.mergedSize,placeholder:this.mergedPlaceholder,disabled:this.mergedDisabled,readonly:this.readonly,round:this.round,textDecoration:this.displayedValueInvalid?"line-through":void 0,onFocus:this.handleFocus,onBlur:this.handleBlur,onKeydown:this.handleKeyDown,onMousedown:this.handleMouseDown,onClear:this.handleClear,clearable:this.clearable,inputProps:this.inputProps,internalLoadingBeforeSuffix:!0},{prefix:()=>{var o;return this.showButton&&"both"===this.buttonPlacement?[n(),wg(t.prefix,(t=>t?li("span",{class:`${e}-input-number-prefix`},t):null))]:null===(o=t.prefix)||void 0===o?void 0:o.call(t)},suffix:()=>{var r;return this.showButton?[wg(t.suffix,(t=>t?li("span",{class:`${e}-input-number-suffix`},t):null)),"right"===this.buttonPlacement?n():null,o()]:null===(r=t.suffix)||void 0===r?void 0:r.call(t)}}))}}),OB="n-layout-sider",MB={type:String,default:"static"},FB=nb("layout","\n color: var(--n-text-color);\n background-color: var(--n-color);\n box-sizing: border-box;\n position: relative;\n z-index: auto;\n flex: auto;\n overflow: hidden;\n transition:\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n",[nb("layout-scroll-container","\n overflow-x: hidden;\n box-sizing: border-box;\n height: 100%;\n "),rb("absolute-positioned","\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ")]),IB={embedded:Boolean,position:MB,nativeScrollbar:{type:Boolean,default:!0},scrollbarProps:Object,onScroll:Function,contentClass:String,contentStyle:{type:[String,Object],default:""},hasSider:Boolean,siderPlacement:{type:String,default:"left"}},LB="n-layout",BB=zn({name:(DB=!1)?"LayoutContent":"Layout",props:Object.assign(Object.assign({},I_.props),IB),setup(e){const t=Et(null),n=Et(null),{mergedClsPrefixRef:o,inlineThemeDisabled:r}=B_(e),i=I_("Layout","-layout",FB,TL,e,o);_o(LB,e);let a=0,l=0;Qx((()=>{if(e.nativeScrollbar){const e=t.value;e&&(e.scrollTop=l,e.scrollLeft=a)}}));const s={scrollTo:function(o,r){if(e.nativeScrollbar){const{value:e}=t;e&&(void 0===r?e.scrollTo(o):e.scrollTo(o,r))}else{const{value:e}=n;e&&e.scrollTo(o,r)}}},c=ai((()=>{const{common:{cubicBezierEaseInOut:t},self:n}=i.value;return{"--n-bezier":t,"--n-color":e.embedded?n.colorEmbedded:n.color,"--n-text-color":n.textColor}})),u=r?KP("layout",ai((()=>e.embedded?"e":"")),c,e):void 0;return Object.assign({mergedClsPrefix:o,scrollableElRef:t,scrollbarInstRef:n,hasSiderStyle:{display:"flex",flexWrap:"nowrap",width:"100%",flexDirection:"row"},mergedTheme:i,handleNativeElScroll:t=>{var n;const o=t.target;a=o.scrollLeft,l=o.scrollTop,null===(n=e.onScroll)||void 0===n||n.call(e,t)},cssVars:r?void 0:c,themeClass:null==u?void 0:u.themeClass,onRender:null==u?void 0:u.onRender},s)},render(){var e;const{mergedClsPrefix:t,hasSider:n}=this;null===(e=this.onRender)||void 0===e||e.call(this);const o=n?this.hasSiderStyle:void 0;return li("div",{class:[this.themeClass,DB&&`${t}-layout-content`,`${t}-layout`,`${t}-layout--${this.position}-positioned`],style:this.cssVars},this.nativeScrollbar?li("div",{ref:"scrollableElRef",class:[`${t}-layout-scroll-container`,this.contentClass],style:[this.contentStyle,o],onScroll:this.handleNativeElScroll},this.$slots):li(lR,Object.assign({},this.scrollbarProps,{onScroll:this.onScroll,ref:"scrollbarInstRef",theme:this.mergedTheme.peers.Scrollbar,themeOverrides:this.mergedTheme.peerOverrides.Scrollbar,contentClass:this.contentClass,contentStyle:[this.contentStyle,o]}),this.$slots))}});var DB;const $B=nb("layout-sider","\n flex-shrink: 0;\n box-sizing: border-box;\n position: relative;\n z-index: 1;\n color: var(--n-text-color);\n transition:\n color .3s var(--n-bezier),\n border-color .3s var(--n-bezier),\n min-width .3s var(--n-bezier),\n max-width .3s var(--n-bezier),\n transform .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n background-color: var(--n-color);\n display: flex;\n justify-content: flex-end;\n",[rb("bordered",[ob("border",'\n content: "";\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background-color: var(--n-border-color);\n transition: background-color .3s var(--n-bezier);\n ')]),ob("left-placement",[rb("bordered",[ob("border","\n right: 0;\n ")])]),rb("right-placement","\n justify-content: flex-start;\n ",[rb("bordered",[ob("border","\n left: 0;\n ")]),rb("collapsed",[nb("layout-toggle-button",[nb("base-icon","\n transform: rotate(180deg);\n ")]),nb("layout-toggle-bar",[eb("&:hover",[ob("top",{transform:"rotate(-12deg) scale(1.15) translateY(-2px)"}),ob("bottom",{transform:"rotate(12deg) scale(1.15) translateY(2px)"})])])]),nb("layout-toggle-button","\n left: 0;\n transform: translateX(-50%) translateY(-50%);\n ",[nb("base-icon","\n transform: rotate(0);\n ")]),nb("layout-toggle-bar","\n left: -28px;\n transform: rotate(180deg);\n ",[eb("&:hover",[ob("top",{transform:"rotate(12deg) scale(1.15) translateY(-2px)"}),ob("bottom",{transform:"rotate(-12deg) scale(1.15) translateY(2px)"})])])]),rb("collapsed",[nb("layout-toggle-bar",[eb("&:hover",[ob("top",{transform:"rotate(-12deg) scale(1.15) translateY(-2px)"}),ob("bottom",{transform:"rotate(12deg) scale(1.15) translateY(2px)"})])]),nb("layout-toggle-button",[nb("base-icon","\n transform: rotate(0);\n ")])]),nb("layout-toggle-button","\n transition:\n color .3s var(--n-bezier),\n right .3s var(--n-bezier),\n left .3s var(--n-bezier),\n border-color .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n cursor: pointer;\n width: 24px;\n height: 24px;\n position: absolute;\n top: 50%;\n right: 0;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n color: var(--n-toggle-button-icon-color);\n border: var(--n-toggle-button-border);\n background-color: var(--n-toggle-button-color);\n box-shadow: 0 2px 4px 0px rgba(0, 0, 0, .06);\n transform: translateX(50%) translateY(-50%);\n z-index: 1;\n ",[nb("base-icon","\n transition: transform .3s var(--n-bezier);\n transform: rotate(180deg);\n ")]),nb("layout-toggle-bar","\n cursor: pointer;\n height: 72px;\n width: 32px;\n position: absolute;\n top: calc(50% - 36px);\n right: -28px;\n ",[ob("top, bottom","\n position: absolute;\n width: 4px;\n border-radius: 2px;\n height: 38px;\n left: 14px;\n transition: \n background-color .3s var(--n-bezier),\n transform .3s var(--n-bezier);\n "),ob("bottom","\n position: absolute;\n top: 34px;\n "),eb("&:hover",[ob("top",{transform:"rotate(12deg) scale(1.15) translateY(-2px)"}),ob("bottom",{transform:"rotate(-12deg) scale(1.15) translateY(2px)"})]),ob("top, bottom",{backgroundColor:"var(--n-toggle-bar-color)"}),eb("&:hover",[ob("top, bottom",{backgroundColor:"var(--n-toggle-bar-color-hover)"})])]),ob("border","\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 1px;\n transition: background-color .3s var(--n-bezier);\n "),nb("layout-sider-scroll-container","\n flex-grow: 1;\n flex-shrink: 0;\n box-sizing: border-box;\n height: 100%;\n opacity: 0;\n transition: opacity .3s var(--n-bezier);\n max-width: 100%;\n "),rb("show-content",[nb("layout-sider-scroll-container",{opacity:1})]),rb("absolute-positioned","\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n ")]),NB=zn({name:"LayoutToggleButton",props:{clsPrefix:{type:String,required:!0},onClick:Function},render(){const{clsPrefix:e}=this;return li("div",{class:`${e}-layout-toggle-button`,onClick:this.onClick},li(CT,{clsPrefix:e},{default:()=>li(eT,null)}))}}),jB=zn({props:{clsPrefix:{type:String,required:!0},onClick:Function},render(){const{clsPrefix:e}=this;return li("div",{onClick:this.onClick,class:`${e}-layout-toggle-bar`},li("div",{class:`${e}-layout-toggle-bar__top`}),li("div",{class:`${e}-layout-toggle-bar__bottom`}))}}),HB={position:MB,bordered:Boolean,collapsedWidth:{type:Number,default:48},width:{type:[Number,String],default:272},contentClass:String,contentStyle:{type:[String,Object],default:""},collapseMode:{type:String,default:"transform"},collapsed:{type:Boolean,default:void 0},defaultCollapsed:Boolean,showCollapsedContent:{type:Boolean,default:!0},showTrigger:{type:[Boolean,String],default:!1},nativeScrollbar:{type:Boolean,default:!0},inverted:Boolean,scrollbarProps:Object,triggerClass:String,triggerStyle:[String,Object],collapsedTriggerClass:String,collapsedTriggerStyle:[String,Object],"onUpdate:collapsed":[Function,Array],onUpdateCollapsed:[Function,Array],onAfterEnter:Function,onAfterLeave:Function,onExpand:[Function,Array],onCollapse:[Function,Array],onScroll:Function},WB=zn({name:"LayoutSider",props:Object.assign(Object.assign({},I_.props),HB),setup(e){const t=Po(LB),n=Et(null),o=Et(null),r=Et(e.defaultCollapsed),i=Db(jt(e,"collapsed"),r),a=ai((()=>Ag(i.value?e.collapsedWidth:e.width))),l=ai((()=>"transform"!==e.collapseMode?{}:{minWidth:Ag(e.width)})),s=ai((()=>t?t.siderPlacement:"left"));let c=0,u=0;Qx((()=>{if(e.nativeScrollbar){const e=n.value;e&&(e.scrollTop=u,e.scrollLeft=c)}})),_o(OB,{collapsedRef:i,collapseModeRef:jt(e,"collapseMode")});const{mergedClsPrefixRef:d,inlineThemeDisabled:p}=B_(e),h=I_("Layout","-layout-sider",$B,TL,e,d),f={scrollTo:function(t,r){if(e.nativeScrollbar){const{value:e}=n;e&&(void 0===r?e.scrollTo(t):e.scrollTo(t,r))}else{const{value:e}=o;e&&e.scrollTo(t,r)}}},v=ai((()=>{const{common:{cubicBezierEaseInOut:t},self:n}=h.value,{siderToggleButtonColor:o,siderToggleButtonBorder:r,siderToggleBarColor:i,siderToggleBarColorHover:a}=n,l={"--n-bezier":t,"--n-toggle-button-color":o,"--n-toggle-button-border":r,"--n-toggle-bar-color":i,"--n-toggle-bar-color-hover":a};return e.inverted?(l["--n-color"]=n.siderColorInverted,l["--n-text-color"]=n.textColorInverted,l["--n-border-color"]=n.siderBorderColorInverted,l["--n-toggle-button-icon-color"]=n.siderToggleButtonIconColorInverted,l.__invertScrollbar=n.__invertScrollbar):(l["--n-color"]=n.siderColor,l["--n-text-color"]=n.textColor,l["--n-border-color"]=n.siderBorderColor,l["--n-toggle-button-icon-color"]=n.siderToggleButtonIconColor),l})),m=p?KP("layout-sider",ai((()=>e.inverted?"a":"b")),v,e):void 0;return Object.assign({scrollableElRef:n,scrollbarInstRef:o,mergedClsPrefix:d,mergedTheme:h,styleMaxWidth:a,mergedCollapsed:i,scrollContainerStyle:l,siderPlacement:s,handleNativeElScroll:t=>{var n;const o=t.target;c=o.scrollLeft,u=o.scrollTop,null===(n=e.onScroll)||void 0===n||n.call(e,t)},handleTransitionend:function(t){var n,o;"max-width"===t.propertyName&&(i.value?null===(n=e.onAfterLeave)||void 0===n||n.call(e):null===(o=e.onAfterEnter)||void 0===o||o.call(e))},handleTriggerClick:function(){const{"onUpdate:collapsed":t,onUpdateCollapsed:n,onExpand:o,onCollapse:a}=e,{value:l}=i;n&&dg(n,!l),t&&dg(t,!l),r.value=!l,l?o&&dg(o):a&&dg(a)},inlineThemeDisabled:p,cssVars:v,themeClass:null==m?void 0:m.themeClass,onRender:null==m?void 0:m.onRender},f)},render(){var e;const{mergedClsPrefix:t,mergedCollapsed:n,showTrigger:o}=this;return null===(e=this.onRender)||void 0===e||e.call(this),li("aside",{class:[`${t}-layout-sider`,this.themeClass,`${t}-layout-sider--${this.position}-positioned`,`${t}-layout-sider--${this.siderPlacement}-placement`,this.bordered&&`${t}-layout-sider--bordered`,n&&`${t}-layout-sider--collapsed`,(!n||this.showCollapsedContent)&&`${t}-layout-sider--show-content`],onTransitionend:this.handleTransitionend,style:[this.inlineThemeDisabled?void 0:this.cssVars,{maxWidth:this.styleMaxWidth,width:Ag(this.width)}]},this.nativeScrollbar?li("div",{class:[`${t}-layout-sider-scroll-container`,this.contentClass],onScroll:this.handleNativeElScroll,style:[this.scrollContainerStyle,{overflow:"auto"},this.contentStyle],ref:"scrollableElRef"},this.$slots):li(lR,Object.assign({},this.scrollbarProps,{onScroll:this.onScroll,ref:"scrollbarInstRef",style:this.scrollContainerStyle,contentStyle:this.contentStyle,contentClass:this.contentClass,theme:this.mergedTheme.peers.Scrollbar,themeOverrides:this.mergedTheme.peerOverrides.Scrollbar,builtinThemeOverrides:this.inverted&&"true"===this.cssVars.__invertScrollbar?{colorHover:"rgba(255, 255, 255, .4)",color:"rgba(255, 255, 255, .3)"}:void 0}),this.$slots),o?li("bar"===o?jB:NB,{clsPrefix:t,class:n?this.collapsedTriggerClass:this.triggerClass,style:n?this.collapsedTriggerStyle:this.triggerStyle,onClick:this.handleTriggerClick}):null,this.bordered?li("div",{class:`${t}-layout-sider__border`}):null)}}),UB={extraFontSize:"12px",width:"440px"},VB={name:"Transfer",common:tz,peers:{Checkbox:jO,Scrollbar:nR,Input:xE,Empty:Yz,Button:eO},self(e){const{iconColorDisabled:t,iconColor:n,fontWeight:o,fontSizeLarge:r,fontSizeMedium:i,fontSizeSmall:a,heightLarge:l,heightMedium:s,heightSmall:c,borderRadius:u,inputColor:d,tableHeaderColor:p,textColor1:h,textColorDisabled:f,textColor2:v,hoverColor:m}=e;return Object.assign(Object.assign({},UB),{itemHeightSmall:c,itemHeightMedium:s,itemHeightLarge:l,fontSizeSmall:a,fontSizeMedium:i,fontSizeLarge:r,borderRadius:u,borderColor:"#0000",listColor:d,headerColor:p,titleTextColor:h,titleTextColorDisabled:f,extraTextColor:v,filterDividerColor:"#0000",itemTextColor:v,itemTextColorDisabled:f,itemColorPending:m,titleFontWeight:o,iconColor:n,iconColorDisabled:t})}},qB=eb([nb("list","\n --n-merged-border-color: var(--n-border-color);\n --n-merged-color: var(--n-color);\n --n-merged-color-hover: var(--n-color-hover);\n margin: 0;\n font-size: var(--n-font-size);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n padding: 0;\n list-style-type: none;\n color: var(--n-text-color);\n background-color: var(--n-merged-color);\n ",[rb("show-divider",[nb("list-item",[eb("&:not(:last-child)",[ob("divider","\n background-color: var(--n-merged-border-color);\n ")])])]),rb("clickable",[nb("list-item","\n cursor: pointer;\n ")]),rb("bordered","\n border: 1px solid var(--n-merged-border-color);\n border-radius: var(--n-border-radius);\n "),rb("hoverable",[nb("list-item","\n border-radius: var(--n-border-radius);\n ",[eb("&:hover","\n background-color: var(--n-merged-color-hover);\n ",[ob("divider","\n background-color: transparent;\n ")])])]),rb("bordered, hoverable",[nb("list-item","\n padding: 12px 20px;\n "),ob("header, footer","\n padding: 12px 20px;\n ")]),ob("header, footer","\n padding: 12px 0;\n box-sizing: border-box;\n transition: border-color .3s var(--n-bezier);\n ",[eb("&:not(:last-child)","\n border-bottom: 1px solid var(--n-merged-border-color);\n ")]),nb("list-item","\n position: relative;\n padding: 12px 0; \n box-sizing: border-box;\n display: flex;\n flex-wrap: nowrap;\n align-items: center;\n transition:\n background-color .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n ",[ob("prefix","\n margin-right: 20px;\n flex: 0;\n "),ob("suffix","\n margin-left: 20px;\n flex: 0;\n "),ob("main","\n flex: 1;\n "),ob("divider","\n height: 1px;\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: transparent;\n transition: background-color .3s var(--n-bezier);\n pointer-events: none;\n ")])]),ab(nb("list","\n --n-merged-color-hover: var(--n-color-hover-modal);\n --n-merged-color: var(--n-color-modal);\n --n-merged-border-color: var(--n-border-color-modal);\n ")),lb(nb("list","\n --n-merged-color-hover: var(--n-color-hover-popover);\n --n-merged-color: var(--n-color-popover);\n --n-merged-border-color: var(--n-border-color-popover);\n "))]),KB=Object.assign(Object.assign({},I_.props),{size:{type:String,default:"medium"},bordered:Boolean,clickable:Boolean,hoverable:Boolean,showDivider:{type:Boolean,default:!0}}),GB="n-list",XB=zn({name:"List",props:KB,setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n,mergedRtlRef:o}=B_(e),r=GP("List",o,t),i=I_("List","-list",qB,zL,e,t);_o(GB,{showDividerRef:jt(e,"showDivider"),mergedClsPrefixRef:t});const a=ai((()=>{const{common:{cubicBezierEaseInOut:e},self:{fontSize:t,textColor:n,color:o,colorModal:r,colorPopover:a,borderColor:l,borderColorModal:s,borderColorPopover:c,borderRadius:u,colorHover:d,colorHoverModal:p,colorHoverPopover:h}}=i.value;return{"--n-font-size":t,"--n-bezier":e,"--n-text-color":n,"--n-color":o,"--n-border-radius":u,"--n-border-color":l,"--n-border-color-modal":s,"--n-border-color-popover":c,"--n-color-modal":r,"--n-color-popover":a,"--n-color-hover":d,"--n-color-hover-modal":p,"--n-color-hover-popover":h}})),l=n?KP("list",void 0,a,e):void 0;return{mergedClsPrefix:t,rtlEnabled:r,cssVars:n?void 0:a,themeClass:null==l?void 0:l.themeClass,onRender:null==l?void 0:l.onRender}},render(){var e;const{$slots:t,mergedClsPrefix:n,onRender:o}=this;return null==o||o(),li("ul",{class:[`${n}-list`,this.rtlEnabled&&`${n}-list--rtl`,this.bordered&&`${n}-list--bordered`,this.showDivider&&`${n}-list--show-divider`,this.hoverable&&`${n}-list--hoverable`,this.clickable&&`${n}-list--clickable`,this.themeClass],style:this.cssVars},t.header?li("div",{class:`${n}-list__header`},t.header()):null,null===(e=t.default)||void 0===e?void 0:e.call(t),t.footer?li("div",{class:`${n}-list__footer`},t.footer()):null)}}),YB=zn({name:"ListItem",setup(){const e=Po(GB,null);return e||fg("list-item","`n-list-item` must be placed in `n-list`."),{showDivider:e.showDividerRef,mergedClsPrefix:e.mergedClsPrefixRef}},render(){const{$slots:e,mergedClsPrefix:t}=this;return li("li",{class:`${t}-list-item`},e.prefix?li("div",{class:`${t}-list-item__prefix`},e.prefix()):null,e.default?li("div",{class:`${t}-list-item__main`},e):null,e.suffix?li("div",{class:`${t}-list-item__suffix`},e.suffix()):null,this.showDivider&&li("div",{class:`${t}-list-item__divider`}))}}),QB="n-loading-bar",ZB="n-loading-bar-api",JB=nb("loading-bar-container","\n z-index: 5999;\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 2px;\n",[rR({enterDuration:"0.3s",leaveDuration:"0.8s"}),nb("loading-bar","\n width: 100%;\n transition:\n max-width 4s linear,\n background .2s linear;\n height: var(--n-height);\n ",[rb("starting","\n background: var(--n-color-loading);\n "),rb("finishing","\n background: var(--n-color-loading);\n transition:\n max-width .2s linear,\n background .2s linear;\n "),rb("error","\n background: var(--n-color-error);\n transition:\n max-width .2s linear,\n background .2s linear;\n ")])]);var eD=globalThis&&globalThis.__awaiter||function(e,t,n,o){return new(n||(n=Promise))((function(r,i){function a(e){try{s(o.next(e))}catch(WQ){i(WQ)}}function l(e){try{s(o.throw(e))}catch(WQ){i(WQ)}}function s(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,l)}s((o=o.apply(e,t||[])).next())}))};function tD(e,t){return`${t}-loading-bar ${t}-loading-bar--${e}`}const nD=zn({name:"LoadingBar",props:{containerClass:String,containerStyle:[String,Object]},setup(){const{inlineThemeDisabled:e}=B_(),{props:t,mergedClsPrefixRef:n}=Po(QB),o=Et(null),r=Et(!1),i=Et(!1),a=Et(!1),l=Et(!1);let s=!1;const c=Et(!1),u=ai((()=>{const{loadingBarStyle:e}=t;return e?e[c.value?"error":"loading"]:""}));function d(){return eD(this,void 0,void 0,(function*(){r.value=!1,a.value=!1,s=!1,c.value=!1,l.value=!0,yield tn(),l.value=!1}))}function p(){return eD(this,arguments,void 0,(function*(e=0,t=80,r="starting"){if(i.value=!0,yield d(),s)return;a.value=!0,yield tn();const l=o.value;l&&(l.style.maxWidth=`${e}%`,l.style.transition="none",l.offsetWidth,l.className=tD(r,n.value),l.style.transition="",l.style.maxWidth=`${t}%`)}))}const h=I_("LoadingBar","-loading-bar",JB,OL,t,n),f=ai((()=>{const{self:{height:e,colorError:t,colorLoading:n}}=h.value;return{"--n-height":e,"--n-color-loading":n,"--n-color-error":t}})),v=e?KP("loading-bar",void 0,f,t):void 0;return{mergedClsPrefix:n,loadingBarRef:o,started:i,loading:a,entering:r,transitionDisabled:l,start:p,error:function(){if(!s&&!c.value)if(a.value){c.value=!0;const e=o.value;if(!e)return;e.className=tD("error",n.value),e.style.maxWidth="100%",e.offsetWidth,a.value=!1}else p(100,100,"error").then((()=>{c.value=!0;const e=o.value;e&&(e.className=tD("error",n.value),e.offsetWidth,a.value=!1)}))},finish:function(){return eD(this,void 0,void 0,(function*(){if(s||c.value)return;i.value&&(yield tn()),s=!0;const e=o.value;e&&(e.className=tD("finishing",n.value),e.style.maxWidth="100%",e.offsetWidth,a.value=!1)}))},handleEnter:function(){r.value=!0},handleAfterEnter:function(){r.value=!1},handleAfterLeave:function(){return eD(this,void 0,void 0,(function*(){yield d()}))},mergedLoadingBarStyle:u,cssVars:e?void 0:f,themeClass:null==v?void 0:v.themeClass,onRender:null==v?void 0:v.onRender}},render(){if(!this.started)return null;const{mergedClsPrefix:e}=this;return li(vi,{name:"fade-in-transition",appear:!0,onEnter:this.handleEnter,onAfterEnter:this.handleAfterEnter,onAfterLeave:this.handleAfterLeave,css:!this.transitionDisabled},{default:()=>{var t;return null===(t=this.onRender)||void 0===t||t.call(this),fn(li("div",{class:[`${e}-loading-bar-container`,this.themeClass,this.containerClass],style:this.containerStyle},li("div",{ref:"loadingBarRef",class:[`${e}-loading-bar`],style:[this.cssVars,this.mergedLoadingBarStyle]})),[[Mi,this.loading||!this.loading&&this.entering]])}})}}),oD=zn({name:"LoadingBarProvider",props:Object.assign(Object.assign({},I_.props),{to:{type:[String,Object,Boolean],default:void 0},containerClass:String,containerStyle:[String,Object],loadingBarStyle:{type:Object}}),setup(e){const t=$b(),n=Et(null),o={start(){var e;t.value?null===(e=n.value)||void 0===e||e.start():tn((()=>{var e;null===(e=n.value)||void 0===e||e.start()}))},error(){var e;t.value?null===(e=n.value)||void 0===e||e.error():tn((()=>{var e;null===(e=n.value)||void 0===e||e.error()}))},finish(){var e;t.value?null===(e=n.value)||void 0===e||e.finish():tn((()=>{var e;null===(e=n.value)||void 0===e||e.finish()}))}},{mergedClsPrefixRef:r}=B_(e);return _o(ZB,o),_o(QB,{props:e,mergedClsPrefixRef:r}),Object.assign(o,{loadingBarRef:n})},render(){var e,t;return li(yr,null,li(Go,{disabled:!1===this.to,to:this.to||"body"},li(nD,{ref:"loadingBarRef",containerStyle:this.containerStyle,containerClass:this.containerClass})),null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e))}}),rD="n-menu",iD="n-submenu",aD="n-menu-item-group";function lD(e){const t=Po(rD),{props:n,mergedCollapsedRef:o}=t,r=Po(iD,null),i=Po(aD,null),a=ai((()=>"horizontal"===n.mode)),l=ai((()=>a.value?n.dropdownPlacement:"tmNodes"in e?"right-start":"right")),s=ai((()=>{var e;return Math.max(null!==(e=n.collapsedIconSize)&&void 0!==e?e:n.iconSize,n.iconSize)})),c=ai((()=>{var t;return!a.value&&e.root&&o.value&&null!==(t=n.collapsedIconSize)&&void 0!==t?t:n.iconSize})),u=ai((()=>{if(a.value)return;const{collapsedWidth:t,indent:l,rootIndent:c}=n,{root:u,isGroup:d}=e,p=void 0===c?l:c;return u?o.value?t/2-s.value/2:p:i&&"number"==typeof i.paddingLeftRef.value?l/2+i.paddingLeftRef.value:r&&"number"==typeof r.paddingLeftRef.value?(d?l/2:l)+r.paddingLeftRef.value:0})),d=ai((()=>{const{collapsedWidth:t,indent:r,rootIndent:i}=n,{value:l}=s,{root:c}=e;return a.value?8:c&&o.value?(void 0===i?r:i)+l+8-(t+l)/2:8}));return{dropdownPlacement:l,activeIconSize:c,maxIconSize:s,paddingLeft:u,iconMarginRight:d,NMenu:t,NSubmenu:r}}const sD={internalKey:{type:[String,Number],required:!0},root:Boolean,isGroup:Boolean,level:{type:Number,required:!0},title:[String,Function],extra:[String,Function]},cD=Object.assign(Object.assign({},sD),{tmNode:{type:Object,required:!0},tmNodes:{type:Array,required:!0}}),uD=zn({name:"MenuOptionGroup",props:cD,setup(e){_o(iD,null);const t=lD(e);_o(aD,{paddingLeftRef:t.paddingLeft});const{mergedClsPrefixRef:n,props:o}=Po(rD);return function(){const{value:r}=n,i=t.paddingLeft.value,{nodeProps:a}=o,l=null==a?void 0:a(e.tmNode.rawNode);return li("div",{class:`${r}-menu-item-group`,role:"group"},li("div",Object.assign({},l,{class:[`${r}-menu-item-group-title`,null==l?void 0:l.class],style:[(null==l?void 0:l.style)||"",void 0!==i?`padding-left: ${i}px;`:""]}),hg(e.title),e.extra?li(yr,null," ",hg(e.extra)):null),li("div",null,e.tmNodes.map((e=>CD(e,o)))))}}}),dD=zn({name:"MenuOptionContent",props:{collapsed:Boolean,disabled:Boolean,title:[String,Function],icon:Function,extra:[String,Function],showArrow:Boolean,childActive:Boolean,hover:Boolean,paddingLeft:Number,selected:Boolean,maxIconSize:{type:Number,required:!0},activeIconSize:{type:Number,required:!0},iconMarginRight:{type:Number,required:!0},clsPrefix:{type:String,required:!0},onClick:Function,tmNode:{type:Object,required:!0},isEllipsisPlaceholder:Boolean},setup(e){const{props:t}=Po(rD);return{menuProps:t,style:ai((()=>{const{paddingLeft:t}=e;return{paddingLeft:t&&`${t}px`}})),iconStyle:ai((()=>{const{maxIconSize:t,activeIconSize:n,iconMarginRight:o}=e;return{width:`${t}px`,height:`${t}px`,fontSize:`${n}px`,marginRight:`${o}px`}}))}},render(){const{clsPrefix:e,tmNode:t,menuProps:{renderIcon:n,renderLabel:o,renderExtra:r,expandIcon:i}}=this,a=n?n(t.rawNode):hg(this.icon);return li("div",{onClick:e=>{var t;null===(t=this.onClick)||void 0===t||t.call(this,e)},role:"none",class:[`${e}-menu-item-content`,{[`${e}-menu-item-content--selected`]:this.selected,[`${e}-menu-item-content--collapsed`]:this.collapsed,[`${e}-menu-item-content--child-active`]:this.childActive,[`${e}-menu-item-content--disabled`]:this.disabled,[`${e}-menu-item-content--hover`]:this.hover}],style:this.style},a&&li("div",{class:`${e}-menu-item-content__icon`,style:this.iconStyle,role:"none"},[a]),li("div",{class:`${e}-menu-item-content-header`,role:"none"},this.isEllipsisPlaceholder?this.title:o?o(t.rawNode):hg(this.title),this.extra||r?li("span",{class:`${e}-menu-item-content-header__extra`}," ",r?r(t.rawNode):hg(this.extra)):null),this.showArrow?li(CT,{ariaHidden:!0,class:`${e}-menu-item-content__arrow`,clsPrefix:e},{default:()=>i?i(t.rawNode):li(gT,null)}):null)}}),pD=Object.assign(Object.assign({},sD),{rawNodes:{type:Array,default:()=>[]},tmNodes:{type:Array,default:()=>[]},tmNode:{type:Object,required:!0},disabled:Boolean,icon:Function,onClick:Function,domId:String,virtualChildActive:{type:Boolean,default:void 0},isEllipsisPlaceholder:Boolean}),hD=zn({name:"Submenu",props:pD,setup(e){const t=lD(e),{NMenu:n,NSubmenu:o}=t,{props:r,mergedCollapsedRef:i,mergedThemeRef:a}=n,l=ai((()=>{const{disabled:t}=e;return!!(null==o?void 0:o.mergedDisabledRef.value)||!!r.disabled||t})),s=Et(!1);return _o(iD,{paddingLeftRef:t.paddingLeft,mergedDisabledRef:l}),_o(aD,null),{menuProps:r,mergedTheme:a,doSelect:n.doSelect,inverted:n.invertedRef,isHorizontal:n.isHorizontalRef,mergedClsPrefix:n.mergedClsPrefixRef,maxIconSize:t.maxIconSize,activeIconSize:t.activeIconSize,iconMarginRight:t.iconMarginRight,dropdownPlacement:t.dropdownPlacement,dropdownShow:s,paddingLeft:t.paddingLeft,mergedDisabled:l,mergedValue:n.mergedValueRef,childActive:mb((()=>{var t;return null!==(t=e.virtualChildActive)&&void 0!==t?t:n.activePathRef.value.includes(e.internalKey)})),collapsed:ai((()=>!("horizontal"===r.mode||!i.value&&n.mergedExpandedKeysRef.value.includes(e.internalKey)))),dropdownEnabled:ai((()=>!l.value&&("horizontal"===r.mode||i.value))),handlePopoverShowChange:function(e){s.value=e},handleClick:function(){l.value||(i.value||n.toggleExpand(e.internalKey),function(){const{onClick:t}=e;t&&t()}())}}},render(){var e;const{mergedClsPrefix:t,menuProps:{renderIcon:n,renderLabel:o}}=this,r=()=>{const{isHorizontal:e,paddingLeft:t,collapsed:n,mergedDisabled:o,maxIconSize:r,activeIconSize:i,title:a,childActive:l,icon:s,handleClick:c,menuProps:{nodeProps:u},dropdownShow:d,iconMarginRight:p,tmNode:h,mergedClsPrefix:f,isEllipsisPlaceholder:v,extra:m}=this,g=null==u?void 0:u(h.rawNode);return li("div",Object.assign({},g,{class:[`${f}-menu-item`,null==g?void 0:g.class],role:"menuitem"}),li(dD,{tmNode:h,paddingLeft:t,collapsed:n,disabled:o,iconMarginRight:p,maxIconSize:r,activeIconSize:i,title:a,extra:m,showArrow:!e,childActive:l,clsPrefix:f,icon:s,hover:d,onClick:c,isEllipsisPlaceholder:v}))},i=()=>li(yT,null,{default:()=>{const{tmNodes:e,collapsed:n}=this;return n?null:li("div",{class:`${t}-submenu-children`,role:"menu"},e.map((e=>CD(e,this.menuProps))))}});return this.root?li(DF,Object.assign({size:"large",trigger:"hover"},null===(e=this.menuProps)||void 0===e?void 0:e.dropdownProps,{themeOverrides:this.mergedTheme.peerOverrides.Dropdown,theme:this.mergedTheme.peers.Dropdown,builtinThemeOverrides:{fontSizeLarge:"14px",optionIconSizeLarge:"18px"},value:this.mergedValue,disabled:!this.dropdownEnabled,placement:this.dropdownPlacement,keyField:this.menuProps.keyField,labelField:this.menuProps.labelField,childrenField:this.menuProps.childrenField,onUpdateShow:this.handlePopoverShowChange,options:this.rawNodes,onSelect:this.doSelect,inverted:this.inverted,renderIcon:n,renderLabel:o}),{default:()=>li("div",{class:`${t}-submenu`,role:"menu","aria-expanded":!this.collapsed,id:this.domId},r(),this.isHorizontal?null:i())}):li("div",{class:`${t}-submenu`,role:"menu","aria-expanded":!this.collapsed,id:this.domId},r(),i())}}),fD=Object.assign(Object.assign({},sD),{tmNode:{type:Object,required:!0},disabled:Boolean,icon:Function,onClick:Function}),vD=zn({name:"MenuOption",props:fD,setup(e){const t=lD(e),{NSubmenu:n,NMenu:o}=t,{props:r,mergedClsPrefixRef:i,mergedCollapsedRef:a}=o,l=n?n.mergedDisabledRef:{value:!1},s=ai((()=>l.value||e.disabled));return{mergedClsPrefix:i,dropdownPlacement:t.dropdownPlacement,paddingLeft:t.paddingLeft,iconMarginRight:t.iconMarginRight,maxIconSize:t.maxIconSize,activeIconSize:t.activeIconSize,mergedTheme:o.mergedThemeRef,menuProps:r,dropdownEnabled:mb((()=>e.root&&a.value&&"horizontal"!==r.mode&&!s.value)),selected:mb((()=>o.mergedValueRef.value===e.internalKey)),mergedDisabled:s,handleClick:function(t){s.value||(o.doSelect(e.internalKey,e.tmNode.rawNode),function(t){const{onClick:n}=e;n&&n(t)}(t))}}},render(){const{mergedClsPrefix:e,mergedTheme:t,tmNode:n,menuProps:{renderLabel:o,nodeProps:r}}=this,i=null==r?void 0:r(n.rawNode);return li("div",Object.assign({},i,{role:"menuitem",class:[`${e}-menu-item`,null==i?void 0:i.class]}),li(NM,{theme:t.peers.Tooltip,themeOverrides:t.peerOverrides.Tooltip,trigger:"hover",placement:this.dropdownPlacement,disabled:!this.dropdownEnabled||void 0===this.title,internalExtraClass:["menu-tooltip"]},{default:()=>o?o(n.rawNode):hg(this.title),trigger:()=>li(dD,{tmNode:n,clsPrefix:e,paddingLeft:this.paddingLeft,iconMarginRight:this.iconMarginRight,maxIconSize:this.maxIconSize,activeIconSize:this.activeIconSize,selected:this.selected,title:this.title,extra:this.extra,disabled:this.mergedDisabled,icon:this.icon,onClick:this.handleClick})}))}}),mD=zn({name:"MenuDivider",setup(){const e=Po(rD),{mergedClsPrefixRef:t,isHorizontalRef:n}=e;return()=>n.value?null:li("div",{class:`${t.value}-menu-divider`})}}),gD=pg(cD),bD=pg(fD),yD=pg(pD);function xD(e){return"divider"===e.type||"render"===e.type}function CD(e,t){const{rawNode:n}=e,{show:o}=n;if(!1===o)return null;if(xD(n))return function(e){return"divider"===e.type}(n)?li(mD,Object.assign({key:e.key},n.props)):null;const{labelField:r}=t,{key:i,level:a,isGroup:l}=e,s=Object.assign(Object.assign({},n),{title:n.title||n[r],extra:n.titleExtra||n.extra,key:i,internalKey:i,level:a,root:0===a,isGroup:l});return e.children?e.isGroup?li(uD,sg(s,gD,{tmNode:e,tmNodes:e.children,key:i})):li(hD,sg(s,yD,{key:i,rawNodes:n[t.childrenField],tmNodes:e.children,tmNode:e})):li(vD,sg(s,bD,{key:i,tmNode:e}))}const wD=[eb("&::before","background-color: var(--n-item-color-hover);"),ob("arrow","\n color: var(--n-arrow-color-hover);\n "),ob("icon","\n color: var(--n-item-icon-color-hover);\n "),nb("menu-item-content-header","\n color: var(--n-item-text-color-hover);\n ",[eb("a","\n color: var(--n-item-text-color-hover);\n "),ob("extra","\n color: var(--n-item-text-color-hover);\n ")])],kD=[ob("icon","\n color: var(--n-item-icon-color-hover-horizontal);\n "),nb("menu-item-content-header","\n color: var(--n-item-text-color-hover-horizontal);\n ",[eb("a","\n color: var(--n-item-text-color-hover-horizontal);\n "),ob("extra","\n color: var(--n-item-text-color-hover-horizontal);\n ")])],SD=eb([nb("menu","\n background-color: var(--n-color);\n color: var(--n-item-text-color);\n overflow: hidden;\n transition: background-color .3s var(--n-bezier);\n box-sizing: border-box;\n font-size: var(--n-font-size);\n padding-bottom: 6px;\n ",[rb("horizontal","\n max-width: 100%;\n width: 100%;\n display: flex;\n overflow: hidden;\n padding-bottom: 0;\n ",[nb("submenu","margin: 0;"),nb("menu-item","margin: 0;"),nb("menu-item-content","\n padding: 0 20px;\n border-bottom: 2px solid #0000;\n ",[eb("&::before","display: none;"),rb("selected","border-bottom: 2px solid var(--n-border-color-horizontal)")]),nb("menu-item-content",[rb("selected",[ob("icon","color: var(--n-item-icon-color-active-horizontal);"),nb("menu-item-content-header","\n color: var(--n-item-text-color-active-horizontal);\n ",[eb("a","color: var(--n-item-text-color-active-horizontal);"),ob("extra","color: var(--n-item-text-color-active-horizontal);")])]),rb("child-active","\n border-bottom: 2px solid var(--n-border-color-horizontal);\n ",[nb("menu-item-content-header","\n color: var(--n-item-text-color-child-active-horizontal);\n ",[eb("a","\n color: var(--n-item-text-color-child-active-horizontal);\n "),ob("extra","\n color: var(--n-item-text-color-child-active-horizontal);\n ")]),ob("icon","\n color: var(--n-item-icon-color-child-active-horizontal);\n ")]),ib("disabled",[ib("selected, child-active",[eb("&:focus-within",kD)]),rb("selected",[_D(null,[ob("icon","color: var(--n-item-icon-color-active-hover-horizontal);"),nb("menu-item-content-header","\n color: var(--n-item-text-color-active-hover-horizontal);\n ",[eb("a","color: var(--n-item-text-color-active-hover-horizontal);"),ob("extra","color: var(--n-item-text-color-active-hover-horizontal);")])])]),rb("child-active",[_D(null,[ob("icon","color: var(--n-item-icon-color-child-active-hover-horizontal);"),nb("menu-item-content-header","\n color: var(--n-item-text-color-child-active-hover-horizontal);\n ",[eb("a","color: var(--n-item-text-color-child-active-hover-horizontal);"),ob("extra","color: var(--n-item-text-color-child-active-hover-horizontal);")])])]),_D("border-bottom: 2px solid var(--n-border-color-horizontal);",kD)]),nb("menu-item-content-header",[eb("a","color: var(--n-item-text-color-horizontal);")])])]),ib("responsive",[nb("menu-item-content-header","\n overflow: hidden;\n text-overflow: ellipsis;\n ")]),rb("collapsed",[nb("menu-item-content",[rb("selected",[eb("&::before","\n background-color: var(--n-item-color-active-collapsed) !important;\n ")]),nb("menu-item-content-header","opacity: 0;"),ob("arrow","opacity: 0;"),ob("icon","color: var(--n-item-icon-color-collapsed);")])]),nb("menu-item","\n height: var(--n-item-height);\n margin-top: 6px;\n position: relative;\n "),nb("menu-item-content",'\n box-sizing: border-box;\n line-height: 1.75;\n height: 100%;\n display: grid;\n grid-template-areas: "icon content arrow";\n grid-template-columns: auto 1fr auto;\n align-items: center;\n cursor: pointer;\n position: relative;\n padding-right: 18px;\n transition:\n background-color .3s var(--n-bezier),\n padding-left .3s var(--n-bezier),\n border-color .3s var(--n-bezier);\n ',[eb("> *","z-index: 1;"),eb("&::before",'\n z-index: auto;\n content: "";\n background-color: #0000;\n position: absolute;\n left: 8px;\n right: 8px;\n top: 0;\n bottom: 0;\n pointer-events: none;\n border-radius: var(--n-border-radius);\n transition: background-color .3s var(--n-bezier);\n '),rb("disabled","\n opacity: .45;\n cursor: not-allowed;\n "),rb("collapsed",[ob("arrow","transform: rotate(0);")]),rb("selected",[eb("&::before","background-color: var(--n-item-color-active);"),ob("arrow","color: var(--n-arrow-color-active);"),ob("icon","color: var(--n-item-icon-color-active);"),nb("menu-item-content-header","\n color: var(--n-item-text-color-active);\n ",[eb("a","color: var(--n-item-text-color-active);"),ob("extra","color: var(--n-item-text-color-active);")])]),rb("child-active",[nb("menu-item-content-header","\n color: var(--n-item-text-color-child-active);\n ",[eb("a","\n color: var(--n-item-text-color-child-active);\n "),ob("extra","\n color: var(--n-item-text-color-child-active);\n ")]),ob("arrow","\n color: var(--n-arrow-color-child-active);\n "),ob("icon","\n color: var(--n-item-icon-color-child-active);\n ")]),ib("disabled",[ib("selected, child-active",[eb("&:focus-within",wD)]),rb("selected",[_D(null,[ob("arrow","color: var(--n-arrow-color-active-hover);"),ob("icon","color: var(--n-item-icon-color-active-hover);"),nb("menu-item-content-header","\n color: var(--n-item-text-color-active-hover);\n ",[eb("a","color: var(--n-item-text-color-active-hover);"),ob("extra","color: var(--n-item-text-color-active-hover);")])])]),rb("child-active",[_D(null,[ob("arrow","color: var(--n-arrow-color-child-active-hover);"),ob("icon","color: var(--n-item-icon-color-child-active-hover);"),nb("menu-item-content-header","\n color: var(--n-item-text-color-child-active-hover);\n ",[eb("a","color: var(--n-item-text-color-child-active-hover);"),ob("extra","color: var(--n-item-text-color-child-active-hover);")])])]),rb("selected",[_D(null,[eb("&::before","background-color: var(--n-item-color-active-hover);")])]),_D(null,wD)]),ob("icon","\n grid-area: icon;\n color: var(--n-item-icon-color);\n transition:\n color .3s var(--n-bezier),\n font-size .3s var(--n-bezier),\n margin-right .3s var(--n-bezier);\n box-sizing: content-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n "),ob("arrow","\n grid-area: arrow;\n font-size: 16px;\n color: var(--n-arrow-color);\n transform: rotate(180deg);\n opacity: 1;\n transition:\n color .3s var(--n-bezier),\n transform 0.2s var(--n-bezier),\n opacity 0.2s var(--n-bezier);\n "),nb("menu-item-content-header","\n grid-area: content;\n transition:\n color .3s var(--n-bezier),\n opacity .3s var(--n-bezier);\n opacity: 1;\n white-space: nowrap;\n color: var(--n-item-text-color);\n ",[eb("a","\n outline: none;\n text-decoration: none;\n transition: color .3s var(--n-bezier);\n color: var(--n-item-text-color);\n ",[eb("&::before",'\n content: "";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n ')]),ob("extra","\n font-size: .93em;\n color: var(--n-group-text-color);\n transition: color .3s var(--n-bezier);\n ")])]),nb("submenu","\n cursor: pointer;\n position: relative;\n margin-top: 6px;\n ",[nb("menu-item-content","\n height: var(--n-item-height);\n "),nb("submenu-children","\n overflow: hidden;\n padding: 0;\n ",[sE({duration:".2s"})])]),nb("menu-item-group",[nb("menu-item-group-title","\n margin-top: 6px;\n color: var(--n-group-text-color);\n cursor: default;\n font-size: .93em;\n height: 36px;\n display: flex;\n align-items: center;\n transition:\n padding-left .3s var(--n-bezier),\n color .3s var(--n-bezier);\n ")])]),nb("menu-tooltip",[eb("a","\n color: inherit;\n text-decoration: none;\n ")]),nb("menu-divider","\n transition: background-color .3s var(--n-bezier);\n background-color: var(--n-divider-color);\n height: 1px;\n margin: 6px 18px;\n ")]);function _D(e,t){return[rb("hover",e,t),eb("&:hover",e,t)]}const PD=zn({name:"Menu",props:Object.assign(Object.assign({},I_.props),{options:{type:Array,default:()=>[]},collapsed:{type:Boolean,default:void 0},collapsedWidth:{type:Number,default:48},iconSize:{type:Number,default:20},collapsedIconSize:{type:Number,default:24},rootIndent:Number,indent:{type:Number,default:32},labelField:{type:String,default:"label"},keyField:{type:String,default:"key"},childrenField:{type:String,default:"children"},disabledField:{type:String,default:"disabled"},defaultExpandAll:Boolean,defaultExpandedKeys:Array,expandedKeys:Array,value:[String,Number],defaultValue:{type:[String,Number],default:null},mode:{type:String,default:"vertical"},watchProps:{type:Array,default:void 0},disabled:Boolean,show:{type:Boolean,default:!0},inverted:Boolean,"onUpdate:expandedKeys":[Function,Array],onUpdateExpandedKeys:[Function,Array],onUpdateValue:[Function,Array],"onUpdate:value":[Function,Array],expandIcon:Function,renderIcon:Function,renderLabel:Function,renderExtra:Function,dropdownProps:Object,accordion:Boolean,nodeProps:Function,dropdownPlacement:{type:String,default:"bottom"},responsive:Boolean,items:Array,onOpenNamesChange:[Function,Array],onSelect:[Function,Array],onExpandedNamesChange:[Function,Array],expandedNames:Array,defaultExpandedNames:Array}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Menu","-menu",SD,LL,e,t),r=Po(OB,null),i=ai((()=>{var t;const{collapsed:n}=e;if(void 0!==n)return n;if(r){const{collapseModeRef:e,collapsedRef:n}=r;if("width"===e.value)return null!==(t=n.value)&&void 0!==t&&t}return!1})),a=ai((()=>{const{keyField:t,childrenField:n,disabledField:o}=e;return JT(e.items||e.options,{getIgnored:e=>xD(e),getChildren:e=>e[n],getDisabled:e=>e[o],getKey(e){var n;return null!==(n=e[t])&&void 0!==n?n:e.name}})})),l=ai((()=>new Set(a.value.treeNodes.map((e=>e.key))))),{watchProps:s}=e,c=Et(null);(null==s?void 0:s.includes("defaultValue"))?ir((()=>{c.value=e.defaultValue})):c.value=e.defaultValue;const u=Db(jt(e,"value"),c),d=Et([]),p=()=>{d.value=e.defaultExpandAll?a.value.getNonLeafKeys():e.defaultExpandedNames||e.defaultExpandedKeys||a.value.getPath(u.value,{includeSelf:!1}).keyPath};(null==s?void 0:s.includes("defaultExpandedKeys"))?ir(p):p();const h=Nb(e,["expandedNames","expandedKeys"]),f=Db(h,d),v=ai((()=>a.value.treeNodes)),m=ai((()=>a.value.getPath(u.value).keyPath));function g(t){const{"onUpdate:expandedKeys":n,onUpdateExpandedKeys:o,onExpandedNamesChange:r,onOpenNamesChange:i}=e;n&&dg(n,t),o&&dg(o,t),r&&dg(r,t),i&&dg(i,t),d.value=t}_o(rD,{props:e,mergedCollapsedRef:i,mergedThemeRef:o,mergedValueRef:u,mergedExpandedKeysRef:f,activePathRef:m,mergedClsPrefixRef:t,isHorizontalRef:ai((()=>"horizontal"===e.mode)),invertedRef:jt(e,"inverted"),doSelect:function(t,n){const{"onUpdate:value":o,onUpdateValue:r,onSelect:i}=e;r&&dg(r,t,n),o&&dg(o,t,n),i&&dg(i,t,n),c.value=t},toggleExpand:function(t){const n=Array.from(f.value),o=n.findIndex((e=>e===t));if(~o)n.splice(o,1);else{if(e.accordion&&l.value.has(t)){const e=n.findIndex((e=>l.value.has(e)));e>-1&&n.splice(e,1)}n.push(t)}g(n)}});const b=ai((()=>{const{inverted:t}=e,{common:{cubicBezierEaseInOut:n},self:r}=o.value,{borderRadius:i,borderColorHorizontal:a,fontSize:l,itemHeight:s,dividerColor:c}=r,u={"--n-divider-color":c,"--n-bezier":n,"--n-font-size":l,"--n-border-color-horizontal":a,"--n-border-radius":i,"--n-item-height":s};return t?(u["--n-group-text-color"]=r.groupTextColorInverted,u["--n-color"]=r.colorInverted,u["--n-item-text-color"]=r.itemTextColorInverted,u["--n-item-text-color-hover"]=r.itemTextColorHoverInverted,u["--n-item-text-color-active"]=r.itemTextColorActiveInverted,u["--n-item-text-color-child-active"]=r.itemTextColorChildActiveInverted,u["--n-item-text-color-child-active-hover"]=r.itemTextColorChildActiveInverted,u["--n-item-text-color-active-hover"]=r.itemTextColorActiveHoverInverted,u["--n-item-icon-color"]=r.itemIconColorInverted,u["--n-item-icon-color-hover"]=r.itemIconColorHoverInverted,u["--n-item-icon-color-active"]=r.itemIconColorActiveInverted,u["--n-item-icon-color-active-hover"]=r.itemIconColorActiveHoverInverted,u["--n-item-icon-color-child-active"]=r.itemIconColorChildActiveInverted,u["--n-item-icon-color-child-active-hover"]=r.itemIconColorChildActiveHoverInverted,u["--n-item-icon-color-collapsed"]=r.itemIconColorCollapsedInverted,u["--n-item-text-color-horizontal"]=r.itemTextColorHorizontalInverted,u["--n-item-text-color-hover-horizontal"]=r.itemTextColorHoverHorizontalInverted,u["--n-item-text-color-active-horizontal"]=r.itemTextColorActiveHorizontalInverted,u["--n-item-text-color-child-active-horizontal"]=r.itemTextColorChildActiveHorizontalInverted,u["--n-item-text-color-child-active-hover-horizontal"]=r.itemTextColorChildActiveHoverHorizontalInverted,u["--n-item-text-color-active-hover-horizontal"]=r.itemTextColorActiveHoverHorizontalInverted,u["--n-item-icon-color-horizontal"]=r.itemIconColorHorizontalInverted,u["--n-item-icon-color-hover-horizontal"]=r.itemIconColorHoverHorizontalInverted,u["--n-item-icon-color-active-horizontal"]=r.itemIconColorActiveHorizontalInverted,u["--n-item-icon-color-active-hover-horizontal"]=r.itemIconColorActiveHoverHorizontalInverted,u["--n-item-icon-color-child-active-horizontal"]=r.itemIconColorChildActiveHorizontalInverted,u["--n-item-icon-color-child-active-hover-horizontal"]=r.itemIconColorChildActiveHoverHorizontalInverted,u["--n-arrow-color"]=r.arrowColorInverted,u["--n-arrow-color-hover"]=r.arrowColorHoverInverted,u["--n-arrow-color-active"]=r.arrowColorActiveInverted,u["--n-arrow-color-active-hover"]=r.arrowColorActiveHoverInverted,u["--n-arrow-color-child-active"]=r.arrowColorChildActiveInverted,u["--n-arrow-color-child-active-hover"]=r.arrowColorChildActiveHoverInverted,u["--n-item-color-hover"]=r.itemColorHoverInverted,u["--n-item-color-active"]=r.itemColorActiveInverted,u["--n-item-color-active-hover"]=r.itemColorActiveHoverInverted,u["--n-item-color-active-collapsed"]=r.itemColorActiveCollapsedInverted):(u["--n-group-text-color"]=r.groupTextColor,u["--n-color"]=r.color,u["--n-item-text-color"]=r.itemTextColor,u["--n-item-text-color-hover"]=r.itemTextColorHover,u["--n-item-text-color-active"]=r.itemTextColorActive,u["--n-item-text-color-child-active"]=r.itemTextColorChildActive,u["--n-item-text-color-child-active-hover"]=r.itemTextColorChildActiveHover,u["--n-item-text-color-active-hover"]=r.itemTextColorActiveHover,u["--n-item-icon-color"]=r.itemIconColor,u["--n-item-icon-color-hover"]=r.itemIconColorHover,u["--n-item-icon-color-active"]=r.itemIconColorActive,u["--n-item-icon-color-active-hover"]=r.itemIconColorActiveHover,u["--n-item-icon-color-child-active"]=r.itemIconColorChildActive,u["--n-item-icon-color-child-active-hover"]=r.itemIconColorChildActiveHover,u["--n-item-icon-color-collapsed"]=r.itemIconColorCollapsed,u["--n-item-text-color-horizontal"]=r.itemTextColorHorizontal,u["--n-item-text-color-hover-horizontal"]=r.itemTextColorHoverHorizontal,u["--n-item-text-color-active-horizontal"]=r.itemTextColorActiveHorizontal,u["--n-item-text-color-child-active-horizontal"]=r.itemTextColorChildActiveHorizontal,u["--n-item-text-color-child-active-hover-horizontal"]=r.itemTextColorChildActiveHoverHorizontal,u["--n-item-text-color-active-hover-horizontal"]=r.itemTextColorActiveHoverHorizontal,u["--n-item-icon-color-horizontal"]=r.itemIconColorHorizontal,u["--n-item-icon-color-hover-horizontal"]=r.itemIconColorHoverHorizontal,u["--n-item-icon-color-active-horizontal"]=r.itemIconColorActiveHorizontal,u["--n-item-icon-color-active-hover-horizontal"]=r.itemIconColorActiveHoverHorizontal,u["--n-item-icon-color-child-active-horizontal"]=r.itemIconColorChildActiveHorizontal,u["--n-item-icon-color-child-active-hover-horizontal"]=r.itemIconColorChildActiveHoverHorizontal,u["--n-arrow-color"]=r.arrowColor,u["--n-arrow-color-hover"]=r.arrowColorHover,u["--n-arrow-color-active"]=r.arrowColorActive,u["--n-arrow-color-active-hover"]=r.arrowColorActiveHover,u["--n-arrow-color-child-active"]=r.arrowColorChildActive,u["--n-arrow-color-child-active-hover"]=r.arrowColorChildActiveHover,u["--n-item-color-hover"]=r.itemColorHover,u["--n-item-color-active"]=r.itemColorActive,u["--n-item-color-active-hover"]=r.itemColorActiveHover,u["--n-item-color-active-collapsed"]=r.itemColorActiveCollapsed),u})),y=n?KP("menu",ai((()=>e.inverted?"a":"b")),b,e):void 0,x=ig(),C=Et(null),w=Et(null);let k=!0;const S=()=>{var e;k?k=!1:null===(e=C.value)||void 0===e||e.sync({showAllItemsBeforeCalculate:!0})},_=Et(-1),P=ai((()=>{const t=_.value;return{children:-1===t?[]:e.options.slice(t)}})),T=ai((()=>{const{childrenField:t,disabledField:n,keyField:o}=e;return JT([P.value],{getIgnored:e=>xD(e),getChildren:e=>e[t],getDisabled:e=>e[n],getKey(e){var t;return null!==(t=e[o])&&void 0!==t?t:e.name}})})),A=ai((()=>JT([{}]).treeNodes[0]));return{mergedClsPrefix:t,controlledExpandedKeys:h,uncontrolledExpanededKeys:d,mergedExpandedKeys:f,uncontrolledValue:c,mergedValue:u,activePath:m,tmNodes:v,mergedTheme:o,mergedCollapsed:i,cssVars:n?void 0:b,themeClass:null==y?void 0:y.themeClass,overflowRef:C,counterRef:w,updateCounter:()=>{},onResize:S,onUpdateOverflow:function(e){e||(_.value=-1)},onUpdateCount:function(t){_.value=e.options.length-t},renderCounter:function(){var e;if(-1===_.value)return li(hD,{root:!0,level:0,key:"__ellpisisGroupPlaceholder__",internalKey:"__ellpisisGroupPlaceholder__",title:"···",tmNode:A.value,domId:x,isEllipsisPlaceholder:!0});const t=T.value.treeNodes[0],n=m.value,o=!!(null===(e=t.children)||void 0===e?void 0:e.some((e=>n.includes(e.key))));return li(hD,{level:0,root:!0,key:"__ellpisisGroup__",internalKey:"__ellpisisGroup__",title:"···",virtualChildActive:o,tmNode:t,domId:x,rawNodes:t.rawNode.children||[],tmNodes:t.children||[],isEllipsisPlaceholder:!0})},getCounter:function(){return document.getElementById(x)},onRender:null==y?void 0:y.onRender,showOption:t=>{const n=a.value.getPath(null!=t?t:u.value,{includeSelf:!1}).keyPath;if(!n.length)return;const o=Array.from(f.value),r=new Set([...o,...n]);e.accordion&&l.value.forEach((e=>{r.has(e)&&!n.includes(e)&&r.delete(e)})),g(Array.from(r))},deriveResponsiveState:S}},render(){const{mergedClsPrefix:e,mode:t,themeClass:n,onRender:o}=this;null==o||o();const r=()=>this.tmNodes.map((e=>CD(e,this.$props))),i="horizontal"===t&&this.responsive,a=()=>li("div",{role:"horizontal"===t?"menubar":"menu",class:[`${e}-menu`,n,`${e}-menu--${t}`,i&&`${e}-menu--responsive`,this.mergedCollapsed&&`${e}-menu--collapsed`],style:this.cssVars},i?li(Ex,{ref:"overflowRef",onUpdateOverflow:this.onUpdateOverflow,getCounter:this.getCounter,onUpdateCount:this.onUpdateCount,updateCounter:this.updateCounter,style:{width:"100%",display:"flex",overflow:"hidden"}},{default:r,counter:this.renderCounter}):r());return i?li(kx,{onResize:this.onResize},{default:a}):a()}}),TD={icon:Function,type:{type:String,default:"info"},content:[String,Number,Function],showIcon:{type:Boolean,default:!0},closable:Boolean,keepAliveOnHover:Boolean,onClose:Function,onMouseenter:Function,onMouseleave:Function},AD="n-message-api",zD="n-message-provider",RD=eb([nb("message-wrapper","\n margin: var(--n-margin);\n z-index: 0;\n transform-origin: top center;\n display: flex;\n ",[sE({overflow:"visible",originalTransition:"transform .3s var(--n-bezier)",enterToProps:{transform:"scale(1)"},leaveToProps:{transform:"scale(0.85)"}})]),nb("message","\n box-sizing: border-box;\n display: flex;\n align-items: center;\n transition:\n color .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier),\n background-color .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n transform .3s var(--n-bezier),\n margin-bottom .3s var(--n-bezier);\n padding: var(--n-padding);\n border-radius: var(--n-border-radius);\n flex-wrap: nowrap;\n overflow: hidden;\n max-width: var(--n-max-width);\n color: var(--n-text-color);\n background-color: var(--n-color);\n box-shadow: var(--n-box-shadow);\n ",[ob("content","\n display: inline-block;\n line-height: var(--n-line-height);\n font-size: var(--n-font-size);\n "),ob("icon","\n position: relative;\n margin: var(--n-icon-margin);\n height: var(--n-icon-size);\n width: var(--n-icon-size);\n font-size: var(--n-icon-size);\n flex-shrink: 0;\n ",[["default","info","success","warning","error","loading"].map((e=>rb(`${e}-type`,[eb("> *",`\n color: var(--n-icon-color-${e});\n transition: color .3s var(--n-bezier);\n `)]))),eb("> *","\n position: absolute;\n left: 0;\n top: 0;\n right: 0;\n bottom: 0;\n ",[PT()])]),ob("close","\n margin: var(--n-close-margin);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n flex-shrink: 0;\n ",[eb("&:hover","\n color: var(--n-close-icon-color-hover);\n "),eb("&:active","\n color: var(--n-close-icon-color-pressed);\n ")])]),nb("message-container","\n z-index: 6000;\n position: fixed;\n height: 0;\n overflow: visible;\n display: flex;\n flex-direction: column;\n align-items: center;\n ",[rb("top","\n top: 12px;\n left: 0;\n right: 0;\n "),rb("top-left","\n top: 12px;\n left: 12px;\n right: 0;\n align-items: flex-start;\n "),rb("top-right","\n top: 12px;\n left: 0;\n right: 12px;\n align-items: flex-end;\n "),rb("bottom","\n bottom: 4px;\n left: 0;\n right: 0;\n justify-content: flex-end;\n "),rb("bottom-left","\n bottom: 4px;\n left: 12px;\n right: 0;\n justify-content: flex-end;\n align-items: flex-start;\n "),rb("bottom-right","\n bottom: 4px;\n left: 0;\n right: 12px;\n justify-content: flex-end;\n align-items: flex-end;\n ")])]),ED={info:()=>li(uT,null),success:()=>li(hT,null),warning:()=>li(fT,null),error:()=>li(iT,null),default:()=>null},OD=zn({name:"Message",props:Object.assign(Object.assign({},TD),{render:Function}),setup(e){const{inlineThemeDisabled:t,mergedRtlRef:n}=B_(e),{props:o,mergedClsPrefixRef:r}=Po(zD),i=GP("Message",n,r),a=I_("Message","-message",RD,xL,o,r),l=ai((()=>{const{type:t}=e,{common:{cubicBezierEaseInOut:n},self:{padding:o,margin:r,maxWidth:i,iconMargin:l,closeMargin:s,closeSize:c,iconSize:u,fontSize:d,lineHeight:p,borderRadius:h,iconColorInfo:f,iconColorSuccess:v,iconColorWarning:m,iconColorError:g,iconColorLoading:b,closeIconSize:y,closeBorderRadius:x,[ub("textColor",t)]:C,[ub("boxShadow",t)]:w,[ub("color",t)]:k,[ub("closeColorHover",t)]:S,[ub("closeColorPressed",t)]:_,[ub("closeIconColor",t)]:P,[ub("closeIconColorPressed",t)]:T,[ub("closeIconColorHover",t)]:A}}=a.value;return{"--n-bezier":n,"--n-margin":r,"--n-padding":o,"--n-max-width":i,"--n-font-size":d,"--n-icon-margin":l,"--n-icon-size":u,"--n-close-icon-size":y,"--n-close-border-radius":x,"--n-close-size":c,"--n-close-margin":s,"--n-text-color":C,"--n-color":k,"--n-box-shadow":w,"--n-icon-color-info":f,"--n-icon-color-success":v,"--n-icon-color-warning":m,"--n-icon-color-error":g,"--n-icon-color-loading":b,"--n-close-color-hover":S,"--n-close-color-pressed":_,"--n-close-icon-color":P,"--n-close-icon-color-pressed":T,"--n-close-icon-color-hover":A,"--n-line-height":p,"--n-border-radius":h}})),s=t?KP("message",ai((()=>e.type[0])),l,{}):void 0;return{mergedClsPrefix:r,rtlEnabled:i,messageProviderProps:o,handleClose(){var t;null===(t=e.onClose)||void 0===t||t.call(e)},cssVars:t?void 0:l,themeClass:null==s?void 0:s.themeClass,onRender:null==s?void 0:s.onRender,placement:o.placement}},render(){const{render:e,type:t,closable:n,content:o,mergedClsPrefix:r,cssVars:i,themeClass:a,onRender:l,icon:s,handleClose:c,showIcon:u}=this;let d;return null==l||l(),li("div",{class:[`${r}-message-wrapper`,a],onMouseenter:this.onMouseenter,onMouseleave:this.onMouseleave,style:[{alignItems:this.placement.startsWith("top")?"flex-start":"flex-end"},i]},e?e(this.$props):li("div",{class:[`${r}-message ${r}-message--${t}-type`,this.rtlEnabled&&`${r}-message--rtl`]},(d=function(e,t,n){if("function"==typeof e)return e();{const e="loading"===t?li(RT,{clsPrefix:n,strokeWidth:24,scale:.85}):ED[t]();return e?li(CT,{clsPrefix:n,key:t},{default:()=>e}):null}}(s,t,r))&&u?li("div",{class:`${r}-message__icon ${r}-message__icon--${t}-type`},li(bT,null,{default:()=>d})):null,li("div",{class:`${r}-message__content`},hg(o)),n?li(kT,{clsPrefix:r,class:`${r}-message__close`,onClick:c,absolute:!0}):null))}}),MD=zn({name:"MessageEnvironment",props:Object.assign(Object.assign({},TD),{duration:{type:Number,default:3e3},onAfterLeave:Function,onLeave:Function,internalKey:{type:String,required:!0},onInternalAfterLeave:Function,onHide:Function,onAfterHide:Function}),setup(e){let t=null;const n=Et(!0);function o(){const{duration:n}=e;n&&(t=window.setTimeout(r,n))}function r(){const{onHide:o}=e;n.value=!1,t&&(window.clearTimeout(t),t=null),o&&o()}return $n((()=>{o()})),{show:n,hide:r,handleClose:function(){const{onClose:t}=e;t&&t(),r()},handleAfterLeave:function(){const{onAfterLeave:t,onInternalAfterLeave:n,onAfterHide:o,internalKey:r}=e;t&&t(),n&&n(r),o&&o()},handleMouseleave:function(e){e.currentTarget===e.target&&o()},handleMouseenter:function(e){e.currentTarget===e.target&&null!==t&&(window.clearTimeout(t),t=null)},deactivate:function(){r()}}},render(){return li(yT,{appear:!0,onAfterLeave:this.handleAfterLeave,onLeave:this.onLeave},{default:()=>[this.show?li(OD,{content:this.content,type:this.type,icon:this.icon,showIcon:this.showIcon,closable:this.closable,onClose:this.handleClose,onMouseenter:this.keepAliveOnHover?this.handleMouseenter:void 0,onMouseleave:this.keepAliveOnHover?this.handleMouseleave:void 0}):null]})}}),FD=zn({name:"MessageProvider",props:Object.assign(Object.assign({},I_.props),{to:[String,Object],duration:{type:Number,default:3e3},keepAliveOnHover:Boolean,max:Number,placement:{type:String,default:"top"},closable:Boolean,containerClass:String,containerStyle:[String,Object]}),setup(e){const{mergedClsPrefixRef:t}=B_(e),n=Et([]),o=Et({}),r={create:(e,t)=>i(e,Object.assign({type:"default"},t)),info:(e,t)=>i(e,Object.assign(Object.assign({},t),{type:"info"})),success:(e,t)=>i(e,Object.assign(Object.assign({},t),{type:"success"})),warning:(e,t)=>i(e,Object.assign(Object.assign({},t),{type:"warning"})),error:(e,t)=>i(e,Object.assign(Object.assign({},t),{type:"error"})),loading:(e,t)=>i(e,Object.assign(Object.assign({},t),{type:"loading"})),destroyAll:function(){Object.values(o.value).forEach((e=>{e.hide()}))}};function i(t,r){const i=ig(),a=vt(Object.assign(Object.assign({},r),{content:t,key:i,destroy:()=>{var e;null===(e=o.value[i])||void 0===e||e.hide()}})),{max:l}=e;return l&&n.value.length>=l&&n.value.shift(),n.value.push(a),a}return _o(zD,{props:e,mergedClsPrefixRef:t}),_o(AD,r),Object.assign({mergedClsPrefix:t,messageRefs:o,messageList:n,handleAfterLeave:function(e){n.value.splice(n.value.findIndex((t=>t.key===e)),1),delete o.value[e]}},r)},render(){var e,t,n;return li(yr,null,null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e),this.messageList.length?li(Go,{to:null!==(n=this.to)&&void 0!==n?n:"body"},li("div",{class:[`${this.mergedClsPrefix}-message-container`,`${this.mergedClsPrefix}-message-container--${this.placement}`,this.containerClass],key:"message-container",style:this.containerStyle},this.messageList.map((e=>li(MD,Object.assign({ref:t=>{t&&(this.messageRefs[e.key]=t)},internalKey:e.key,onInternalAfterLeave:this.handleAfterLeave},cg(e,["destroy"],void 0),{duration:void 0===e.duration?this.duration:e.duration,keepAliveOnHover:void 0===e.keepAliveOnHover?this.keepAliveOnHover:e.keepAliveOnHover,closable:void 0===e.closable?this.closable:e.closable})))))):null)}}),ID=zn({name:"ModalEnvironment",props:Object.assign(Object.assign({},zI),{internalKey:{type:String,required:!0},onInternalAfterLeave:{type:Function,required:!0}}),setup(e){const t=Et(!0);function n(){t.value=!1}return{show:t,hide:n,handleUpdateShow:function(e){t.value=e},handleAfterLeave:function(){const{onInternalAfterLeave:t,internalKey:n,onAfterLeave:o}=e;t&&t(n),o&&o()},handleCloseClick:function(){const{onClose:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&n()})):n()},handleNegativeClick:function(){const{onNegativeClick:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&n()})):n()},handlePositiveClick:function(){const{onPositiveClick:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&n()})):n()},handleMaskClick:function(t){const{onMaskClick:o,maskClosable:r}=e;o&&(o(t),r&&n())},handleEsc:function(){const{onEsc:t}=e;t&&t()}}},render(){const{handleUpdateShow:e,handleAfterLeave:t,handleMaskClick:n,handleEsc:o,show:r}=this;return li(RI,Object.assign({},this.$props,{show:r,onUpdateShow:e,onMaskClick:n,onEsc:o,onAfterLeave:t,internalAppear:!0,internalModal:!0}))}}),LD="n-modal-provider",BD="n-modal-api",DD=zn({name:"ModalProvider",props:{to:[String,Object]},setup(){const e=Bb(64),t=Ob(),n=Et([]),o={},r={create:function(e={}){const t=ig(),r=vt(Object.assign(Object.assign({},e),{key:t,destroy:()=>{var e;null===(e=o[`n-modal-${t}`])||void 0===e||e.hide()}}));return n.value.push(r),r},destroyAll:function(){Object.values(o).forEach((e=>{null==e||e.hide()}))}};return _o(BD,r),_o(LD,{clickedRef:Bb(64),clickedPositionRef:Ob()}),_o("n-modal-reactive-list",n),_o(LD,{clickedRef:e,clickedPositionRef:t}),Object.assign(Object.assign({},r),{modalList:n,modalInstRefs:o,handleAfterLeave:function(e){const{value:t}=n;t.splice(t.findIndex((t=>t.key===e)),1)}})},render(){var e,t;return li(yr,null,[this.modalList.map((e=>{var t;return li(ID,cg(e,["destroy"],{to:null!==(t=e.to)&&void 0!==t?t:this.to,ref:t=>{null===t?delete this.modalInstRefs[`n-modal-${e.key}`]:this.modalInstRefs[`n-modal-${e.key}`]=t},internalKey:e.key,onInternalAfterLeave:this.handleAfterLeave}))})),null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e)])}}),$D="n-notification-provider",ND=zn({name:"NotificationContainer",props:{scrollable:{type:Boolean,required:!0},placement:{type:String,required:!0}},setup(){const{mergedThemeRef:e,mergedClsPrefixRef:t,wipTransitionCountRef:n}=Po($D),o=Et(null);return ir((()=>{var e,t;n.value>0?null===(e=null==o?void 0:o.value)||void 0===e||e.classList.add("transitioning"):null===(t=null==o?void 0:o.value)||void 0===t||t.classList.remove("transitioning")})),{selfRef:o,mergedTheme:e,mergedClsPrefix:t,transitioning:n}},render(){const{$slots:e,scrollable:t,mergedClsPrefix:n,mergedTheme:o,placement:r}=this;return li("div",{ref:"selfRef",class:[`${n}-notification-container`,t&&`${n}-notification-container--scrollable`,`${n}-notification-container--${r}`]},t?li(lR,{theme:o.peers.Scrollbar,themeOverrides:o.peerOverrides.Scrollbar,contentStyle:{overflow:"hidden"}},e):e)}}),jD={info:()=>li(uT,null),success:()=>li(hT,null),warning:()=>li(fT,null),error:()=>li(iT,null),default:()=>null},HD={closable:{type:Boolean,default:!0},type:{type:String,default:"default"},avatar:Function,title:[String,Function],description:[String,Function],content:[String,Function],meta:[String,Function],action:[String,Function],onClose:{type:Function,required:!0},keepAliveOnHover:Boolean,onMouseenter:Function,onMouseleave:Function},WD=pg(HD),UD=zn({name:"Notification",props:HD,setup(e){const{mergedClsPrefixRef:t,mergedThemeRef:n,props:o}=Po($D),{inlineThemeDisabled:r,mergedRtlRef:i}=B_(),a=GP("Notification",i,t),l=ai((()=>{const{type:t}=e,{self:{color:o,textColor:r,closeIconColor:i,closeIconColorHover:a,closeIconColorPressed:l,headerTextColor:s,descriptionTextColor:c,actionTextColor:u,borderRadius:d,headerFontWeight:p,boxShadow:h,lineHeight:f,fontSize:v,closeMargin:m,closeSize:g,width:b,padding:y,closeIconSize:x,closeBorderRadius:C,closeColorHover:w,closeColorPressed:k,titleFontSize:S,metaFontSize:_,descriptionFontSize:P,[ub("iconColor",t)]:T},common:{cubicBezierEaseOut:A,cubicBezierEaseIn:z,cubicBezierEaseInOut:R}}=n.value,{left:E,right:O,top:M,bottom:F}=Bm(y);return{"--n-color":o,"--n-font-size":v,"--n-text-color":r,"--n-description-text-color":c,"--n-action-text-color":u,"--n-title-text-color":s,"--n-title-font-weight":p,"--n-bezier":R,"--n-bezier-ease-out":A,"--n-bezier-ease-in":z,"--n-border-radius":d,"--n-box-shadow":h,"--n-close-border-radius":C,"--n-close-color-hover":w,"--n-close-color-pressed":k,"--n-close-icon-color":i,"--n-close-icon-color-hover":a,"--n-close-icon-color-pressed":l,"--n-line-height":f,"--n-icon-color":T,"--n-close-margin":m,"--n-close-size":g,"--n-close-icon-size":x,"--n-width":b,"--n-padding-left":E,"--n-padding-right":O,"--n-padding-top":M,"--n-padding-bottom":F,"--n-title-font-size":S,"--n-meta-font-size":_,"--n-description-font-size":P}})),s=r?KP("notification",ai((()=>e.type[0])),l,o):void 0;return{mergedClsPrefix:t,showAvatar:ai((()=>e.avatar||"default"!==e.type)),handleCloseClick(){e.onClose()},rtlEnabled:a,cssVars:r?void 0:l,themeClass:null==s?void 0:s.themeClass,onRender:null==s?void 0:s.onRender}},render(){var e;const{mergedClsPrefix:t}=this;return null===(e=this.onRender)||void 0===e||e.call(this),li("div",{class:[`${t}-notification-wrapper`,this.themeClass],onMouseenter:this.onMouseenter,onMouseleave:this.onMouseleave,style:this.cssVars},li("div",{class:[`${t}-notification`,this.rtlEnabled&&`${t}-notification--rtl`,this.themeClass,{[`${t}-notification--closable`]:this.closable,[`${t}-notification--show-avatar`]:this.showAvatar}],style:this.cssVars},this.showAvatar?li("div",{class:`${t}-notification__avatar`},this.avatar?hg(this.avatar):"default"!==this.type?li(CT,{clsPrefix:t},{default:()=>jD[this.type]()}):null):null,this.closable?li(kT,{clsPrefix:t,class:`${t}-notification__close`,onClick:this.handleCloseClick}):null,li("div",{ref:"bodyRef",class:`${t}-notification-main`},this.title?li("div",{class:`${t}-notification-main__header`},hg(this.title)):null,this.description?li("div",{class:`${t}-notification-main__description`},hg(this.description)):null,this.content?li("pre",{class:`${t}-notification-main__content`},hg(this.content)):null,this.meta||this.action?li("div",{class:`${t}-notification-main-footer`},this.meta?li("div",{class:`${t}-notification-main-footer__meta`},hg(this.meta)):null,this.action?li("div",{class:`${t}-notification-main-footer__action`},hg(this.action)):null):null)))}}),VD=Object.assign(Object.assign({},HD),{duration:Number,onClose:Function,onLeave:Function,onAfterEnter:Function,onAfterLeave:Function,onHide:Function,onAfterShow:Function,onAfterHide:Function}),qD=zn({name:"NotificationEnvironment",props:Object.assign(Object.assign({},VD),{internalKey:{type:String,required:!0},onInternalAfterLeave:{type:Function,required:!0}}),setup(e){const{wipTransitionCountRef:t}=Po($D),n=Et(!0);let o=null;function r(){n.value=!1,o&&window.clearTimeout(o)}return $n((()=>{e.duration&&(o=window.setTimeout(r,e.duration))})),{show:n,hide:r,handleClose:function(){const{onClose:t}=e;t?Promise.resolve(t()).then((e=>{!1!==e&&r()})):r()},handleAfterLeave:function(){t.value--;const{onAfterLeave:n,onInternalAfterLeave:o,onAfterHide:r,internalKey:i}=e;n&&n(),o(i),r&&r()},handleLeave:function(t){const{onHide:n}=e;n&&n(),t.style.maxHeight="0",t.offsetHeight},handleBeforeLeave:function(e){t.value++,e.style.maxHeight=`${e.offsetHeight}px`,e.style.height=`${e.offsetHeight}px`,e.offsetHeight},handleAfterEnter:function(n){t.value--,n.style.height="",n.style.maxHeight="";const{onAfterEnter:o,onAfterShow:r}=e;o&&o(),r&&r()},handleBeforeEnter:function(e){t.value++,tn((()=>{e.style.height=`${e.offsetHeight}px`,e.style.maxHeight="0",e.style.transition="none",e.offsetHeight,e.style.transition="",e.style.maxHeight=e.style.height}))},handleMouseenter:function(e){e.currentTarget===e.target&&null!==o&&(window.clearTimeout(o),o=null)},handleMouseleave:function(t){t.currentTarget===t.target&&function(){const{duration:t}=e;t&&(o=window.setTimeout(r,t))}()}}},render(){return li(vi,{name:"notification-transition",appear:!0,onBeforeEnter:this.handleBeforeEnter,onAfterEnter:this.handleAfterEnter,onBeforeLeave:this.handleBeforeLeave,onLeave:this.handleLeave,onAfterLeave:this.handleAfterLeave},{default:()=>this.show?li(UD,Object.assign({},sg(this.$props,WD),{onClose:this.handleClose,onMouseenter:this.duration&&this.keepAliveOnHover?this.handleMouseenter:void 0,onMouseleave:this.duration&&this.keepAliveOnHover?this.handleMouseleave:void 0})):null})}}),KD=eb([nb("notification-container","\n z-index: 4000;\n position: fixed;\n overflow: visible;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n ",[eb(">",[nb("scrollbar","\n width: initial;\n overflow: visible;\n height: -moz-fit-content !important;\n height: fit-content !important;\n max-height: 100vh !important;\n ",[eb(">",[nb("scrollbar-container","\n height: -moz-fit-content !important;\n height: fit-content !important;\n max-height: 100vh !important;\n ",[nb("scrollbar-content","\n padding-top: 12px;\n padding-bottom: 33px;\n ")])])])]),rb("top, top-right, top-left","\n top: 12px;\n ",[eb("&.transitioning >",[nb("scrollbar",[eb(">",[nb("scrollbar-container","\n min-height: 100vh !important;\n ")])])])]),rb("bottom, bottom-right, bottom-left","\n bottom: 12px;\n ",[eb(">",[nb("scrollbar",[eb(">",[nb("scrollbar-container",[nb("scrollbar-content","\n padding-bottom: 12px;\n ")])])])]),nb("notification-wrapper","\n display: flex;\n align-items: flex-end;\n margin-bottom: 0;\n margin-top: 12px;\n ")]),rb("top, bottom","\n left: 50%;\n transform: translateX(-50%);\n ",[nb("notification-wrapper",[eb("&.notification-transition-enter-from, &.notification-transition-leave-to","\n transform: scale(0.85);\n "),eb("&.notification-transition-leave-from, &.notification-transition-enter-to","\n transform: scale(1);\n ")])]),rb("top",[nb("notification-wrapper","\n transform-origin: top center;\n ")]),rb("bottom",[nb("notification-wrapper","\n transform-origin: bottom center;\n ")]),rb("top-right, bottom-right",[nb("notification","\n margin-left: 28px;\n margin-right: 16px;\n ")]),rb("top-left, bottom-left",[nb("notification","\n margin-left: 16px;\n margin-right: 28px;\n ")]),rb("top-right","\n right: 0;\n ",[GD("top-right")]),rb("top-left","\n left: 0;\n ",[GD("top-left")]),rb("bottom-right","\n right: 0;\n ",[GD("bottom-right")]),rb("bottom-left","\n left: 0;\n ",[GD("bottom-left")]),rb("scrollable",[rb("top-right","\n top: 0;\n "),rb("top-left","\n top: 0;\n "),rb("bottom-right","\n bottom: 0;\n "),rb("bottom-left","\n bottom: 0;\n ")]),nb("notification-wrapper","\n margin-bottom: 12px;\n ",[eb("&.notification-transition-enter-from, &.notification-transition-leave-to","\n opacity: 0;\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n "),eb("&.notification-transition-leave-from, &.notification-transition-enter-to","\n opacity: 1;\n "),eb("&.notification-transition-leave-active","\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n transform .3s var(--n-bezier-ease-in),\n max-height .3s var(--n-bezier),\n margin-top .3s linear,\n margin-bottom .3s linear,\n box-shadow .3s var(--n-bezier);\n "),eb("&.notification-transition-enter-active","\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n transform .3s var(--n-bezier-ease-out),\n max-height .3s var(--n-bezier),\n margin-top .3s linear,\n margin-bottom .3s linear,\n box-shadow .3s var(--n-bezier);\n ")]),nb("notification","\n background-color: var(--n-color);\n color: var(--n-text-color);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier);\n font-family: inherit;\n font-size: var(--n-font-size);\n font-weight: 400;\n position: relative;\n display: flex;\n overflow: hidden;\n flex-shrink: 0;\n padding-left: var(--n-padding-left);\n padding-right: var(--n-padding-right);\n width: var(--n-width);\n max-width: calc(100vw - 16px - 16px);\n border-radius: var(--n-border-radius);\n box-shadow: var(--n-box-shadow);\n box-sizing: border-box;\n opacity: 1;\n ",[ob("avatar",[nb("icon","\n color: var(--n-icon-color);\n "),nb("base-icon","\n color: var(--n-icon-color);\n ")]),rb("show-avatar",[nb("notification-main","\n margin-left: 40px;\n width: calc(100% - 40px); \n ")]),rb("closable",[nb("notification-main",[eb("> *:first-child","\n padding-right: 20px;\n ")]),ob("close","\n position: absolute;\n top: 0;\n right: 0;\n margin: var(--n-close-margin);\n transition:\n background-color .3s var(--n-bezier),\n color .3s var(--n-bezier);\n ")]),ob("avatar","\n position: absolute;\n top: var(--n-padding-top);\n left: var(--n-padding-left);\n width: 28px;\n height: 28px;\n font-size: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n ",[nb("icon","transition: color .3s var(--n-bezier);")]),nb("notification-main","\n padding-top: var(--n-padding-top);\n padding-bottom: var(--n-padding-bottom);\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n margin-left: 8px;\n width: calc(100% - 8px);\n ",[nb("notification-main-footer","\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 12px;\n ",[ob("meta","\n font-size: var(--n-meta-font-size);\n transition: color .3s var(--n-bezier-ease-out);\n color: var(--n-description-text-color);\n "),ob("action","\n cursor: pointer;\n transition: color .3s var(--n-bezier-ease-out);\n color: var(--n-action-text-color);\n ")]),ob("header","\n font-weight: var(--n-title-font-weight);\n font-size: var(--n-title-font-size);\n transition: color .3s var(--n-bezier-ease-out);\n color: var(--n-title-text-color);\n "),ob("description","\n margin-top: 8px;\n font-size: var(--n-description-font-size);\n white-space: pre-wrap;\n word-wrap: break-word;\n transition: color .3s var(--n-bezier-ease-out);\n color: var(--n-description-text-color);\n "),ob("content","\n line-height: var(--n-line-height);\n margin: 12px 0 0 0;\n font-family: inherit;\n white-space: pre-wrap;\n word-wrap: break-word;\n transition: color .3s var(--n-bezier-ease-out);\n color: var(--n-text-color);\n ",[eb("&:first-child","margin: 0;")])])])])]);function GD(e){const t=e.split("-")[1];return nb("notification-wrapper",[eb("&.notification-transition-enter-from, &.notification-transition-leave-to",`\n transform: translate(${"left"===t?"calc(-100%)":"calc(100%)"}, 0);\n `),eb("&.notification-transition-leave-from, &.notification-transition-enter-to","\n transform: translate(0, 0);\n ")])}const XD="n-notification-api",YD=zn({name:"NotificationProvider",props:Object.assign(Object.assign({},I_.props),{containerClass:String,containerStyle:[String,Object],to:[String,Object],scrollable:{type:Boolean,default:!0},max:Number,placement:{type:String,default:"top-right"},keepAliveOnHover:Boolean}),setup(e){const{mergedClsPrefixRef:t}=B_(e),n=Et([]),o={},r=new Set;function i(t){const i=ig(),a=()=>{r.add(i),o[i]&&o[i].hide()},l=vt(Object.assign(Object.assign({},t),{key:i,destroy:a,hide:a,deactivate:a})),{max:s}=e;if(s&&n.value.length-r.size>=s){let e=!1,t=0;for(const i of n.value){if(!r.has(i.key)){o[i.key]&&(i.destroy(),e=!0);break}t++}e||n.value.splice(t,1)}return n.value.push(l),l}const a=["info","success","warning","error"].map((e=>t=>i(Object.assign(Object.assign({},t),{type:e})))),l=I_("Notification","-notification",KD,mL,e,t),s={create:i,info:a[0],success:a[1],warning:a[2],error:a[3],open:function(e){return i(e)},destroyAll:function(){Object.values(n.value).forEach((e=>{e.hide()}))}},c=Et(0);return _o(XD,s),_o($D,{props:e,mergedClsPrefixRef:t,mergedThemeRef:l,wipTransitionCountRef:c}),Object.assign({mergedClsPrefix:t,notificationList:n,notificationRefs:o,handleAfterLeave:function(e){r.delete(e),n.value.splice(n.value.findIndex((t=>t.key===e)),1)}},s)},render(){var e,t,n;const{placement:o}=this;return li(yr,null,null===(t=(e=this.$slots).default)||void 0===t?void 0:t.call(e),this.notificationList.length?li(Go,{to:null!==(n=this.to)&&void 0!==n?n:"body"},li(ND,{class:this.containerClass,style:this.containerStyle,scrollable:this.scrollable&&"top"!==o&&"bottom"!==o,placement:o},{default:()=>this.notificationList.map((e=>li(qD,Object.assign({ref:t=>{const n=e.key;null===t?delete this.notificationRefs[n]:this.notificationRefs[n]=t}},cg(e,["destroy","hide","deactivate"]),{internalKey:e.key,onInternalAfterLeave:this.handleAfterLeave,keepAliveOnHover:void 0===e.keepAliveOnHover?this.keepAliveOnHover:e.keepAliveOnHover}))))})):null)}}),QD=eb([nb("progress",{display:"inline-block"},[nb("progress-icon","\n color: var(--n-icon-color);\n transition: color .3s var(--n-bezier);\n "),rb("line","\n width: 100%;\n display: block;\n ",[nb("progress-content","\n display: flex;\n align-items: center;\n ",[nb("progress-graph",{flex:1})]),nb("progress-custom-content",{marginLeft:"14px"}),nb("progress-icon","\n width: 30px;\n padding-left: 14px;\n height: var(--n-icon-size-line);\n line-height: var(--n-icon-size-line);\n font-size: var(--n-icon-size-line);\n ",[rb("as-text","\n color: var(--n-text-color-line-outer);\n text-align: center;\n width: 40px;\n font-size: var(--n-font-size);\n padding-left: 4px;\n transition: color .3s var(--n-bezier);\n ")])]),rb("circle, dashboard",{width:"120px"},[nb("progress-custom-content","\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translateX(-50%) translateY(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n "),nb("progress-text","\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translateX(-50%) translateY(-50%);\n display: flex;\n align-items: center;\n color: inherit;\n font-size: var(--n-font-size-circle);\n color: var(--n-text-color-circle);\n font-weight: var(--n-font-weight-circle);\n transition: color .3s var(--n-bezier);\n white-space: nowrap;\n "),nb("progress-icon","\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translateX(-50%) translateY(-50%);\n display: flex;\n align-items: center;\n color: var(--n-icon-color);\n font-size: var(--n-icon-size-circle);\n ")]),rb("multiple-circle","\n width: 200px;\n color: inherit;\n ",[nb("progress-text","\n font-weight: var(--n-font-weight-circle);\n color: var(--n-text-color-circle);\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translateX(-50%) translateY(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color .3s var(--n-bezier);\n ")]),nb("progress-content",{position:"relative"}),nb("progress-graph",{position:"relative"},[nb("progress-graph-circle",[eb("svg",{verticalAlign:"bottom"}),nb("progress-graph-circle-fill","\n stroke: var(--n-fill-color);\n transition:\n opacity .3s var(--n-bezier),\n stroke .3s var(--n-bezier),\n stroke-dasharray .3s var(--n-bezier);\n ",[rb("empty",{opacity:0})]),nb("progress-graph-circle-rail","\n transition: stroke .3s var(--n-bezier);\n overflow: hidden;\n stroke: var(--n-rail-color);\n ")]),nb("progress-graph-line",[rb("indicator-inside",[nb("progress-graph-line-rail","\n height: 16px;\n line-height: 16px;\n border-radius: 10px;\n ",[nb("progress-graph-line-fill","\n height: inherit;\n border-radius: 10px;\n "),nb("progress-graph-line-indicator","\n background: #0000;\n white-space: nowrap;\n text-align: right;\n margin-left: 14px;\n margin-right: 14px;\n height: inherit;\n font-size: 12px;\n color: var(--n-text-color-line-inner);\n transition: color .3s var(--n-bezier);\n ")])]),rb("indicator-inside-label","\n height: 16px;\n display: flex;\n align-items: center;\n ",[nb("progress-graph-line-rail","\n flex: 1;\n transition: background-color .3s var(--n-bezier);\n "),nb("progress-graph-line-indicator","\n background: var(--n-fill-color);\n font-size: 12px;\n transform: translateZ(0);\n display: flex;\n vertical-align: middle;\n height: 16px;\n line-height: 16px;\n padding: 0 10px;\n border-radius: 10px;\n position: absolute;\n white-space: nowrap;\n color: var(--n-text-color-line-inner);\n transition:\n right .2s var(--n-bezier),\n color .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n ")]),nb("progress-graph-line-rail","\n position: relative;\n overflow: hidden;\n height: var(--n-rail-height);\n border-radius: 5px;\n background-color: var(--n-rail-color);\n transition: background-color .3s var(--n-bezier);\n ",[nb("progress-graph-line-fill","\n background: var(--n-fill-color);\n position: relative;\n border-radius: 5px;\n height: inherit;\n width: 100%;\n max-width: 0%;\n transition:\n background-color .3s var(--n-bezier),\n max-width .2s var(--n-bezier);\n ",[rb("processing",[eb("&::after",'\n content: "";\n background-image: var(--n-line-bg-processing);\n animation: progress-processing-animation 2s var(--n-bezier) infinite;\n ')])])])])])]),eb("@keyframes progress-processing-animation","\n 0% {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n right: 100%;\n opacity: 1;\n }\n 66% {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n right: 0;\n opacity: 0;\n }\n 100% {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n right: 0;\n opacity: 0;\n }\n ")]),ZD={success:li(hT,null),error:li(iT,null),warning:li(fT,null),info:li(uT,null)},JD=zn({name:"ProgressLine",props:{clsPrefix:{type:String,required:!0},percentage:{type:Number,default:0},railColor:String,railStyle:[String,Object],fillColor:String,status:{type:String,required:!0},indicatorPlacement:{type:String,required:!0},indicatorTextColor:String,unit:{type:String,default:"%"},processing:{type:Boolean,required:!0},showIndicator:{type:Boolean,required:!0},height:[String,Number],railBorderRadius:[String,Number],fillBorderRadius:[String,Number]},setup(e,{slots:t}){const n=ai((()=>Ag(e.height))),o=ai((()=>void 0!==e.railBorderRadius?Ag(e.railBorderRadius):void 0!==e.height?Ag(e.height,{c:.5}):"")),r=ai((()=>void 0!==e.fillBorderRadius?Ag(e.fillBorderRadius):void 0!==e.railBorderRadius?Ag(e.railBorderRadius):void 0!==e.height?Ag(e.height,{c:.5}):""));return()=>{const{indicatorPlacement:i,railColor:a,railStyle:l,percentage:s,unit:c,indicatorTextColor:u,status:d,showIndicator:p,fillColor:h,processing:f,clsPrefix:v}=e;return li("div",{class:`${v}-progress-content`,role:"none"},li("div",{class:`${v}-progress-graph`,"aria-hidden":!0},li("div",{class:[`${v}-progress-graph-line`,{[`${v}-progress-graph-line--indicator-${i}`]:!0}]},li("div",{class:`${v}-progress-graph-line-rail`,style:[{backgroundColor:a,height:n.value,borderRadius:o.value},l]},li("div",{class:[`${v}-progress-graph-line-fill`,f&&`${v}-progress-graph-line-fill--processing`],style:{maxWidth:`${e.percentage}%`,backgroundColor:h,height:n.value,lineHeight:n.value,borderRadius:r.value}},"inside"===i?li("div",{class:`${v}-progress-graph-line-indicator`,style:{color:u}},t.default?t.default():`${s}${c}`):null)))),p&&"outside"===i?li("div",null,t.default?li("div",{class:`${v}-progress-custom-content`,style:{color:u},role:"none"},t.default()):"default"===d?li("div",{role:"none",class:`${v}-progress-icon ${v}-progress-icon--as-text`,style:{color:u}},s,c):li("div",{class:`${v}-progress-icon`,"aria-hidden":!0},li(CT,{clsPrefix:v},{default:()=>ZD[d]}))):null)}}}),e$={success:li(hT,null),error:li(iT,null),warning:li(fT,null),info:li(uT,null)},t$=zn({name:"ProgressCircle",props:{clsPrefix:{type:String,required:!0},status:{type:String,required:!0},strokeWidth:{type:Number,required:!0},fillColor:String,railColor:String,railStyle:[String,Object],percentage:{type:Number,default:0},offsetDegree:{type:Number,default:0},showIndicator:{type:Boolean,required:!0},indicatorTextColor:String,unit:String,viewBoxWidth:{type:Number,required:!0},gapDegree:{type:Number,required:!0},gapOffsetDegree:{type:Number,default:0}},setup(e,{slots:t}){function n(t,n,o){const{gapDegree:r,viewBoxWidth:i,strokeWidth:a}=e,l=50,s=50+a/2;return{pathString:`M ${s},${s} m 0,50\n a 50,50 0 1 1 0,-100\n a 50,50 0 1 1 0,100`,pathStyle:{stroke:o,strokeDasharray:`${t/100*(2*Math.PI*l-r)}px ${8*i}px`,strokeDashoffset:`-${r/2}px`,transformOrigin:n?"center":void 0,transform:n?`rotate(${n}deg)`:void 0}}}return()=>{const{fillColor:o,railColor:r,strokeWidth:i,offsetDegree:a,status:l,percentage:s,showIndicator:c,indicatorTextColor:u,unit:d,gapOffsetDegree:p,clsPrefix:h}=e,{pathString:f,pathStyle:v}=n(100,0,r),{pathString:m,pathStyle:g}=n(s,a,o),b=100+i;return li("div",{class:`${h}-progress-content`,role:"none"},li("div",{class:`${h}-progress-graph`,"aria-hidden":!0},li("div",{class:`${h}-progress-graph-circle`,style:{transform:p?`rotate(${p}deg)`:void 0}},li("svg",{viewBox:`0 0 ${b} ${b}`},li("g",null,li("path",{class:`${h}-progress-graph-circle-rail`,d:f,"stroke-width":i,"stroke-linecap":"round",fill:"none",style:v})),li("g",null,li("path",{class:[`${h}-progress-graph-circle-fill`,0===s&&`${h}-progress-graph-circle-fill--empty`],d:m,"stroke-width":i,"stroke-linecap":"round",fill:"none",style:g}))))),c?li("div",null,t.default?li("div",{class:`${h}-progress-custom-content`,role:"none"},t.default()):"default"!==l?li("div",{class:`${h}-progress-icon`,"aria-hidden":!0},li(CT,{clsPrefix:h},{default:()=>e$[l]})):li("div",{class:`${h}-progress-text`,style:{color:u},role:"none"},li("span",{class:`${h}-progress-text__percentage`},s),li("span",{class:`${h}-progress-text__unit`},d))):null)}}});function n$(e,t,n=100){return`m ${n/2} ${n/2-e} a ${e} ${e} 0 1 1 0 ${2*e} a ${e} ${e} 0 1 1 0 -${2*e}`}const o$=zn({name:"ProgressMultipleCircle",props:{clsPrefix:{type:String,required:!0},viewBoxWidth:{type:Number,required:!0},percentage:{type:Array,default:[0]},strokeWidth:{type:Number,required:!0},circleGap:{type:Number,required:!0},showIndicator:{type:Boolean,required:!0},fillColor:{type:Array,default:()=>[]},railColor:{type:Array,default:()=>[]},railStyle:{type:Array,default:()=>[]}},setup(e,{slots:t}){const n=ai((()=>e.percentage.map(((t,n)=>`${Math.PI*t/100*(e.viewBoxWidth/2-e.strokeWidth/2*(1+2*n)-e.circleGap*n)*2}, ${8*e.viewBoxWidth}`))));return()=>{const{viewBoxWidth:o,strokeWidth:r,circleGap:i,showIndicator:a,fillColor:l,railColor:s,railStyle:c,percentage:u,clsPrefix:d}=e;return li("div",{class:`${d}-progress-content`,role:"none"},li("div",{class:`${d}-progress-graph`,"aria-hidden":!0},li("div",{class:`${d}-progress-graph-circle`},li("svg",{viewBox:`0 0 ${o} ${o}`},u.map(((e,t)=>li("g",{key:t},li("path",{class:`${d}-progress-graph-circle-rail`,d:n$(o/2-r/2*(1+2*t)-i*t,0,o),"stroke-width":r,"stroke-linecap":"round",fill:"none",style:[{strokeDashoffset:0,stroke:s[t]},c[t]]}),li("path",{class:[`${d}-progress-graph-circle-fill`,0===e&&`${d}-progress-graph-circle-fill--empty`],d:n$(o/2-r/2*(1+2*t)-i*t,0,o),"stroke-width":r,"stroke-linecap":"round",fill:"none",style:{strokeDasharray:n.value[t],strokeDashoffset:0,stroke:l[t]}}))))))),a&&t.default?li("div",null,li("div",{class:`${d}-progress-text`},t.default())):null)}}}),r$=zn({name:"Progress",props:Object.assign(Object.assign({},I_.props),{processing:Boolean,type:{type:String,default:"line"},gapDegree:Number,gapOffsetDegree:Number,status:{type:String,default:"default"},railColor:[String,Array],railStyle:[String,Array],color:[String,Array],viewBoxWidth:{type:Number,default:100},strokeWidth:{type:Number,default:7},percentage:[Number,Array],unit:{type:String,default:"%"},showIndicator:{type:Boolean,default:!0},indicatorPosition:{type:String,default:"outside"},indicatorPlacement:{type:String,default:"outside"},indicatorTextColor:String,circleGap:{type:Number,default:1},height:Number,borderRadius:[String,Number],fillBorderRadius:[String,Number],offsetDegree:Number}),setup(e){const t=ai((()=>e.indicatorPlacement||e.indicatorPosition)),n=ai((()=>e.gapDegree||0===e.gapDegree?e.gapDegree:"dashboard"===e.type?75:void 0)),{mergedClsPrefixRef:o,inlineThemeDisabled:r}=B_(e),i=I_("Progress","-progress",QD,WL,e,o),a=ai((()=>{const{status:t}=e,{common:{cubicBezierEaseInOut:n},self:{fontSize:o,fontSizeCircle:r,railColor:a,railHeight:l,iconSizeCircle:s,iconSizeLine:c,textColorCircle:u,textColorLineInner:d,textColorLineOuter:p,lineBgProcessing:h,fontWeightCircle:f,[ub("iconColor",t)]:v,[ub("fillColor",t)]:m}}=i.value;return{"--n-bezier":n,"--n-fill-color":m,"--n-font-size":o,"--n-font-size-circle":r,"--n-font-weight-circle":f,"--n-icon-color":v,"--n-icon-size-circle":s,"--n-icon-size-line":c,"--n-line-bg-processing":h,"--n-rail-color":a,"--n-rail-height":l,"--n-text-color-circle":u,"--n-text-color-line-inner":d,"--n-text-color-line-outer":p}})),l=r?KP("progress",ai((()=>e.status[0])),a,e):void 0;return{mergedClsPrefix:o,mergedIndicatorPlacement:t,gapDeg:n,cssVars:r?void 0:a,themeClass:null==l?void 0:l.themeClass,onRender:null==l?void 0:l.onRender}},render(){const{type:e,cssVars:t,indicatorTextColor:n,showIndicator:o,status:r,railColor:i,railStyle:a,color:l,percentage:s,viewBoxWidth:c,strokeWidth:u,mergedIndicatorPlacement:d,unit:p,borderRadius:h,fillBorderRadius:f,height:v,processing:m,circleGap:g,mergedClsPrefix:b,gapDeg:y,gapOffsetDegree:x,themeClass:C,$slots:w,onRender:k}=this;return null==k||k(),li("div",{class:[C,`${b}-progress`,`${b}-progress--${e}`,`${b}-progress--${r}`],style:t,"aria-valuemax":100,"aria-valuemin":0,"aria-valuenow":s,role:"circle"===e||"line"===e||"dashboard"===e?"progressbar":"none"},"circle"===e||"dashboard"===e?li(t$,{clsPrefix:b,status:r,showIndicator:o,indicatorTextColor:n,railColor:i,fillColor:l,railStyle:a,offsetDegree:this.offsetDegree,percentage:s,viewBoxWidth:c,strokeWidth:u,gapDegree:void 0===y?"dashboard"===e?75:0:y,gapOffsetDegree:x,unit:p},w):"line"===e?li(JD,{clsPrefix:b,status:r,showIndicator:o,indicatorTextColor:n,railColor:i,fillColor:l,railStyle:a,percentage:s,processing:m,indicatorPlacement:d,unit:p,fillBorderRadius:f,railBorderRadius:h,height:v},w):"multiple-circle"===e?li(o$,{clsPrefix:b,strokeWidth:u,railColor:i,fillColor:l,railStyle:a,viewBoxWidth:c,percentage:s,showIndicator:o,circleGap:g},w):null)}}),i$={name:"QrCode",common:tz,self:e=>({borderRadius:e.borderRadius})},a$={name:"QrCode",common:qz,self:function(e){return{borderRadius:e.borderRadius}}},l$=eb([nb("qr-code","\n background: #fff;\n border-radius: var(--n-border-radius);\n display: inline-flex;\n ")]);var s$,c$;!function(e){class t{static encodeText(n,o){const r=e.QrSegment.makeSegments(n);return t.encodeSegments(r,o)}static encodeBinary(n,o){const r=e.QrSegment.makeBytes(n);return t.encodeSegments([r],o)}static encodeSegments(e,o,i=1,a=40,l=-1,s=!0){if(!(t.MIN_VERSION<=i&&i<=a&&a<=t.MAX_VERSION)||l<-1||l>7)throw new RangeError("Invalid value");let c,u;for(c=i;;c++){const n=8*t.getNumDataCodewords(c,o),i=r.getTotalBits(e,c);if(i<=n){u=i;break}if(c>=a)throw new RangeError("Data too long")}for(const n of[t.Ecc.MEDIUM,t.Ecc.QUARTILE,t.Ecc.HIGH])s&&u<=8*t.getNumDataCodewords(c,n)&&(o=n);const d=[];for(const t of e){n(t.mode.modeBits,4,d),n(t.numChars,t.mode.numCharCountBits(c),d);for(const e of t.getData())d.push(e)}const p=8*t.getNumDataCodewords(c,o);n(0,Math.min(4,p-d.length),d),n(0,(8-d.length%8)%8,d);for(let t=236;d.lengthh[t>>>3]|=e<<7-(7&t))),new t(c,o,h,l)}constructor(e,n,o,r){if(this.version=e,this.errorCorrectionLevel=n,this.modules=[],this.isFunction=[],et.MAX_VERSION)throw new RangeError("Version value out of range");if(r<-1||r>7)throw new RangeError("Mask value out of range");this.size=4*e+17;const i=[];for(let t=0;t=0&&e=0&&t>>9);const r=21522^(t<<10|n);for(let i=0;i<=5;i++)this.setFunctionModule(8,i,o(r,i));this.setFunctionModule(8,7,o(r,6)),this.setFunctionModule(8,8,o(r,7)),this.setFunctionModule(7,8,o(r,8));for(let i=9;i<15;i++)this.setFunctionModule(14-i,8,o(r,i));for(let i=0;i<8;i++)this.setFunctionModule(this.size-1-i,8,o(r,i));for(let i=8;i<15;i++)this.setFunctionModule(8,this.size-15+i,o(r,i));this.setFunctionModule(8,this.size-8,!0)}drawVersion(){if(this.version<7)return;let e=this.version;for(let n=0;n<12;n++)e=e<<1^7973*(e>>>11);const t=this.version<<12|e;for(let n=0;n<18;n++){const e=o(t,n),r=this.size-11+n%3,i=Math.floor(n/3);this.setFunctionModule(r,i,e),this.setFunctionModule(i,r,e)}}drawFinderPattern(e,t){for(let n=-4;n<=4;n++)for(let o=-4;o<=4;o++){const r=Math.max(Math.abs(o),Math.abs(n)),i=e+o,a=t+n;i>=0&&i=0&&a{(t!==s-i||n>=l)&&d.push(e[t])}));return d}drawCodewords(e){if(e.length!==Math.floor(t.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");let n=0;for(let t=this.size-1;t>=1;t-=2){6===t&&(t=5);for(let r=0;r>>3],7-(7&n)),n++)}}}applyMask(e){if(e<0||e>7)throw new RangeError("Mask value out of range");for(let t=0;t5&&e++):(this.finderPenaltyAddHistory(o,i),n||(e+=this.finderPenaltyCountPatterns(i)*t.PENALTY_N3),n=this.modules[r][a],o=1);e+=this.finderPenaltyTerminateAndCount(n,o,i)*t.PENALTY_N3}for(let r=0;r5&&e++):(this.finderPenaltyAddHistory(o,i),n||(e+=this.finderPenaltyCountPatterns(i)*t.PENALTY_N3),n=this.modules[a][r],o=1);e+=this.finderPenaltyTerminateAndCount(n,o,i)*t.PENALTY_N3}for(let r=0;re+(t?1:0)),n);const o=this.size*this.size;return e+=(Math.ceil(Math.abs(20*n-10*o)/o)-1)*t.PENALTY_N4,e}getAlignmentPatternPositions(){if(1===this.version)return[];{const e=Math.floor(this.version/7)+2,t=32===this.version?26:2*Math.ceil((4*this.version+4)/(2*e-2)),n=[6];for(let o=this.size-7;n.lengtht.MAX_VERSION)throw new RangeError("Version number out of range");let n=(16*e+128)*e+64;if(e>=2){const t=Math.floor(e/7)+2;n-=(25*t-10)*t-55,e>=7&&(n-=36)}return n}static getNumDataCodewords(e,n){return Math.floor(t.getNumRawDataModules(e)/8)-t.ECC_CODEWORDS_PER_BLOCK[n.ordinal][e]*t.NUM_ERROR_CORRECTION_BLOCKS[n.ordinal][e]}static reedSolomonComputeDivisor(e){if(e<1||e>255)throw new RangeError("Degree out of range");const n=[];for(let t=0;t0));for(const r of e){const e=r^o.shift();o.push(0),n.forEach(((n,r)=>o[r]^=t.reedSolomonMultiply(n,e)))}return o}static reedSolomonMultiply(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");let n=0;for(let o=7;o>=0;o--)n=n<<1^285*(n>>>7),n^=(t>>>o&1)*e;return n}finderPenaltyCountPatterns(e){const t=e[1],n=t>0&&e[2]===t&&e[3]===3*t&&e[4]===t&&e[5]===t;return(n&&e[0]>=4*t&&e[6]>=t?1:0)+(n&&e[6]>=4*t&&e[0]>=t?1:0)}finderPenaltyTerminateAndCount(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),t+=this.size,this.finderPenaltyAddHistory(t,n),this.finderPenaltyCountPatterns(n)}finderPenaltyAddHistory(e,t){0===t[0]&&(e+=this.size),t.pop(),t.unshift(e)}}function n(e,t,n){if(t<0||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(let o=t-1;o>=0;o--)n.push(e>>>o&1)}function o(e,t){return!!(e>>>t&1)}t.MIN_VERSION=1,t.MAX_VERSION=40,t.PENALTY_N1=3,t.PENALTY_N2=3,t.PENALTY_N3=40,t.PENALTY_N4=10,t.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],t.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],e.QrCode=t;class r{static makeBytes(e){const t=[];for(const o of e)n(o,8,t);return new r(r.Mode.BYTE,e.length,t)}static makeNumeric(e){if(!r.isNumeric(e))throw new RangeError("String contains non-numeric characters");const t=[];for(let o=0;o=1<({"--n-border-radius":o.value.self.borderRadius}))),i=n?KP("qr-code",void 0,r,e):void 0,a=Et(),l=ai((()=>{var t;const n=d$[e.errorCorrectionLevel];return u$.QrCode.encodeText(null!==(t=e.value)&&void 0!==t?t:"-",n)}));$n((()=>{const t=Et(0);let n=null;ir((()=>{"svg"!==e.type&&(t.value,function(e,t,n,o,r){const i=a.value;if(!i)return;const l=2*t,s=e.size,c=l/s;i.width=l,i.height=l;const u=i.getContext("2d");if(u){u.clearRect(0,0,i.width,i.height);for(let t=0;t=1?a:a*c,p=c<=1?a:a/c,h=l+(a-d)/2,f=s+(a-p)/2;u.drawImage(e,h,f,d,p)}}}(l.value,e.size,e.color,e.backgroundColor,n?{icon:n,iconBorderRadius:e.iconBorderRadius,iconSize:e.iconSize,iconBackgroundColor:e.iconBackgroundColor}:null))})),ir((()=>{if("svg"===e.type)return;const{iconSrc:o}=e;if(o){let e=!1;const r=new Image;return r.src=o,r.onload=()=>{e||(n=r,t.value++)},()=>{e=!0}}}))}));const s=ai((()=>function(t,n,o){const r=t.getModules(),i=r.length,a=r;let l="";const s=``,c=``;let u="";if(o){const{iconSrc:e,iconSize:t}=o,a=.1,l=Math.floor(n*a),s=i/n,c=(t||l)*s,d=(t||l)*s;u+=``}return l+=s,l+=c,l+=u,{innerHtml:l,numCells:i}}(l.value,e.size,e.iconSrc?{iconSrc:e.iconSrc,iconBorderRadius:e.iconBorderRadius,iconSize:e.iconSize,iconBackgroundColor:e.iconBackgroundColor}:null)));return{canvasRef:a,mergedClsPrefix:t,cssVars:n?void 0:r,themeClass:null==i?void 0:i.themeClass,svgInfo:s}},render(){const{mergedClsPrefix:e,backgroundColor:t,padding:n,cssVars:o,themeClass:r,size:i,type:a}=this;return li("div",{class:[`${e}-qr-code`,r],style:Object.assign({padding:"number"==typeof n?`${n}px`:n,backgroundColor:t,width:`${i}px`,height:`${i}px`},o)},"canvas"===a?li("canvas",{ref:"canvasRef",style:{width:`${i}px`,height:`${i}px`}}):li("svg",{height:i,width:i,viewBox:`0 0 ${this.svgInfo.numCells} ${this.svgInfo.numCells}`,role:"img",innerHTML:this.svgInfo.innerHtml}))}}),f$=li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},li("circle",{fill:"#FFCB4C",cx:"18",cy:"17.018",r:"17"}),li("path",{fill:"#65471B",d:"M14.524 21.036c-.145-.116-.258-.274-.312-.464-.134-.46.13-.918.59-1.021 4.528-1.021 7.577 1.363 7.706 1.465.384.306.459.845.173 1.205-.286.358-.828.401-1.211.097-.11-.084-2.523-1.923-6.182-1.098-.274.061-.554-.016-.764-.184z"}),li("ellipse",{fill:"#65471B",cx:"13.119",cy:"11.174",rx:"2.125",ry:"2.656"}),li("ellipse",{fill:"#65471B",cx:"24.375",cy:"12.236",rx:"2.125",ry:"2.656"}),li("path",{fill:"#F19020",d:"M17.276 35.149s1.265-.411 1.429-1.352c.173-.972-.624-1.167-.624-1.167s1.041-.208 1.172-1.376c.123-1.101-.861-1.363-.861-1.363s.97-.4 1.016-1.539c.038-.959-.995-1.428-.995-1.428s5.038-1.221 5.556-1.341c.516-.12 1.32-.615 1.069-1.694-.249-1.08-1.204-1.118-1.697-1.003-.494.115-6.744 1.566-8.9 2.068l-1.439.334c-.54.127-.785-.11-.404-.512.508-.536.833-1.129.946-2.113.119-1.035-.232-2.313-.433-2.809-.374-.921-1.005-1.649-1.734-1.899-1.137-.39-1.945.321-1.542 1.561.604 1.854.208 3.375-.833 4.293-2.449 2.157-3.588 3.695-2.83 6.973.828 3.575 4.377 5.876 7.952 5.048l3.152-.681z"}),li("path",{fill:"#65471B",d:"M9.296 6.351c-.164-.088-.303-.224-.391-.399-.216-.428-.04-.927.393-1.112 4.266-1.831 7.699-.043 7.843.034.433.231.608.747.391 1.154-.216.405-.74.546-1.173.318-.123-.063-2.832-1.432-6.278.047-.257.109-.547.085-.785-.042zm12.135 3.75c-.156-.098-.286-.243-.362-.424-.187-.442.023-.927.468-1.084 4.381-1.536 7.685.48 7.823.567.415.26.555.787.312 1.178-.242.39-.776.495-1.191.238-.12-.072-2.727-1.621-6.267-.379-.266.091-.553.046-.783-.096z"})),v$=li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},li("path",{fill:"#FFCC4D",d:"M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"}),li("ellipse",{fill:"#664500",cx:"18",cy:"27",rx:"5",ry:"6"}),li("path",{fill:"#664500",d:"M5.999 11c-.208 0-.419-.065-.599-.2-.442-.331-.531-.958-.2-1.4C8.462 5.05 12.816 5 13 5c.552 0 1 .448 1 1 0 .551-.445.998-.996 1-.155.002-3.568.086-6.204 3.6-.196.262-.497.4-.801.4zm24.002 0c-.305 0-.604-.138-.801-.4-2.64-3.521-6.061-3.598-6.206-3.6-.55-.006-.994-.456-.991-1.005C22.006 5.444 22.45 5 23 5c.184 0 4.537.05 7.8 4.4.332.442.242 1.069-.2 1.4-.18.135-.39.2-.599.2zm-16.087 4.5l1.793-1.793c.391-.391.391-1.023 0-1.414s-1.023-.391-1.414 0L12.5 14.086l-1.793-1.793c-.391-.391-1.023-.391-1.414 0s-.391 1.023 0 1.414l1.793 1.793-1.793 1.793c-.391.391-.391 1.023 0 1.414.195.195.451.293.707.293s.512-.098.707-.293l1.793-1.793 1.793 1.793c.195.195.451.293.707.293s.512-.098.707-.293c.391-.391.391-1.023 0-1.414L13.914 15.5zm11 0l1.793-1.793c.391-.391.391-1.023 0-1.414s-1.023-.391-1.414 0L23.5 14.086l-1.793-1.793c-.391-.391-1.023-.391-1.414 0s-.391 1.023 0 1.414l1.793 1.793-1.793 1.793c-.391.391-.391 1.023 0 1.414.195.195.451.293.707.293s.512-.098.707-.293l1.793-1.793 1.793 1.793c.195.195.451.293.707.293s.512-.098.707-.293c.391-.391.391-1.023 0-1.414L24.914 15.5z"})),m$=li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},li("ellipse",{fill:"#292F33",cx:"18",cy:"26",rx:"18",ry:"10"}),li("ellipse",{fill:"#66757F",cx:"18",cy:"24",rx:"18",ry:"10"}),li("path",{fill:"#E1E8ED",d:"M18 31C3.042 31 1 16 1 12h34c0 2-1.958 19-17 19z"}),li("path",{fill:"#77B255",d:"M35 12.056c0 5.216-7.611 9.444-17 9.444S1 17.271 1 12.056C1 6.84 8.611 3.611 18 3.611s17 3.229 17 8.445z"}),li("ellipse",{fill:"#A6D388",cx:"18",cy:"13",rx:"15",ry:"7"}),li("path",{d:"M21 17c-.256 0-.512-.098-.707-.293-2.337-2.337-2.376-4.885-.125-8.262.739-1.109.9-2.246.478-3.377-.461-1.236-1.438-1.996-1.731-2.077-.553 0-.958-.443-.958-.996 0-.552.491-.995 1.043-.995.997 0 2.395 1.153 3.183 2.625 1.034 1.933.91 4.039-.351 5.929-1.961 2.942-1.531 4.332-.125 5.738.391.391.391 1.023 0 1.414-.195.196-.451.294-.707.294zm-6-2c-.256 0-.512-.098-.707-.293-2.337-2.337-2.376-4.885-.125-8.262.727-1.091.893-2.083.494-2.947-.444-.961-1.431-1.469-1.684-1.499-.552 0-.989-.447-.989-1 0-.552.458-1 1.011-1 .997 0 2.585.974 3.36 2.423.481.899 1.052 2.761-.528 5.131-1.961 2.942-1.531 4.332-.125 5.738.391.391.391 1.023 0 1.414-.195.197-.451.295-.707.295z",fill:"#5C913B"})),g$=li("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 36 36"},li("path",{fill:"#EF9645",d:"M15.5 2.965c1.381 0 2.5 1.119 2.5 2.5v.005L20.5.465c1.381 0 2.5 1.119 2.5 2.5V4.25l2.5-1.535c1.381 0 2.5 1.119 2.5 2.5V8.75L29 18H15.458L15.5 2.965z"}),li("path",{fill:"#FFDC5D",d:"M4.625 16.219c1.381-.611 3.354.208 4.75 2.188.917 1.3 1.187 3.151 2.391 3.344.46.073 1.234-.313 1.234-1.397V4.5s0-2 2-2 2 2 2 2v11.633c0-.029 1-.064 1-.082V2s0-2 2-2 2 2 2 2v14.053c0 .017 1 .041 1 .069V4.25s0-2 2-2 2 2 2 2v12.638c0 .118 1 .251 1 .398V8.75s0-2 2-2 2 2 2 2V24c0 6.627-5.373 12-12 12-4.775 0-8.06-2.598-9.896-5.292C8.547 28.423 8.096 26.051 8 25.334c0 0-.123-1.479-1.156-2.865-1.469-1.969-2.5-3.156-3.125-3.866-.317-.359-.625-1.707.906-2.384z"})),b$=nb("result","\n color: var(--n-text-color);\n line-height: var(--n-line-height);\n font-size: var(--n-font-size);\n transition:\n color .3s var(--n-bezier);\n",[nb("result-icon","\n display: flex;\n justify-content: center;\n transition: color .3s var(--n-bezier);\n ",[ob("status-image","\n font-size: var(--n-icon-size);\n width: 1em;\n height: 1em;\n "),nb("base-icon","\n color: var(--n-icon-color);\n font-size: var(--n-icon-size);\n ")]),nb("result-content",{marginTop:"24px"}),nb("result-footer","\n margin-top: 24px;\n text-align: center;\n "),nb("result-header",[ob("title","\n margin-top: 16px;\n font-weight: var(--n-title-font-weight);\n transition: color .3s var(--n-bezier);\n text-align: center;\n color: var(--n-title-text-color);\n font-size: var(--n-title-font-size);\n "),ob("description","\n margin-top: 4px;\n text-align: center;\n font-size: var(--n-font-size);\n ")])]),y$={403:()=>g$,404:()=>f$,418:()=>m$,500:()=>v$,info:()=>li(uT,null),success:()=>li(hT,null),warning:()=>li(fT,null),error:()=>li(iT,null)},x$=zn({name:"Result",props:Object.assign(Object.assign({},I_.props),{size:{type:String,default:"medium"},status:{type:String,default:"info"},title:String,description:String}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Result","-result",b$,GL,e,t),r=ai((()=>{const{size:t,status:n}=e,{common:{cubicBezierEaseInOut:r},self:{textColor:i,lineHeight:a,titleTextColor:l,titleFontWeight:s,[ub("iconColor",n)]:c,[ub("fontSize",t)]:u,[ub("titleFontSize",t)]:d,[ub("iconSize",t)]:p}}=o.value;return{"--n-bezier":r,"--n-font-size":u,"--n-icon-size":p,"--n-line-height":a,"--n-text-color":i,"--n-title-font-size":d,"--n-title-font-weight":s,"--n-title-text-color":l,"--n-icon-color":c||""}})),i=n?KP("result",ai((()=>{const{size:t,status:n}=e;let o="";return t&&(o+=t[0]),n&&(o+=n[0]),o})),r,e):void 0;return{mergedClsPrefix:t,cssVars:n?void 0:r,themeClass:null==i?void 0:i.themeClass,onRender:null==i?void 0:i.onRender}},render(){var e;const{status:t,$slots:n,mergedClsPrefix:o,onRender:r}=this;return null==r||r(),li("div",{class:[`${o}-result`,this.themeClass],style:this.cssVars},li("div",{class:`${o}-result-icon`},(null===(e=n.icon)||void 0===e?void 0:e.call(n))||li(CT,{clsPrefix:o},{default:()=>y$[t]()})),li("div",{class:`${o}-result-header`},this.title?li("div",{class:`${o}-result-header__title`},this.title):null,this.description?li("div",{class:`${o}-result-header__description`},this.description):null),n.default&&li("div",{class:`${o}-result-content`},n),n.footer&&li("div",{class:`${o}-result-footer`},n.footer()))}}),C$=zn({name:"Scrollbar",props:Object.assign(Object.assign({},I_.props),{trigger:String,xScrollable:Boolean,onScroll:Function,contentClass:String,contentStyle:[Object,String],size:Number}),setup(){const e=Et(null),t={scrollTo:(...t)=>{var n;null===(n=e.value)||void 0===n||n.scrollTo(t[0],t[1])},scrollBy:(...t)=>{var n;null===(n=e.value)||void 0===n||n.scrollBy(t[0],t[1])}};return Object.assign(Object.assign({},t),{scrollbarInstRef:e})},render(){return li(lR,Object.assign({ref:"scrollbarInstRef"},this.$props),this.$slots)}}),w$=C$,k$={name:"Skeleton",common:tz,self(e){const{heightSmall:t,heightMedium:n,heightLarge:o,borderRadius:r}=e;return{color:"rgba(255, 255, 255, 0.12)",colorEnd:"rgba(255, 255, 255, 0.18)",borderRadius:r,heightSmall:t,heightMedium:n,heightLarge:o}}},S$={name:"Skeleton",common:qz,self:function(e){const{heightSmall:t,heightMedium:n,heightLarge:o,borderRadius:r}=e;return{color:"#eee",colorEnd:"#ddd",borderRadius:r,heightSmall:t,heightMedium:n,heightLarge:o}}},_$=eb([nb("skeleton","\n height: 1em;\n width: 100%;\n transition:\n --n-color-start .3s var(--n-bezier),\n --n-color-end .3s var(--n-bezier),\n background-color .3s var(--n-bezier);\n animation: 2s skeleton-loading infinite cubic-bezier(0.36, 0, 0.64, 1);\n background-color: var(--n-color-start);\n "),eb("@keyframes skeleton-loading","\n 0% {\n background: var(--n-color-start);\n }\n 40% {\n background: var(--n-color-end);\n }\n 80% {\n background: var(--n-color-start);\n }\n 100% {\n background: var(--n-color-start);\n }\n ")]),P$=zn({name:"Skeleton",inheritAttrs:!1,props:Object.assign(Object.assign({},I_.props),{text:Boolean,round:Boolean,circle:Boolean,height:[String,Number],width:[String,Number],size:String,repeat:{type:Number,default:1},animated:{type:Boolean,default:!0},sharp:{type:Boolean,default:!0}}),setup(e){!function(){if(pb&&window.CSS&&!Qb&&(Qb=!0,"registerProperty"in(null===window||void 0===window?void 0:window.CSS)))try{CSS.registerProperty({name:"--n-color-start",syntax:"",inherits:!1,initialValue:"#0000"}),CSS.registerProperty({name:"--n-color-end",syntax:"",inherits:!1,initialValue:"#0000"})}catch(Cb){}}();const{mergedClsPrefixRef:t}=B_(e),n=I_("Skeleton","-skeleton",_$,S$,e,t);return{mergedClsPrefix:t,style:ai((()=>{var t,o;const r=n.value,{common:{cubicBezierEaseInOut:i}}=r,a=r.self,{color:l,colorEnd:s,borderRadius:c}=a;let u;const{circle:d,sharp:p,round:h,width:f,height:v,size:m,text:g,animated:b}=e;void 0!==m&&(u=a[ub("height",m)]);const y=d?null!==(t=null!=f?f:v)&&void 0!==t?t:u:f,x=null!==(o=d&&null!=f?f:v)&&void 0!==o?o:u;return{display:g?"inline-block":"",verticalAlign:g?"-0.125em":"",borderRadius:d?"50%":h?"4096px":p?"":c,width:"number"==typeof y?Lm(y):y,height:"number"==typeof x?Lm(x):x,animation:b?"":"none","--n-bezier":i,"--n-color-start":l,"--n-color-end":s}}))}},render(){const{repeat:e,style:t,mergedClsPrefix:n,$attrs:o}=this,r=li("div",Wr({class:`${n}-skeleton`,style:t},o));return e>1?li(yr,null,ag(e,null).map((e=>[r,"\n"]))):r}}),T$=eb([eb("@keyframes spin-rotate","\n from {\n transform: rotate(0);\n }\n to {\n transform: rotate(360deg);\n }\n "),nb("spin-container","\n position: relative;\n ",[nb("spin-body","\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translateX(-50%) translateY(-50%);\n ",[rR()])]),nb("spin-body","\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n "),nb("spin","\n display: inline-flex;\n height: var(--n-size);\n width: var(--n-size);\n font-size: var(--n-size);\n color: var(--n-color);\n ",[rb("rotate","\n animation: spin-rotate 2s linear infinite;\n ")]),nb("spin-description","\n display: inline-block;\n font-size: var(--n-font-size);\n color: var(--n-text-color);\n transition: color .3s var(--n-bezier);\n margin-top: 8px;\n "),nb("spin-content","\n opacity: 1;\n transition: opacity .3s var(--n-bezier);\n pointer-events: all;\n ",[rb("spinning","\n user-select: none;\n -webkit-user-select: none;\n pointer-events: none;\n opacity: var(--n-opacity-spinning);\n ")])]),A$={small:20,medium:18,large:16},z$=zn({name:"Spin",props:Object.assign(Object.assign({},I_.props),{contentClass:String,contentStyle:[Object,String],description:String,stroke:String,size:{type:[String,Number],default:"medium"},show:{type:Boolean,default:!0},strokeWidth:Number,rotate:{type:Boolean,default:!0},spinning:{type:Boolean,validator:()=>!0,default:void 0},delay:Number}),setup(e){const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Spin","-spin",T$,JL,e,t),r=ai((()=>{const{size:t}=e,{common:{cubicBezierEaseInOut:n},self:r}=o.value,{opacitySpinning:i,color:a,textColor:l}=r;return{"--n-bezier":n,"--n-opacity-spinning":i,"--n-size":"number"==typeof t?Lm(t):r[ub("size",t)],"--n-color":a,"--n-text-color":l}})),i=n?KP("spin",ai((()=>{const{size:t}=e;return"number"==typeof t?String(t):t[0]})),r,e):void 0,a=Nb(e,["spinning","show"]),l=Et(!1);return ir((t=>{let n;if(a.value){const{delay:o}=e;if(o)return n=window.setTimeout((()=>{l.value=!0}),o),void t((()=>{clearTimeout(n)}))}l.value=a.value})),{mergedClsPrefix:t,active:l,mergedStrokeWidth:ai((()=>{const{strokeWidth:t}=e;if(void 0!==t)return t;const{size:n}=e;return A$["number"==typeof n?"medium":n]})),cssVars:n?void 0:r,themeClass:null==i?void 0:i.themeClass,onRender:null==i?void 0:i.onRender}},render(){var e,t;const{$slots:n,mergedClsPrefix:o,description:r}=this,i=n.icon&&this.rotate,a=(r||n.description)&&li("div",{class:`${o}-spin-description`},r||(null===(e=n.description)||void 0===e?void 0:e.call(n))),l=n.icon?li("div",{class:[`${o}-spin-body`,this.themeClass]},li("div",{class:[`${o}-spin`,i&&`${o}-spin--rotate`],style:n.default?"":this.cssVars},n.icon()),a):li("div",{class:[`${o}-spin-body`,this.themeClass]},li(RT,{clsPrefix:o,style:n.default?"":this.cssVars,stroke:this.stroke,"stroke-width":this.mergedStrokeWidth,class:`${o}-spin`}),a);return null===(t=this.onRender)||void 0===t||t.call(this),n.default?li("div",{class:[`${o}-spin-container`,this.themeClass],style:this.cssVars},li("div",{class:[`${o}-spin-content`,this.active&&`${o}-spin-content--spinning`,this.contentClass],style:this.contentStyle},n),li(vi,{name:"fade-in-transition"},{default:()=>this.active?l:null})):l}}),R$={name:"Split",common:tz},E$=nb("switch","\n height: var(--n-height);\n min-width: var(--n-width);\n vertical-align: middle;\n user-select: none;\n -webkit-user-select: none;\n display: inline-flex;\n outline: none;\n justify-content: center;\n align-items: center;\n",[ob("children-placeholder","\n height: var(--n-rail-height);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n pointer-events: none;\n visibility: hidden;\n "),ob("rail-placeholder","\n display: flex;\n flex-wrap: none;\n "),ob("button-placeholder","\n width: calc(1.75 * var(--n-rail-height));\n height: var(--n-rail-height);\n "),nb("base-loading","\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translateX(-50%) translateY(-50%);\n font-size: calc(var(--n-button-width) - 4px);\n color: var(--n-loading-color);\n transition: color .3s var(--n-bezier);\n ",[PT({left:"50%",top:"50%",originalTransform:"translateX(-50%) translateY(-50%)"})]),ob("checked, unchecked","\n transition: color .3s var(--n-bezier);\n color: var(--n-text-color);\n box-sizing: border-box;\n position: absolute;\n white-space: nowrap;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n line-height: 1;\n "),ob("checked","\n right: 0;\n padding-right: calc(1.25 * var(--n-rail-height) - var(--n-offset));\n "),ob("unchecked","\n left: 0;\n justify-content: flex-end;\n padding-left: calc(1.25 * var(--n-rail-height) - var(--n-offset));\n "),eb("&:focus",[ob("rail","\n box-shadow: var(--n-box-shadow-focus);\n ")]),rb("round",[ob("rail","border-radius: calc(var(--n-rail-height) / 2);",[ob("button","border-radius: calc(var(--n-button-height) / 2);")])]),ib("disabled",[ib("icon",[rb("rubber-band",[rb("pressed",[ob("rail",[ob("button","max-width: var(--n-button-width-pressed);")])]),ob("rail",[eb("&:active",[ob("button","max-width: var(--n-button-width-pressed);")])]),rb("active",[rb("pressed",[ob("rail",[ob("button","left: calc(100% - var(--n-offset) - var(--n-button-width-pressed));")])]),ob("rail",[eb("&:active",[ob("button","left: calc(100% - var(--n-offset) - var(--n-button-width-pressed));")])])])])])]),rb("active",[ob("rail",[ob("button","left: calc(100% - var(--n-button-width) - var(--n-offset))")])]),ob("rail","\n overflow: hidden;\n height: var(--n-rail-height);\n min-width: var(--n-rail-width);\n border-radius: var(--n-rail-border-radius);\n cursor: pointer;\n position: relative;\n transition:\n opacity .3s var(--n-bezier),\n background .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier);\n background-color: var(--n-rail-color);\n ",[ob("button-icon","\n color: var(--n-icon-color);\n transition: color .3s var(--n-bezier);\n font-size: calc(var(--n-button-height) - 4px);\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n line-height: 1;\n ",[PT()]),ob("button",'\n align-items: center; \n top: var(--n-offset);\n left: var(--n-offset);\n height: var(--n-button-height);\n width: var(--n-button-width-pressed);\n max-width: var(--n-button-width);\n border-radius: var(--n-button-border-radius);\n background-color: var(--n-button-color);\n box-shadow: var(--n-button-box-shadow);\n box-sizing: border-box;\n cursor: inherit;\n content: "";\n position: absolute;\n transition:\n background-color .3s var(--n-bezier),\n left .3s var(--n-bezier),\n opacity .3s var(--n-bezier),\n max-width .3s var(--n-bezier),\n box-shadow .3s var(--n-bezier);\n ')]),rb("active",[ob("rail","background-color: var(--n-rail-color-active);")]),rb("loading",[ob("rail","\n cursor: wait;\n ")]),rb("disabled",[ob("rail","\n cursor: not-allowed;\n opacity: .5;\n ")])]);let O$;const M$=zn({name:"Switch",props:Object.assign(Object.assign({},I_.props),{size:{type:String,default:"medium"},value:{type:[String,Number,Boolean],default:void 0},loading:Boolean,defaultValue:{type:[String,Number,Boolean],default:!1},disabled:{type:Boolean,default:void 0},round:{type:Boolean,default:!0},"onUpdate:value":[Function,Array],onUpdateValue:[Function,Array],checkedValue:{type:[String,Number,Boolean],default:!0},uncheckedValue:{type:[String,Number,Boolean],default:!1},railStyle:Function,rubberBand:{type:Boolean,default:!0},onChange:[Function,Array]}),setup(e){void 0===O$&&(O$="undefined"==typeof CSS||void 0!==CSS.supports&&CSS.supports("width","max(1px)"));const{mergedClsPrefixRef:t,inlineThemeDisabled:n}=B_(e),o=I_("Switch","-switch",E$,aB,e,t),r=eC(e),{mergedSizeRef:i,mergedDisabledRef:a}=r,l=Et(e.defaultValue),s=Db(jt(e,"value"),l),c=ai((()=>s.value===e.checkedValue)),u=Et(!1),d=Et(!1),p=ai((()=>{const{railStyle:t}=e;if(t)return t({focused:d.value,checked:c.value})}));function h(t){const{"onUpdate:value":n,onChange:o,onUpdateValue:i}=e,{nTriggerFormInput:a,nTriggerFormChange:s}=r;n&&dg(n,t),i&&dg(i,t),o&&dg(o,t),l.value=t,a(),s()}const f=ai((()=>{const{value:e}=i,{self:{opacityDisabled:t,railColor:n,railColorActive:r,buttonBoxShadow:a,buttonColor:l,boxShadowFocus:s,loadingColor:c,textColor:u,iconColor:d,[ub("buttonHeight",e)]:p,[ub("buttonWidth",e)]:h,[ub("buttonWidthPressed",e)]:f,[ub("railHeight",e)]:v,[ub("railWidth",e)]:m,[ub("railBorderRadius",e)]:g,[ub("buttonBorderRadius",e)]:b},common:{cubicBezierEaseInOut:y}}=o.value;let x,C,w;return O$?(x=`calc((${v} - ${p}) / 2)`,C=`max(${v}, ${p})`,w=`max(${m}, calc(${m} + ${p} - ${v}))`):(x=Lm((Im(v)-Im(p))/2),C=Lm(Math.max(Im(v),Im(p))),w=Im(v)>Im(p)?m:Lm(Im(m)+Im(p)-Im(v))),{"--n-bezier":y,"--n-button-border-radius":b,"--n-button-box-shadow":a,"--n-button-color":l,"--n-button-width":h,"--n-button-width-pressed":f,"--n-button-height":p,"--n-height":C,"--n-offset":x,"--n-opacity-disabled":t,"--n-rail-border-radius":g,"--n-rail-color":n,"--n-rail-color-active":r,"--n-rail-height":v,"--n-rail-width":m,"--n-width":w,"--n-box-shadow-focus":s,"--n-loading-color":c,"--n-text-color":u,"--n-icon-color":d}})),v=n?KP("switch",ai((()=>i.value[0])),f,e):void 0;return{handleClick:function(){e.loading||a.value||(s.value!==e.checkedValue?h(e.checkedValue):h(e.uncheckedValue))},handleBlur:function(){d.value=!1,function(){const{nTriggerFormBlur:e}=r;e()}(),u.value=!1},handleFocus:function(){d.value=!0,function(){const{nTriggerFormFocus:e}=r;e()}()},handleKeyup:function(t){e.loading||a.value||" "===t.key&&(s.value!==e.checkedValue?h(e.checkedValue):h(e.uncheckedValue),u.value=!1)},handleKeydown:function(t){e.loading||a.value||" "===t.key&&(t.preventDefault(),u.value=!0)},mergedRailStyle:p,pressed:u,mergedClsPrefix:t,mergedValue:s,checked:c,mergedDisabled:a,cssVars:n?void 0:f,themeClass:null==v?void 0:v.themeClass,onRender:null==v?void 0:v.onRender}},render(){const{mergedClsPrefix:e,mergedDisabled:t,checked:n,mergedRailStyle:o,onRender:r,$slots:i}=this;null==r||r();const{checked:a,unchecked:l,icon:s,"checked-icon":c,"unchecked-icon":u}=i,d=!(kg(s)&&kg(c)&&kg(u));return li("div",{role:"switch","aria-checked":n,class:[`${e}-switch`,this.themeClass,d&&`${e}-switch--icon`,n&&`${e}-switch--active`,t&&`${e}-switch--disabled`,this.round&&`${e}-switch--round`,this.loading&&`${e}-switch--loading`,this.pressed&&`${e}-switch--pressed`,this.rubberBand&&`${e}-switch--rubber-band`],tabindex:this.mergedDisabled?void 0:0,style:this.cssVars,onClick:this.handleClick,onFocus:this.handleFocus,onBlur:this.handleBlur,onKeyup:this.handleKeyup,onKeydown:this.handleKeydown},li("div",{class:`${e}-switch__rail`,"aria-hidden":"true",style:o},wg(a,(t=>wg(l,(n=>t||n?li("div",{"aria-hidden":!0,class:`${e}-switch__children-placeholder`},li("div",{class:`${e}-switch__rail-placeholder`},li("div",{class:`${e}-switch__button-placeholder`}),t),li("div",{class:`${e}-switch__rail-placeholder`},li("div",{class:`${e}-switch__button-placeholder`}),n)):null)))),li("div",{class:`${e}-switch__button`},wg(s,(t=>wg(c,(n=>wg(u,(o=>li(bT,null,{default:()=>this.loading?li(RT,{key:"loading",clsPrefix:e,strokeWidth:20}):this.checked&&(n||t)?li("div",{class:`${e}-switch__button-icon`,key:n?"checked-icon":"icon"},n||t):this.checked||!o&&!t?null:li("div",{class:`${e}-switch__button-icon`,key:o?"unchecked-icon":"icon"},o||t)}))))))),wg(a,(t=>t&&li("div",{key:"checked",class:`${e}-switch__checked`},t))),wg(l,(t=>t&&li("div",{key:"unchecked",class:`${e}-switch__unchecked`},t))))))}}),F$=zn({name:"InjectionExtractor",props:{onSetup:Function},setup(e,{slots:t}){var n;return null===(n=e.onSetup)||void 0===n||n.call(e),()=>{var e;return null===(e=t.default)||void 0===e?void 0:e.call(t)}}}),I$={message:function(){const e=Po(AD,null);return null===e&&fg("use-message","No outer founded. See prerequisite in https://www.naiveui.com/en-US/os-theme/components/message for more details. If you want to use `useMessage` outside setup, please check https://www.naiveui.com/zh-CN/os-theme/components/message#Q-&-A."),e},notification:function(){const e=Po(XD,null);return null===e&&fg("use-notification","No outer `n-notification-provider` found."),e},loadingBar:function(){const e=Po(ZB,null);return null===e&&fg("use-loading-bar","No outer founded."),e},dialog:function(){const e=Po(CI,null);return null===e&&fg("use-dialog","No outer founded."),e},modal:function(){const e=Po(BD,null);return null===e&&fg("use-modal","No outer founded."),e}};function L$(e,{configProviderProps:t,messageProviderProps:n,dialogProviderProps:o,notificationProviderProps:r,loadingBarProviderProps:i,modalProviderProps:a}={}){const l=[];e.forEach((e=>{switch(e){case"message":l.push({type:e,Provider:FD,props:n});break;case"notification":l.push({type:e,Provider:YD,props:r});break;case"dialog":l.push({type:e,Provider:MI,props:o});break;case"loadingBar":l.push({type:e,Provider:oD,props:i});break;case"modal":l.push({type:e,Provider:DD,props:a})}}));const s=function({providersAndProps:e,configProviderProps:t}){let n=ga((function(){return li(ZO,It(t),{default:()=>e.map((({type:e,Provider:t,props:n})=>li(t,It(n),{default:()=>li(F$,{onSetup:()=>o[e]=I$[e]()})})))})}));const o={app:n};let r;return pb&&(r=document.createElement("div"),document.body.appendChild(r),n.mount(r)),Object.assign({unmount:()=>{var e;null!==n&&null!==r&&(n.unmount(),null===(e=r.parentNode)||void 0===e||e.removeChild(r),r=null,n=null)}},o)}({providersAndProps:l,configProviderProps:t});return s}const B$={name:"dark",common:tz,Alert:oE,Anchor:pE,AutoComplete:EE,Avatar:OE,AvatarGroup:ME,BackTop:IE,Badge:NE,Breadcrumb:UE,Button:eO,ButtonGroup:wL,Calendar:aO,Card:dO,Carousel:bO,Cascader:HO,Checkbox:jO,Code:XO,Collapse:YO,CollapseTransition:QO,ColorPicker:lO,DataTable:$M,DatePicker:sI,Descriptions:uI,Dialog:fI,Divider:LI,Drawer:jI,Dropdown:IM,DynamicInput:tL,DynamicTags:sL,Element:cL,Empty:Yz,Ellipsis:TM,Equation:{name:"Equation",common:tz,self:()=>({})},Flex:dL,Form:hL,GradientText:kL,Icon:wF,IconWrapper:SB,Image:_B,Input:xE,InputNumber:SL,LegacyTransfer:VB,Layout:PL,List:RL,LoadingBar:EL,Log:ML,Menu:BL,Mention:FL,Message:CL,Modal:SI,Notification:gL,PageHeader:$L,Pagination:gM,Popconfirm:jL,Popover:_R,Popselect:tM,Progress:UL,QrCode:i$,Radio:RM,Rate:VL,Result:XL,Row:wB,Scrollbar:nR,Select:dM,Skeleton:k$,Slider:QL,Space:oL,Spin:eB,Statistic:tB,Steps:oB,Switch:iB,Table:sB,Tabs:uB,Tag:jR,Thing:dB,TimePicker:aI,Timeline:hB,Tooltip:_M,Transfer:vB,Tree:mB,TreeSelect:gB,Typography:yB,Upload:xB,Watermark:CB,Split:R$,FloatButton:kB,FloatButtonGroup:{name:"FloatButtonGroup",common:tz,self(e){const{popoverColor:t,dividerColor:n,borderRadius:o}=e;return{color:t,buttonBorderColor:n,borderRadiusSquare:o,boxShadow:"0 2px 8px 0px rgba(0, 0, 0, .12)"}}}},D$={"aria-hidden":"true",width:"1em",height:"1em"},$$=["xlink:href","fill"],N$=zn({__name:"SvgIcon",props:{icon:{type:String,required:!0},prefix:{type:String,default:"icon-custom"},color:{type:String,default:"currentColor"}},setup(e){const t=e,n=ai((()=>`#${t.prefix}-${t.icon}`));return(t,o)=>(_r(),zr("svg",D$,[Ir("use",{"xlink:href":n.value,fill:e.color},null,8,$$)]))}}),j$=(e,t={size:12})=>()=>li(SF,t,(()=>li(Tm,{icon:e}))),H$=(e,t={size:12})=>()=>li(SF,t,(()=>li(N$,{icon:e}))),W$={header:{height:60},tags:{visible:!0,height:50},naiveThemeOverrides:{common:{primaryColor:"#316C72FF",primaryColorHover:"#316C72E3",primaryColorPressed:"#2B4C59FF",primaryColorSuppl:"#316C72E3",infoColor:"#316C72FF",infoColorHover:"#316C72E3",infoColorPressed:"#2B4C59FF",infoColorSuppl:"#316C72E3",successColor:"#18A058FF",successColorHover:"#36AD6AFF",successColorPressed:"#0C7A43FF",successColorSuppl:"#36AD6AFF",warningColor:"#F0A020FF",warningColorHover:"#FCB040FF",warningColorPressed:"#C97C10FF",warningColorSuppl:"#FCB040FF",errorColor:"#D03050FF",errorColorHover:"#DE576DFF",errorColorPressed:"#AB1F3FFF",errorColorSuppl:"#DE576DFF"}}},U$={header:{height:60},tags:{visible:!0,height:50},naiveThemeOverrides:{common:{primaryColor:"#0665d0",primaryColorHover:"#2a84de",primaryColorPressed:"#004085",primaryColorSuppl:"#0056b3",infoColor:"#0665d0",infoColorHover:"#2a84de",infoColorPressed:"#0c5460",infoColorSuppl:"#004085",successColor:"#28a745",successColorHover:"#218838",successColorPressed:"#1e7e34",successColorSuppl:"#218838",warningColor:"#ffc107",warningColorHover:"#e0a800",warningColorPressed:"#d39e00",warningColorSuppl:"#e0a800",errorColor:"#dc3545",errorColorHover:"#c82333",errorColorPressed:"#bd2130",errorColorSuppl:"#c82333"}}},V$={header:{height:60},tags:{visible:!0,height:50},naiveThemeOverrides:{common:{primaryColor:"#343a40",primaryColorHover:"#23272b",primaryColorPressed:"#1d2124",primaryColorSuppl:"#23272b",infoColor:"#343a40",infoColorHover:"#23272b",infoColorPressed:"#1d2124",infoColorSuppl:"#23272b",successColor:"#28a745",successColorHover:"#218838",successColorPressed:"#1e7e34",successColorSuppl:"#218838",warningColor:"#ffc107",warningColorHover:"#e0a800",warningColorPressed:"#d39e00",warningColorSuppl:"#e0a800",errorColor:"#dc3545",errorColorHover:"#c82333",errorColorPressed:"#bd2130",errorColorSuppl:"#c82333"}}},q$={header:{height:60},tags:{visible:!0,height:50},naiveThemeOverrides:{common:{primaryColor:"#004175",primaryColorHover:"#002c4c",primaryColorPressed:"#001f35",primaryColorSuppl:"#002c4c",infoColor:"#004175",infoColorHover:"#002c4c",infoColorPressed:"#001f35",infoColorSuppl:"#002c4c",successColor:"#28a745",successColorHover:"#218838",successColorPressed:"#1e7e34",successColorSuppl:"#218838",warningColor:"#ffc107",warningColorHover:"#e0a800",warningColorPressed:"#d39e00",warningColorSuppl:"#e0a800",errorColor:"#dc3545",errorColorHover:"#c82333",errorColorPressed:"#bd2130",errorColorSuppl:"#c82333"}}},{header:K$,tags:G$,naiveThemeOverrides:X$}=function(){var e,t;const n={default:W$,blue:U$,black:V$,darkblue:q$},o=(null==(t=null==(e=window.settings)?void 0:e.theme)?void 0:t.color)||"default";return Object.prototype.hasOwnProperty.call(n,o)?n[o]:n.default}();function Y$(e){return!!ue()&&(de(e),!0)}function Q$(e){return"function"==typeof e?e():It(e)}const Z$="undefined"!=typeof window&&"undefined"!=typeof document;"undefined"!=typeof WorkerGlobalScope&&(globalThis,WorkerGlobalScope);const J$=e=>null!=e,eN=Object.prototype.toString,tN=()=>{},nN=e=>e();function oN(...e){if(1!==e.length)return jt(...e);const t=e[0];return"function"==typeof t?gt(new Dt((()=>({get:t,set:tN})))):Et(t)}function rN(e,t,n={}){const o=n,{eventFilter:r=nN}=o,i=h(o,["eventFilter"]);return lr(e,(a=r,l=t,function(...e){return new Promise(((t,n)=>{Promise.resolve(a((()=>l.apply(this,e)),{fn:l,thisArg:this,args:e})).then(t).catch(n)}))}),i);var a,l}function iN(e,t,n={}){const o=n,{eventFilter:r}=o,i=h(o,["eventFilter"]),{eventFilter:a,pause:l,resume:s,isActive:c}=function(e=nN){const t=Et(!0);return{isActive:gt(t),pause:function(){t.value=!1},resume:function(){t.value=!0},eventFilter:(...n)=>{t.value&&e(...n)}}}(r);return{stop:rN(e,t,p(d({},i),{eventFilter:a})),pause:l,resume:s,isActive:c}}function aN(e,t=!0,n){const o=function(e){return e||Gr()}();o?$n(e,n):t?e():tn(e)}function lN(e){var t;const n=Q$(e);return null!=(t=null==n?void 0:n.$el)?t:n}const sN=Z$?window:void 0,cN=Z$?window.document:void 0;function uN(...e){let t,n,o,r;if("string"==typeof e[0]||Array.isArray(e[0])?([n,o,r]=e,t=sN):[t,n,o,r]=e,!t)return tN;Array.isArray(n)||(n=[n]),Array.isArray(o)||(o=[o]);const i=[],a=()=>{i.forEach((e=>e())),i.length=0},l=lr((()=>[lN(t),Q$(r)]),(([e,t])=>{if(a(),!e)return;const r=(l=t,"[object Object]"===eN.call(l)?d({},t):t);var l;i.push(...n.flatMap((t=>o.map((n=>((e,t,n,o)=>(e.addEventListener(t,n,o),()=>e.removeEventListener(t,n,o)))(e,t,n,r))))))}),{immediate:!0,flush:"post"}),s=()=>{l(),a()};return Y$(s),s}function dN(e){const t=function(){const e=Et(!1),t=Gr();return t&&$n((()=>{e.value=!0}),t),e}();return ai((()=>(t.value,Boolean(e()))))}const pN="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},hN="__vueuse_ssr_handlers__",fN=vN();function vN(){return hN in pN||(pN[hN]=pN[hN]||{}),pN[hN]}function mN(e,t){return fN[e]||t}const gN={boolean:{read:e=>"true"===e,write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},bN="vueuse-storage";function yN(e,t,n,o={}){var r;const{flush:i="pre",deep:a=!0,listenToStorageChanges:l=!0,writeDefaults:s=!0,mergeDefaults:c=!1,shallow:u,window:p=sN,eventFilter:h,onError:f=(e=>{}),initOnMounted:v}=o,m=(u?Ot:Et)("function"==typeof t?t():t);if(!n)try{n=mN("getDefaultStorage",(()=>{var e;return null==(e=sN)?void 0:e.localStorage}))()}catch(WQ){f(WQ)}if(!n)return m;const g=Q$(t),b=function(e){return null==e?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":"boolean"==typeof e?"boolean":"string"==typeof e?"string":"object"==typeof e?"object":Number.isNaN(e)?"any":"number"}(g),y=null!=(r=o.serializer)?r:gN[b],{pause:x,resume:C}=iN(m,(()=>function(t){try{const o=n.getItem(e);if(null==t)w(o,null),n.removeItem(e);else{const r=y.write(t);o!==r&&(n.setItem(e,r),w(o,r))}}catch(WQ){f(WQ)}}(m.value)),{flush:i,deep:a,eventFilter:h});function w(t,o){p&&p.dispatchEvent(new CustomEvent(bN,{detail:{key:e,oldValue:t,newValue:o,storageArea:n}}))}function k(t){if(!t||t.storageArea===n)if(t&&null==t.key)m.value=g;else if(!t||t.key===e){x();try{(null==t?void 0:t.newValue)!==y.write(m.value)&&(m.value=function(t){const o=t?t.newValue:n.getItem(e);if(null==o)return s&&null!=g&&n.setItem(e,y.write(g)),g;if(!t&&c){const e=y.read(o);return"function"==typeof c?c(e,g):"object"!==b||Array.isArray(e)?e:d(d({},g),e)}return"string"!=typeof o?o:y.read(o)}(t))}catch(WQ){f(WQ)}finally{t?tn(C):C()}}}function S(e){k(e.detail)}return p&&l&&aN((()=>{uN(p,"storage",k),uN(p,bN,S),v&&k()})),v||k(),m}function xN(e){return function(e,t={}){const{window:n=sN}=t,o=dN((()=>n&&"matchMedia"in n&&"function"==typeof n.matchMedia));let r;const i=Et(!1),a=e=>{i.value=e.matches},l=()=>{r&&("removeEventListener"in r?r.removeEventListener("change",a):r.removeListener(a))},s=ir((()=>{o.value&&(l(),r=n.matchMedia(Q$(e)),"addEventListener"in r?r.addEventListener("change",a):r.addListener(a),i.value=r.matches)}));return Y$((()=>{s(),l(),r=void 0})),i}("(prefers-color-scheme: dark)",e)}function CN(e,t,n={}){const{window:o=sN,initialValue:r="",observe:i=!1}=n,a=Et(r),l=ai((()=>{var e;return lN(t)||(null==(e=null==o?void 0:o.document)?void 0:e.documentElement)}));function s(){var t;const n=Q$(e),i=Q$(l);if(i&&o){const e=null==(t=o.getComputedStyle(i).getPropertyValue(n))?void 0:t.trim();a.value=e||r}}return i&&function(e,t,n={}){const o=n,{window:r=sN}=o,i=h(o,["window"]);let a;const l=dN((()=>r&&"MutationObserver"in r)),s=()=>{a&&(a.disconnect(),a=void 0)},c=ai((()=>{const t=Q$(e),n=(Array.isArray(t)?t:[t]).map(lN).filter(J$);return new Set(n)})),u=lr((()=>c.value),(e=>{s(),l.value&&e.size&&(a=new MutationObserver(t),e.forEach((e=>a.observe(e,i))))}),{immediate:!0,flush:"post"}),d=()=>{s(),u()};Y$(d)}(l,s,{attributeFilter:["style","class"],window:o}),lr([l,()=>Q$(e)],s,{immediate:!0}),lr(a,(t=>{var n;(null==(n=l.value)?void 0:n.style)&&l.value.style.setProperty(Q$(e),t)})),a}function wN(e={}){const{valueDark:t="dark",valueLight:n="",window:o=sN}=e,r=function(e={}){const{selector:t="html",attribute:n="class",initialValue:o="auto",window:r=sN,storage:i,storageKey:a="vueuse-color-scheme",listenToStorageChanges:l=!0,storageRef:s,emitAuto:c,disableTransition:u=!0}=e,p=d({auto:"",light:"light",dark:"dark"},e.modes||{}),h=xN({window:r}),f=ai((()=>h.value?"dark":"light")),v=s||(null==a?oN(o):yN(a,o,i,{window:r,listenToStorageChanges:l})),m=ai((()=>"auto"===v.value?f.value:v.value)),g=mN("updateHTMLAttrs",((e,t,n)=>{const o="string"==typeof e?null==r?void 0:r.document.querySelector(e):lN(e);if(!o)return;let i;if(u){i=r.document.createElement("style");const e="*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}";i.appendChild(document.createTextNode(e)),r.document.head.appendChild(i)}if("class"===t){const e=n.split(/\s/g);Object.values(p).flatMap((e=>(e||"").split(/\s/g))).filter(Boolean).forEach((t=>{e.includes(t)?o.classList.add(t):o.classList.remove(t)}))}else o.setAttribute(t,n);u&&(r.getComputedStyle(i).opacity,document.head.removeChild(i))}));function b(e){var o;g(t,n,null!=(o=p[e])?o:e)}function y(t){e.onChanged?e.onChanged(t,b):b(t)}lr(m,y,{flush:"post",immediate:!0}),aN((()=>y(m.value)));const x=ai({get:()=>c?v.value:m.value,set(e){v.value=e}});try{return Object.assign(x,{store:v,system:f,state:m})}catch(WQ){return x}}(p(d({},e),{onChanged:(t,n)=>{var o;e.onChanged?null==(o=e.onChanged)||o.call(e,"dark"===t,n,t):n(t)},modes:{dark:t,light:n}})),i=ai((()=>r.system?r.system.value:xN({window:o}).value?"dark":"light"));return ai({get:()=>"dark"===r.value,set(e){const t=e?"dark":"light";i.value===t?r.value="auto":r.value=t}})}const kN=["fullscreenchange","webkitfullscreenchange","webkitendfullscreen","mozfullscreenchange","MSFullscreenChange"];function SN(e,t={}){const{document:n=cN,autoExit:o=!1}=t,r=ai((()=>{var t;return null!=(t=lN(e))?t:null==n?void 0:n.querySelector("html")})),i=Et(!1),a=ai((()=>["requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen","webkitEnterFullScreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"].find((e=>n&&e in n||r.value&&e in r.value)))),l=ai((()=>["exitFullscreen","webkitExitFullscreen","webkitExitFullScreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"].find((e=>n&&e in n||r.value&&e in r.value)))),s=ai((()=>["fullScreen","webkitIsFullScreen","webkitDisplayingFullscreen","mozFullScreen","msFullscreenElement"].find((e=>n&&e in n||r.value&&e in r.value)))),c=["fullscreenElement","webkitFullscreenElement","mozFullScreenElement","msFullscreenElement"].find((e=>n&&e in n)),u=dN((()=>r.value&&n&&void 0!==a.value&&void 0!==l.value&&void 0!==s.value)),d=()=>{if(s.value){if(n&&null!=n[s.value])return n[s.value];{const e=r.value;if(null!=(null==e?void 0:e[s.value]))return Boolean(e[s.value])}}return!1};function p(){return v(this,null,(function*(){if(u.value&&i.value){if(l.value)if(null!=(null==n?void 0:n[l.value]))yield n[l.value]();else{const e=r.value;null!=(null==e?void 0:e[l.value])&&(yield e[l.value]())}i.value=!1}}))}function h(){return v(this,null,(function*(){if(!u.value||i.value)return;d()&&(yield p());const e=r.value;a.value&&null!=(null==e?void 0:e[a.value])&&(yield e[a.value](),i.value=!0)}))}const f=()=>{const e=d();(!e||e&&c&&(null==n?void 0:n[c])===r.value)&&(i.value=e)};return uN(n,kN,f,!1),uN((()=>lN(r)),kN,f,!1),o&&Y$(p),{isSupported:u,isFullscreen:i,enter:h,exit:p,toggle:function(){return v(this,null,(function*(){yield i.value?p():h()}))}}}const _N=$s("app",{state(){var e,t,n,o,r,i,a;return{collapsed:window.innerWidth<768,isDark:wN(),title:null==(e=window.settings)?void 0:e.title,assets_path:null==(t=window.settings)?void 0:t.assets_path,theme:null==(n=window.settings)?void 0:n.theme,version:null==(o=window.settings)?void 0:o.version,background_url:null==(r=window.settings)?void 0:r.background_url,description:null==(i=window.settings)?void 0:i.description,logo:null==(a=window.settings)?void 0:a.logo,lang:Df().value||"zh-CN",appConfig:{}}},actions:{getConfig(){return v(this,null,(function*(){const{data:e}=yield EN.get("/user/comm/config");e&&(this.appConfig=e)}))},switchCollapsed(){this.collapsed=!this.collapsed},setCollapsed(e){this.collapsed=e},setDark(e){this.isDark=e},toggleDark(){this.isDark=!this.isDark},switchLang(e){return v(this,null,(function*(){$f(e),location.reload()}))}}});function PN(e){sd.set("access_token",e,21600)}function TN(e){if("get"===e.method&&(e.params=p(d({},e.params),{t:(new Date).getTime()})),function({url:e,method:t=""}){return od.some((n=>n.url===e.split("?")[0]&&n.method===t.toUpperCase()))}(e))return e;const t=dd();return t.value?(e.headers.Authorization=e.headers.Authorization||t.value,e):(hd(),Promise.reject({code:"-1",message:"未登录"}))}function AN(e){return Promise.reject(e)}function zN(e){return Promise.resolve((null==e?void 0:e.data)||{code:-1,message:"未知错误"})}function RN(e){var t;const n=(null==(t=e.response)?void 0:t.data)||{code:-1,message:"未知错误"};let o=n.message;const{code:r,errors:i}=n;switch(r){case 401:o=o||"登录已过期";break;case 403:o=o||"没有权限";break;case 404:o=o||"资源或接口不存在";break;default:o=o||"未知异常"}return window.$message.error(o),Promise.resolve({code:r,message:o,errors:i})}const EN=function(e={}){const t={headers:{"Content-Type":"application/x-www-form-urlencoded","Content-Language":Df().value||"zh-CN"},timeout:12e3},n=nd.create(d(d({},t),e));return n.interceptors.request.use(TN,AN),n.interceptors.response.use(zN,RN),n}({baseURL:function(){let e=((t=window.routerBase||"/").endsWith("/")?t:"/"+t)+"api/v1";var t;return/^https?:\/\//.test(e)||(e=window.location.origin+e),e}()});function ON(){return EN.get("/user/server/fetch")}function MN(){return EN.get("/user/order/fetch")}function FN(e){return EN.post("/user/order/cancel",{trade_no:e})}function IN(e){return EN.post("/user/update",e)}function LN(e,t,n){return EN.post("/user/order/save",{plan_id:e,period:t,coupon_code:n})}const BN=$s("user",{state:()=>({userInfo:{}}),getters:{userUUID(){var e;return null==(e=this.userInfo)?void 0:e.uuid},email(){var e;return null==(e=this.userInfo)?void 0:e.email},avatar(){var e;return null!=(e=this.userInfo.avatar_url)?e:""},role:()=>[],remind_expire(){return this.userInfo.remind_expire},remind_traffic(){return this.userInfo.remind_traffic},balance(){return this.userInfo.balance},plan_id(){return this.userInfo.plan_id},expired_at(){return this.userInfo.expired_at},plan(){return this.userInfo.plan},subscribe(){return this.userInfo.subscribe}},actions:{getUserInfo(){return v(this,null,(function*(){try{const e=yield EN.get("/user/info"),{data:t}=e;return t?(this.userInfo=t,t):Promise.reject(e)}catch(e){return Promise.reject(e)}}))},getUserSubscribe(){return v(this,null,(function*(){try{const e=yield EN.get("/user/getSubscribe"),{data:t}=e;return t?(this.userInfo.subscribe=t,this.userInfo.plan=t.plan,t):Promise.reject(e)}catch(e){return Promise.reject(e)}}))},logout(){return v(this,null,(function*(){pd(),this.userInfo={},hd()}))},setUserInfo(e){this.userInfo=d(d({},this.userInfo),e)}}});function DN(e,t){const n=[];return e.forEach((e=>{if(function(e,t){var n,o;if(!(null==(n=e.meta)?void 0:n.requireAuth))return!0;const r=(null==(o=e.meta)?void 0:o.role)||[];return!(!t.length||!r.length)&&t.some((e=>r.includes(e)))}(e,t)){const o=p(d({},e),{children:[]});e.children&&e.children.length?o.children=DN(e.children,t):Reflect.deleteProperty(o,"children"),n.push(o)}})),n}const $N=$s("permission",{state:()=>({accessRoutes:[]}),getters:{routes(){return gs.concat(JSON.parse(JSON.stringify(this.accessRoutes)))},menus(){return this.routes.filter((e=>{var t;return e.name&&!(null==(t=e.meta)?void 0:t.isHidden)}))}},actions:{generateRoutes(e){const t=DN(xs,e);return this.accessRoutes=t,t}}}),NN=cd.get("activeTag"),jN=cd.get("tags"),HN=["/404","/login"],WN=$s({id:"tag",state:()=>({tags:Et(jN.value),activeTag:Et(NN.value),reloading:Et(!1)}),getters:{activeIndex:e=>()=>e.tags.findIndex((t=>t.path===e.activeTag))},actions:{setActiveTag(e){this.activeTag=e,cd.set("activeTag",e)},setTags(e){this.tags=e,cd.set("tags",e)},addTag(e={}){if(HN.includes(e.path))return;let t=this.tags.find((t=>t.path===e.path));t?t=e:this.setTags([...this.tags,e]),this.setActiveTag(e.path)},reloadTag(e,t){return v(this,null,(function*(){let n=this.tags.find((t=>t.path===e));n?t&&(n.keepAlive=!1):(n={path:e,keepAlive:!1},this.tags.push(n)),window.$loadingBar.start(),this.reloading=!0,yield tn(),this.reloading=!1,n.keepAlive=t,setTimeout((()=>{document.documentElement.scrollTo({left:0,top:0}),window.$loadingBar.finish()}),100)}))},removeTag(e){this.setTags(this.tags.filter((t=>t.path!==e))),e===this.activeTag&&qN.push(this.tags[this.tags.length-1].path)},removeOther(e){e||(e=this.activeTag),e||this.setTags(this.tags.filter((t=>t.path===e))),e!==this.activeTag&&qN.push(this.tags[this.tags.length-1].path)},removeLeft(e){const t=this.tags.findIndex((t=>t.path===e)),n=this.tags.filter(((e,n)=>n>=t));this.setTags(n),n.find((e=>e.path===this.activeTag))||qN.push(n[n.length-1].path)},removeRight(e){const t=this.tags.findIndex((t=>t.path===e)),n=this.tags.filter(((e,n)=>n<=t));this.setTags(n),n.find((e=>e.path===this.activeTag))||qN.push(n[n.length-1].path)},resetTags(){this.setTags([]),this.setActiveTag("")}}}),UN=["/login","/register","/forgetpassword"];function VN(e){!function(e){e.beforeEach((()=>{var e;null==(e=window.$loadingBar)||e.start()})),e.afterEach((()=>{setTimeout((()=>{var e;null==(e=window.$loadingBar)||e.finish()}),200)})),e.onError((()=>{var e;null==(e=window.$loadingBar)||e.error()}))}(e),function(e){const t=BN(),n=$N();e.beforeEach(((o,r,i)=>v(this,null,(function*(){var r,a;dd().value?"/login"===o.path?i({path:null!=(a=null==(r=o.query.redirect)?void 0:r.toString())?a:"/dashboard"}):t.userUUID?i():(yield Promise.all([_N().getConfig(),t.getUserInfo().catch((e=>{pd(),hd(),window.$message.error(e.message||"获取用户信息失败!")}))]),n.generateRoutes(t.role).forEach((t=>{t.name&&!e.hasRoute(t.name)&&e.addRoute(t)})),e.addRoute(bs),i(p(d({},o),{replace:!0}))):UN.includes(o.path)?i():i({path:"/login"})}))))}(e),function(e){e.afterEach((e=>{var t;const n=null==(t=e.meta)?void 0:t.title;document.title=n?`${n} | ${Cs}`:Cs}))}(e)}const qN=function(e){const t=_l(e.routes,e),n=e.parseQuery||Ol,o=e.stringifyQuery||Ml,r=e.history,i=Nl(),a=Nl(),l=Nl(),s=Ot(Ga);let c=Ga;ba&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=xa.bind(null,(e=>""+e)),d=xa.bind(null,$a),p=xa.bind(null,Na);function h(e,i){if(i=ya({},i||s.value),"string"==typeof e){const o=Ha(n,e,i.path),a=t.resolve({path:o.path},i),l=r.createHref(o.fullPath);return ya(o,a,{params:p(a.params),hash:Na(o.hash),redirectedFrom:void 0,href:l})}let a;if(null!=e.path)a=ya({},e,{path:Ha(n,e.path,i.path).path});else{const t=ya({},e.params);for(const e in t)null==t[e]&&delete t[e];a=ya({},e,{params:d(t)}),i.params=d(i.params)}const l=t.resolve(a,i),c=e.hash||"";l.params=u(p(l.params));const h=function(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}(o,ya({},e,{hash:(f=c,Ba(f).replace(Ma,"{").replace(Ia,"}").replace(Ea,"^")),path:l.path}));var f;const v=r.createHref(h);return ya({fullPath:h,hash:c,query:o===Ml?Fl(e.query):e.query||{}},l,{redirectedFrom:void 0,href:v})}function f(e){return"string"==typeof e?Ha(n,e,s.value.path):ya({},e)}function v(e,t){if(c!==e)return fl(8,{from:t,to:e})}function m(e){return b(e)}function g(e){const t=e.matched[e.matched.length-1];if(t&&t.redirect){const{redirect:n}=t;let o="function"==typeof n?n(e):n;return"string"==typeof o&&(o=o.includes("?")||o.includes("#")?o=f(o):{path:o},o.params={}),ya({query:e.query,hash:e.hash,params:null!=o.path?{}:e.params},o)}}function b(e,t){const n=c=h(e),r=s.value,i=e.state,a=e.force,l=!0===e.replace,u=g(n);if(u)return b(ya(f(u),{state:"object"==typeof u?ya({},i,u.state):i,force:a,replace:l}),t||n);const d=n;let p;return d.redirectedFrom=t,!a&&function(e,t,n){const o=t.matched.length-1,r=n.matched.length-1;return o>-1&&o===r&&Ua(t.matched[o],n.matched[r])&&Va(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}(o,r,n)&&(p=fl(16,{to:d,from:r}),E(r,r,!0,!1)),(p?Promise.resolve(p):C(d,r)).catch((e=>vl(e)?vl(e,2)?e:R(e):z(e,d,r))).then((e=>{if(e){if(vl(e,2))return b(ya({replace:l},f(e.to),{state:"object"==typeof e.to?ya({},i,e.to.state):i,force:a}),t||d)}else e=k(d,r,!0,l,i);return w(d,r,e),e}))}function y(e,t){const n=v(e,t);return n?Promise.reject(n):Promise.resolve()}function x(e){const t=F.values().next().value;return t&&"function"==typeof t.runWithContext?t.runWithContext(e):e()}function C(e,t){let n;const[o,r,l]=function(e,t){const n=[],o=[],r=[],i=Math.max(t.matched.length,e.matched.length);for(let a=0;aUa(e,i)))?o.push(i):n.push(i));const l=e.matched[a];l&&(t.matched.find((e=>Ua(e,l)))||r.push(l))}return[n,o,r]}(e,t);n=Hl(o.reverse(),"beforeRouteLeave",e,t);for(const i of o)i.leaveGuards.forEach((o=>{n.push(jl(o,e,t))}));const s=y.bind(null,e,t);return n.push(s),L(n).then((()=>{n=[];for(const o of i.list())n.push(jl(o,e,t));return n.push(s),L(n)})).then((()=>{n=Hl(r,"beforeRouteUpdate",e,t);for(const o of r)o.updateGuards.forEach((o=>{n.push(jl(o,e,t))}));return n.push(s),L(n)})).then((()=>{n=[];for(const o of l)if(o.beforeEnter)if(wa(o.beforeEnter))for(const r of o.beforeEnter)n.push(jl(r,e,t));else n.push(jl(o.beforeEnter,e,t));return n.push(s),L(n)})).then((()=>(e.matched.forEach((e=>e.enterCallbacks={})),n=Hl(l,"beforeRouteEnter",e,t,x),n.push(s),L(n)))).then((()=>{n=[];for(const o of a.list())n.push(jl(o,e,t));return n.push(s),L(n)})).catch((e=>vl(e,8)?e:Promise.reject(e)))}function w(e,t,n){l.list().forEach((o=>x((()=>o(e,t,n)))))}function k(e,t,n,o,i){const a=v(e,t);if(a)return a;const l=t===Ga,c=ba?history.state:{};n&&(o||l?r.replace(e.fullPath,ya({scroll:l&&c&&c.scroll},i)):r.push(e.fullPath,i)),s.value=e,E(e,t,n,l),R()}let S;function _(){S||(S=r.listen(((e,t,n)=>{if(!I.listening)return;const o=h(e),i=g(o);if(i)return void b(ya(i,{replace:!0}),o).catch(Ca);c=o;const a=s.value;var l,u;ba&&(l=rl(a.fullPath,n.delta),u=nl(),il.set(l,u)),C(o,a).catch((e=>vl(e,12)?e:vl(e,2)?(b(e.to,o).then((e=>{vl(e,20)&&!n.delta&&n.type===Xa.pop&&r.go(-1,!1)})).catch(Ca),Promise.reject()):(n.delta&&r.go(-n.delta,!1),z(e,o,a)))).then((e=>{(e=e||k(o,a,!1))&&(n.delta&&!vl(e,8)?r.go(-n.delta,!1):n.type===Xa.pop&&vl(e,20)&&r.go(-1,!1)),w(o,a,e)})).catch(Ca)})))}let P,T=Nl(),A=Nl();function z(e,t,n){R(e);const o=A.list();return o.length&&o.forEach((o=>o(e,t,n))),Promise.reject(e)}function R(e){return P||(P=!e,_(),T.list().forEach((([t,n])=>e?n(e):t())),T.reset()),e}function E(t,n,o,r){const{scrollBehavior:i}=e;if(!ba||!i)return Promise.resolve();const a=!o&&function(e){const t=il.get(e);return il.delete(e),t}(rl(t.fullPath,0))||(r||!o)&&history.state&&history.state.scroll||null;return tn().then((()=>i(t,n,a))).then((e=>e&&ol(e))).catch((e=>z(e,t,n)))}const O=e=>r.go(e);let M;const F=new Set,I={currentRoute:s,listening:!0,addRoute:function(e,n){let o,r;return ul(e)?(o=t.getRecordMatcher(e),r=n):r=e,t.addRoute(r,o)},removeRoute:function(e){const n=t.getRecordMatcher(e);n&&t.removeRoute(n)},clearRoutes:t.clearRoutes,hasRoute:function(e){return!!t.getRecordMatcher(e)},getRoutes:function(){return t.getRoutes().map((e=>e.record))},resolve:h,options:e,push:m,replace:function(e){return m(ya(f(e),{replace:!0}))},go:O,back:()=>O(-1),forward:()=>O(1),beforeEach:i.add,beforeResolve:a.add,afterEach:l.add,onError:A.add,isReady:function(){return P&&s.value!==Ga?Promise.resolve():new Promise(((e,t)=>{T.add([e,t])}))},install(e){e.component("RouterLink",Ul),e.component("RouterView",Gl),e.config.globalProperties.$router=this,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>It(s)}),ba&&!M&&s.value===Ga&&(M=!0,m(r.location).catch((e=>{})));const t={};for(const o in Ga)Object.defineProperty(t,o,{get:()=>s.value[o],enumerable:!0});e.provide(Bl,this),e.provide(Dl,mt(t)),e.provide($l,s);const n=e.unmount;F.add(e),e.unmount=function(){F.delete(e),F.size<1&&(c=Ga,S&&S(),S=null,s.value=Ga,M=!1,P=!1),n()}}};function L(e){return e.reduce(((e,t)=>e.then((()=>x(t)))),Promise.resolve())}return I}({history:(KN="/",(KN=location.host?KN||location.pathname+location.search:"").includes("#")||(KN+="#"),cl(KN)),routes:gs,scrollBehavior:()=>({left:0,top:0})});var KN;const GN=zn({__name:"AppProvider",setup(e){const t=_N(),n={"zh-CN":[$_,cP],"en-US":[j_,vP],"fa-IR":[q_,UP],"ko-KR":[U_,BP],"vi-VN":[V_,WP],"zh-TW":[N_,cP],"ja-JP":[W_,EP],"ru-RU":[H_,_P]};return function(){const e=X$.common;for(const t in e)CN(`--${w_(t)}`,document.documentElement).value=e[t]||"","primaryColor"===t&&window.localStorage.setItem("__THEME_COLOR__",e[t]||"")}(),(e,o)=>{const r=ZO;return _r(),Rr(r,{"wh-full":"",locale:n[It(t).lang][0],"date-locale":n[It(t).lang][1],theme:It(t).isDark?It(B$):void 0,"theme-overrides":It(X$)},{default:hn((()=>[to(e.$slots,"default")])),_:3},8,["locale","date-locale","theme","theme-overrides"])}}}),XN=ga(zn({__name:"App",setup(e){const t=BN();return ir((()=>{(()=>{const{balance:e,plan:n,expired_at:o,subscribe:r,email:i}=t;if(window.$crisp&&i){const t=[["Balance",(e/100).toString()],...(null==n?void 0:n.name)?[["Plan",n.name]]:[],["ExpireTime",Vf(o)],["UsedTraffic",Qf(((null==r?void 0:r.u)||0)+((null==r?void 0:r.d)||0))],["AllTraffic",Qf(null==r?void 0:r.transfer_enable)]];window.$crisp.push(["set","user:email",i]),window.$crisp.push(["set","session:data",[t]])}})(),(()=>{const{balance:e,plan:n,expired_at:o,subscribe:r,email:i}=t;window.tidioChatApi&&i&&(window.tidioChatApi.setVisitorData({name:i,email:i}),window.tidioChatApi.setContactProperties({Plan:(null==n?void 0:n.name)||"-",ExpireTime:Vf(o),UsedTraffic:Qf(((null==r?void 0:r.u)||0)+((null==r?void 0:r.d)||0)),AllTraffic:Qf(null==r?void 0:r.transfer_enable),Balance:e/100}))})()})),(e,t)=>{const n=Xn("router-view");return _r(),Rr(GN,null,{default:hn((()=>[Lr(n,null,{default:hn((({Component:e})=>[(_r(),Rr(Qn(e)))])),_:1})])),_:1})}}}));XN.use(function(){const e=ce(!0),t=e.run((()=>Et({})));let n=[],o=[];const r=St({install(e){ks(r),r._a=e,e.provide(Ss,r),e.config.globalProperties.$pinia=r,o.forEach((e=>n.push(e))),o=[]},use(e){return this._a?n.push(e):o.push(e),this},_p:n,_a:null,_e:e,_s:new Map,state:t});return r}()),function(){const e=_N(),t=ai((()=>({theme:e.isDark?B$:void 0,themeOverrides:X$}))),{message:n,dialog:o,notification:r,loadingBar:i}=L$(["message","dialog","notification","loadingBar"],{configProviderProps:t});var a;window.$loadingBar=i,window.$notification=r,window.$message=function(e){let t=null;return new class{removeMessage(e=t,n=2e3){setTimeout((()=>{e&&(e.destroy(),e=null)}),n)}showMessage(n,o,r={}){if(t&&"loading"===t.type)t.type=n,t.content=o,"loading"!==n&&this.removeMessage(t,r.duration);else{const i=e[n](o,r);"loading"===n&&(t=i)}}loading(e){this.showMessage("loading",e,{duration:0})}success(e,t={}){this.showMessage("success",e,t)}error(e,t={}){this.showMessage("error",e,t)}info(e,t={}){this.showMessage("info",e,t)}warning(e,t={}){this.showMessage("warning",e,t)}}}(n),window.$dialog=((a=o).confirm=function(e={}){const t=!(function(e){return null===e}(n=e.title)||function(e){return void 0===e}(n));var n;return new Promise((n=>{a[e.type||"warning"](d({showIcon:t,positiveText:jf.global.t("确定"),negativeText:jf.global.t("取消"),onPositiveClick:()=>{e.confirm&&e.confirm(),n(!0)},onNegativeClick:()=>{e.cancel&&e.cancel(),n(!1)},onMaskClick:()=>{e.cancel&&e.cancel(),n(!1)}},e))}))},a)}(),function(e){e.use(qN),VN(qN)}(XN),function(e){v(this,null,(function*(){e.use(jf),Hf()}))}(XN),XN.mount("#app");const YN={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},QN=[Ir("path",{fill:"currentColor",d:"M6.225 4.811a1 1 0 0 0-1.414 1.414L10.586 12L4.81 17.775a1 1 0 1 0 1.414 1.414L12 13.414l5.775 5.775a1 1 0 0 0 1.414-1.414L13.414 12l5.775-5.775a1 1 0 0 0-1.414-1.414L12 10.586z"},null,-1)],ZN={name:"gg-close",render:function(e,t){return _r(),zr("svg",YN,[...QN])}},JN={class:"h-15 f-c-c relative"},ej=["src"],tj=(e,t)=>{const n=e.__vccOpts||e;for(const[o,r]of t)n[o]=r;return n},nj=tj(zn({__name:"SideLogo",setup(e){const t=_N();return(e,n)=>{const o=ZN,r=oO;return _r(),zr("div",JN,[It(t).logo?(_r(),zr("img",{key:0,src:It(t).logo,height:"30"},null,8,ej)):$r("",!0),fn(Ir("h2",{class:"title-text font-bold color-primary text-base mx-2"},oe(It(t).title),513),[[Mi,!It(t).collapsed]]),Lr(r,{onClick:[n[0]||(n[0]=pa((()=>{}),["stop"])),It(t).switchCollapsed],class:"absolute right-4 h-auto p-0 md:hidden",tertiary:"",size:"medium"},{icon:hn((()=>[Lr(o,{class:"cursor-pointer opacity-85"})])),_:1},8,["onClick"])])}}}),[["__scopeId","data-v-143fb1b0"]]),oj=zn({__name:"SideMenu",setup(e){const t=_N(),n=e=>jf.global.t(e),o=Xl(),r=Yl(),i=$N(),a=ai((()=>{var e;return(null==(e=r.meta)?void 0:e.activeMenu)||r.name})),l=ai((()=>{const e=i.menus.reduce(((e,t)=>{var o,r,i,a;const l=c(t);if(null==(r=null==(o=l.meta)?void 0:o.group)?void 0:r.key){const t=l.meta.group.key,o=e.findIndex((e=>e.key===t));if(-1!==o)null==(i=e[o].children)||i.push(l),e[o].children=null==(a=e[o].children)?void 0:a.sort(((e,t)=>e.order-t.order));else{const o={type:"group",label:n(l.meta.group.label||""),key:t,children:[l]};e.push(o)}}else e.push(l);return e.sort(((e,t)=>e.order-t.order))}),[]);return e.sort(((e,t)=>"group"===e.type&&"group"!==t.type?1:"group"!==e.type&&"group"===t.type?-1:e.order-t.order))}));function s(e,t){return Jf(t)?t:"/"+[e,t].filter((e=>!!e&&"/"!==e)).map((e=>e.replace(/(^\/)|(\/$)/g,""))).join("/")}function c(e,t=""){const{title:o,order:r}=e.meta||{title:"",order:0},{name:i,path:a}=e,l=o||i||"",u=i||"",p=function(e){return(null==e?void 0:e.customIcon)?H$(e.customIcon,{size:18}):(null==e?void 0:e.icon)?j$(e.icon,{size:18}):null}(e.meta),h=r||0,f=e.meta;let v={label:n(l),key:u,path:s(t,a),icon:null!==p?p:void 0,meta:f,order:h};const m=function(e,t){var n;const o=(null==(n=e.children)?void 0:n.filter((e=>{var t;return e.name&&!(null==(t=e.meta)?void 0:t.isHidden)})))||[];return 1===o.length?c(o[0],t):o.length>1?{children:o.map((e=>c(e,t))).sort(((e,t)=>e.order-t.order))}:null}(e,v.path);return m&&(v=d(d({},v),m)),v}function u(e,t){Jf(t.path)?window.open(t.path):o.push(t.path)}return(e,n)=>{const o=PD;return _r(),Rr(o,{ref:"menu",class:"side-menu",accordion:"","root-indent":18,indent:0,"collapsed-icon-size":22,"collapsed-width":60,options:l.value,value:a.value,"onUpdate:value":u,onClick:n[0]||(n[0]=e=>{window.innerWidth<=950&&(t.collapsed=!0)})},null,8,["options","value"])}}}),rj=zn({__name:"index",setup:e=>(e,t)=>(_r(),zr(yr,null,[Lr(nj),Lr(oj)],64))}),ij=zn({__name:"AppMain",setup(e){const t=WN();return(e,n)=>{const o=Xn("router-view");return _r(),Rr(o,null,{default:hn((({Component:e,route:n})=>[It(t).reloading?$r("",!0):(_r(),Rr(Qn(e),{key:n.fullPath}))])),_:1})}}}),aj=zn({__name:"BreadCrumb",setup(e){const t=Yl();return(e,n)=>{const o=GE,r=KE;return _r(),Rr(r,null,{default:hn((()=>[(_r(!0),zr(yr,null,eo(It(t).matched.filter((e=>{var t;return!!(null==(t=e.meta)?void 0:t.title)})),(t=>(_r(),Rr(o,{key:t.path},{default:hn((()=>{return[(_r(),Rr(Qn((n=t.meta,(null==n?void 0:n.customIcon)?H$(n.customIcon,{size:18}):(null==n?void 0:n.icon)?j$(n.icon,{size:18}):null)))),Dr(" "+oe(e.$t(t.meta.title)),1)];var n})),_:2},1024)))),128))])),_:1})}}}),lj={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},sj=[Ir("path",{fill:"currentColor",d:"M11 13h10v-2H11m0-2h10V7H11M3 3v2h18V3M3 21h18v-2H3m0-7l4 4V8m4 9h10v-2H11z"},null,-1)],cj={name:"mdi-format-indent-decrease",render:function(e,t){return _r(),zr("svg",lj,[...sj])}},uj={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},dj=[Ir("path",{fill:"currentColor",d:"M11 13h10v-2H11m0-2h10V7H11M3 3v2h18V3M11 17h10v-2H11M3 8v8l4-4m-4 9h18v-2H3z"},null,-1)],pj={name:"mdi-format-indent-increase",render:function(e,t){return _r(),zr("svg",uj,[...dj])}},hj=zn({__name:"MenuCollapse",setup(e){const t=_N();return(e,n)=>{const o=pj,r=cj,i=SF;return _r(),Rr(i,{size:"20","cursor-pointer":"",onClick:It(t).switchCollapsed},{default:hn((()=>[It(t).collapsed?(_r(),Rr(o,{key:0})):(_r(),Rr(r,{key:1}))])),_:1},8,["onClick"])}}}),fj={class:"inline-block",viewBox:"0 0 1024 1024",width:"1em",height:"1em"},vj=[Ir("path",{fill:"currentColor",d:"m290 236.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L169 160c-5.1-.6-9.5 3.7-8.9 8.9L179 329.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L370 423.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3zm352.7 187.3c3.1 3.1 8.2 3.1 11.3 0l133.7-133.6l43.7 43.7a8.01 8.01 0 0 0 13.6-4.7L863.9 169c.6-5.1-3.7-9.5-8.9-8.9L694.8 179c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L600.3 370a8.03 8.03 0 0 0 0 11.3zM845 694.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L654 600.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L734 787.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L855 864c5.1.6 9.5-3.7 8.9-8.9zm-463.7-94.6a8.03 8.03 0 0 0-11.3 0L236.3 733.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L160.1 855c-.6 5.1 3.7 9.5 8.9 8.9L329.2 845c6.6-.8 9.4-8.9 4.7-13.6L290 787.6L423.7 654c3.1-3.1 3.1-8.2 0-11.3z"},null,-1)],mj={name:"ant-design-fullscreen-outlined",render:function(e,t){return _r(),zr("svg",fj,[...vj])}},gj={class:"inline-block",viewBox:"0 0 1024 1024",width:"1em",height:"1em"},bj=[Ir("path",{fill:"currentColor",d:"M391 240.9c-.8-6.6-8.9-9.4-13.6-4.7l-43.7 43.7L200 146.3a8.03 8.03 0 0 0-11.3 0l-42.4 42.3a8.03 8.03 0 0 0 0 11.3L280 333.6l-43.9 43.9a8.01 8.01 0 0 0 4.7 13.6L401 410c5.1.6 9.5-3.7 8.9-8.9zm10.1 373.2L240.8 633c-6.6.8-9.4 8.9-4.7 13.6l43.9 43.9L146.3 824a8.03 8.03 0 0 0 0 11.3l42.4 42.3c3.1 3.1 8.2 3.1 11.3 0L333.7 744l43.7 43.7A8.01 8.01 0 0 0 391 783l18.9-160.1c.6-5.1-3.7-9.4-8.8-8.8m221.8-204.2L783.2 391c6.6-.8 9.4-8.9 4.7-13.6L744 333.6L877.7 200c3.1-3.1 3.1-8.2 0-11.3l-42.4-42.3a8.03 8.03 0 0 0-11.3 0L690.3 279.9l-43.7-43.7a8.01 8.01 0 0 0-13.6 4.7L614.1 401c-.6 5.2 3.7 9.5 8.8 8.9M744 690.4l43.9-43.9a8.01 8.01 0 0 0-4.7-13.6L623 614c-5.1-.6-9.5 3.7-8.9 8.9L633 783.1c.8 6.6 8.9 9.4 13.6 4.7l43.7-43.7L824 877.7c3.1 3.1 8.2 3.1 11.3 0l42.4-42.3c3.1-3.1 3.1-8.2 0-11.3z"},null,-1)],yj={name:"ant-design-fullscreen-exit-outlined",render:function(e,t){return _r(),zr("svg",gj,[...bj])}},xj=zn({__name:"FullScreen",setup(e){const{isFullscreen:t,toggle:n}=SN();return(e,o)=>{const r=yj,i=mj,a=SF;return _r(),Rr(a,{class:"mr-5 cursor-pointer",size:"18",onClick:It(n)},{default:hn((()=>[It(t)?(_r(),Rr(r,{key:0})):(_r(),Rr(i,{key:1}))])),_:1},8,["onClick"])}}}),Cj={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},wj=[Ir("path",{fill:"currentColor",d:"M15.88 9.29L12 13.17L8.12 9.29a.996.996 0 1 0-1.41 1.41l4.59 4.59c.39.39 1.02.39 1.41 0l4.59-4.59a.996.996 0 0 0 0-1.41c-.39-.38-1.03-.39-1.42 0"},null,-1)],kj={name:"ic-round-expand-more",render:function(e,t){return _r(),zr("svg",Cj,[...wj])}},Sj={class:"inline-block",viewBox:"0 0 32 32",width:"1em",height:"1em"},_j=[Ir("path",{fill:"none",d:"M8.007 24.93A4.996 4.996 0 0 1 13 20h6a4.996 4.996 0 0 1 4.993 4.93a11.94 11.94 0 0 1-15.986 0M20.5 12.5A4.5 4.5 0 1 1 16 8a4.5 4.5 0 0 1 4.5 4.5"},null,-1),Ir("path",{fill:"currentColor",d:"M26.749 24.93A13.99 13.99 0 1 0 2 16a13.9 13.9 0 0 0 3.251 8.93l-.02.017c.07.084.15.156.222.239c.09.103.187.2.28.3q.418.457.87.87q.14.124.28.242q.48.415.99.782c.044.03.084.069.128.1v-.012a13.9 13.9 0 0 0 16 0v.012c.044-.031.083-.07.128-.1q.51-.368.99-.782q.14-.119.28-.242q.451-.413.87-.87c.093-.1.189-.197.28-.3c.071-.083.152-.155.222-.24ZM16 8a4.5 4.5 0 1 1-4.5 4.5A4.5 4.5 0 0 1 16 8M8.007 24.93A4.996 4.996 0 0 1 13 20h6a4.996 4.996 0 0 1 4.993 4.93a11.94 11.94 0 0 1-15.986 0"},null,-1)],Pj={name:"carbon-user-avatar-filled",render:function(e,t){return _r(),zr("svg",Sj,[..._j])}},Tj={class:"hidden md:block"},Aj=zn({__name:"UserAvatar",setup(e){const t=BN(),n=e=>jf.global.t(e),o=[{label:n("个人中心"),key:"profile",icon:j$("mdi-account-outline",{size:14})},{label:n("登出"),key:"logout",icon:j$("mdi:exit-to-app",{size:14})}];function r(e){"logout"===e&&window.$dialog.confirm({title:n("提示"),type:"info",content:n("确认退出?"),confirm(){t.logout(),window.$message.success(n("已退出登录"))}}),"profile"===e&&qN.push("/profile")}return(e,n)=>{const i=Pj,a=kj,l=oO,s=DF;return _r(),Rr(s,{options:o,onSelect:r},{default:hn((()=>[Lr(l,{text:"",flex:"","cursor-pointer":"","items-center":""},{default:hn((()=>[Lr(i,{class:"mr-0 h-5 w-5 rounded-full md:mr-2.5 md:h-8 md:w-8"}),Lr(a,{class:"h-5 w-5 md:hidden"}),Ir("span",Tj,oe(It(t).email),1)])),_:1})])),_:1})}}}),zj={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},Rj=[Ir("path",{fill:"currentColor",d:"M11.4 18.4H.9a.9.9 0 0 1-.9-.9V7.3a.9.9 0 0 1 .9-.9h10.5zm-4.525-2.72c.058.187.229.32.431.32h.854a.45.45 0 0 0 .425-.597l.001.003l-2.15-6.34a.45.45 0 0 0-.426-.306H4.791a.45.45 0 0 0-.425.302l-.001.003l-2.154 6.34a.45.45 0 0 0 .426.596h.856a.45.45 0 0 0 .431-.323l.001-.003l.342-1.193h2.258l.351 1.195zM5.41 10.414s.16.79.294 1.245l.406 1.408H4.68l.415-1.408c.131-.455.294-1.245.294-1.245zM23.1 18.4H12.6v-12h10.5a.9.9 0 0 1 .9.9v10.2a.9.9 0 0 1-.9.9m-1.35-8.55h-2.4v-.601a.45.45 0 0 0-.45-.45h-.601a.45.45 0 0 0-.45.45v.601h-2.4a.45.45 0 0 0-.45.45v.602c0 .248.201.45.45.45h4.281a5.9 5.9 0 0 1-1.126 1.621l.001-.001a7 7 0 0 1-.637-.764l-.014-.021a.45.45 0 0 0-.602-.129l.002-.001l-.273.16l-.24.146a.45.45 0 0 0-.139.642l-.001-.001c.253.359.511.674.791.969l-.004-.004c-.28.216-.599.438-.929.645l-.05.029a.45.45 0 0 0-.159.61l-.001-.002l.298.52a.45.45 0 0 0 .628.159l-.002.001c.507-.312.94-.619 1.353-.95l-.026.02c.387.313.82.62 1.272.901l.055.032a.45.45 0 0 0 .626-.158l.001-.002l.298-.52a.45.45 0 0 0-.153-.605l-.002-.001a12 12 0 0 1-1.004-.696l.027.02a6.7 6.7 0 0 0 1.586-2.572l.014-.047h.43a.45.45 0 0 0 .45-.45v-.602a.45.45 0 0 0-.45-.447h-.001z"},null,-1)],Ej={name:"fontisto-language",render:function(e,t){return _r(),zr("svg",zj,[...Rj])}},Oj=zn({__name:"SwitchLang",setup(e){const t=_N();return(e,n)=>{const o=Ej,r=oO,i=sM;return _r(),Rr(i,{value:It(t).lang,"onUpdate:value":n[0]||(n[0]=e=>It(t).lang=e),options:Object.entries(It(Wf)).map((([e,t])=>({label:t,value:e}))),trigger:"click","on-update:value":It(t).switchLang},{default:hn((()=>[Lr(r,{text:"","icon-placement":"left",class:"mr-5"},{icon:hn((()=>[Lr(o)])),_:1})])),_:1},8,["value","options","on-update:value"])}}}),Mj={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},Fj=[Ir("path",{fill:"currentColor",d:"m3.55 19.09l1.41 1.41l1.8-1.79l-1.42-1.42M12 6c-3.31 0-6 2.69-6 6s2.69 6 6 6s6-2.69 6-6c0-3.32-2.69-6-6-6m8 7h3v-2h-3m-2.76 7.71l1.8 1.79l1.41-1.41l-1.79-1.8M20.45 5l-1.41-1.4l-1.8 1.79l1.42 1.42M13 1h-2v3h2M6.76 5.39L4.96 3.6L3.55 5l1.79 1.81zM1 13h3v-2H1m12 9h-2v3h2"},null,-1)],Ij={name:"mdi-white-balance-sunny",render:function(e,t){return _r(),zr("svg",Mj,[...Fj])}},Lj={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},Bj=[Ir("path",{fill:"currentColor",d:"M2 12a10 10 0 0 0 13 9.54a10 10 0 0 1 0-19.08A10 10 0 0 0 2 12"},null,-1)],Dj={name:"mdi-moon-waning-crescent",render:function(e,t){return _r(),zr("svg",Lj,[...Bj])}},$j=zn({__name:"ThemeMode",setup(e){const t=_N(),n=wN(),o=()=>{t.toggleDark(),function(e=!1,t={}){const{truthyValue:n=!0,falsyValue:o=!1}=t,r=Rt(e),i=Et(e);function a(e){if(arguments.length)return i.value=e,i.value;{const e=Q$(n);return i.value=i.value===e?Q$(o):e,i.value}}return r?a:[i,a]}(n)()};return(e,t)=>{const r=Dj,i=Ij,a=SF;return _r(),Rr(a,{class:"mr-5 cursor-pointer",size:"18",onClick:o},{default:hn((()=>[It(n)?(_r(),Rr(r,{key:0})):(_r(),Rr(i,{key:1}))])),_:1})}}}),Nj={flex:"","items-center":""},jj={"ml-auto":"",flex:"","items-center":""},Hj=zn({__name:"index",setup:e=>(e,t)=>(_r(),zr(yr,null,[Ir("div",Nj,[Lr(hj),Lr(aj)]),Ir("div",jj,[Lr($j),Lr(Oj),Lr(xj),Lr(Aj)])],64))}),Wj={class:"flex flex-col flex-1 overflow-hidden"},Uj={class:"flex-1 overflow-hidden bg-hex-f5f6fb dark:bg-hex-101014"},Vj=zn({__name:"index",setup(e){const t=_N();function n(e){t.collapsed=e}const o=ai({get:()=>r.value&&!t.collapsed,set:e=>t.collapsed=!e}),r=Et(!1),i=()=>{document.body.clientWidth<=950?(r.value=!0,t.collapsed=!0):(t.collapsed=!1,r.value=!1)};return $n((()=>{window.addEventListener("resize",i),i()})),(e,r)=>{const i=WB,a=ZI,l=BB;return _r(),Rr(l,{"has-sider":"","wh-full":""},{default:hn((()=>[fn(Lr(i,{bordered:"","collapse-mode":"transform","collapsed-width":0,width:220,"native-scrollbar":!1,collapsed:It(t).collapsed,"on-update:collapsed":n},{default:hn((()=>[Lr(rj)])),_:1},8,["collapsed"]),[[Mi,!o.value]]),Lr(a,{show:o.value,"onUpdate:show":r[0]||(r[0]=e=>o.value=e),width:220,placement:"left"},{default:hn((()=>[Lr(i,{bordered:"","collapse-mode":"transform","collapsed-width":0,width:220,"native-scrollbar":!1,collapsed:It(t).collapsed,"on-update:collapsed":n},{default:hn((()=>[Lr(rj)])),_:1},8,["collapsed"])])),_:1},8,["show"]),Ir("article",Wj,[Ir("header",{class:"flex items-center bg-white px-4",dark:"bg-dark border-0",style:G(`height: ${It(K$).height}px`)},[Lr(Hj)],4),Ir("section",Uj,[Lr(ij)])])])),_:1})}}}),qj=Object.freeze(Object.defineProperty({__proto__:null,default:Vj},Symbol.toStringTag,{value:"Module"})),Kj={},Gj={"f-c-c":"","flex-col":"","text-14":"",color:"#6a6a6a"},Xj=[Ir("p",null,[Dr(" Copyright © 2022-present "),Ir("a",{href:"https://github.com/zclzone",target:"__blank",hover:"decoration-underline color-primary"}," Ronnie Zhang ")],-1),Ir("p",null,null,-1)],Yj=tj(Kj,[["render",function(e,t){return _r(),zr("footer",Gj,Xj)}]]),Qj={class:"cus-scroll-y wh-full flex-col bg-[#f5f6fb] p-1 dark:bg-hex-121212 md:p-4"},Zj=zn({__name:"AppPage",props:{showFooter:{type:Boolean,default:!1}},setup:e=>(t,n)=>{const o=Yj,r=$E;return _r(),Rr(vi,{name:"fade-slide",mode:"out-in",appear:""},{default:hn((()=>[Ir("section",Qj,[to(t.$slots,"default"),e.showFooter?(_r(),Rr(o,{key:0,"mt-15":""})):$r("",!0),Lr(r,{bottom:20,class:"z-99999"})])])),_:3})}}),Jj={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},eH=[Ir("path",{fill:"currentColor",d:"M20 2H4c-.53 0-1.04.21-1.41.59C2.21 2.96 2 3.47 2 4v12c0 .53.21 1.04.59 1.41c.37.38.88.59 1.41.59h4l4 4l4-4h4c.53 0 1.04-.21 1.41-.59S22 16.53 22 16V4c0-.53-.21-1.04-.59-1.41C21.04 2.21 20.53 2 20 2M4 16V4h16v12h-4.83L12 19.17L8.83 16m1.22-9.96c.54-.36 1.25-.54 2.14-.54c.94 0 1.69.21 2.23.62q.81.63.81 1.68c0 .44-.15.83-.44 1.2c-.29.36-.67.64-1.13.85c-.26.15-.43.3-.52.47c-.09.18-.14.4-.14.68h-2c0-.5.1-.84.29-1.08c.21-.24.55-.52 1.07-.84c.26-.14.47-.32.64-.54c.14-.21.22-.46.22-.74c0-.3-.09-.52-.27-.69c-.18-.18-.45-.26-.76-.26c-.27 0-.49.07-.69.21c-.16.14-.26.35-.26.63H9.27c-.05-.69.23-1.29.78-1.65M11 14v-2h2v2Z"},null,-1)],tH={name:"mdi-tooltip-question-outline",render:function(e,t){return _r(),zr("svg",Jj,[...eH])}},nH={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},oH=[Ir("path",{fill:"currentColor",d:"M12 20a8 8 0 0 0 8-8a8 8 0 0 0-8-8a8 8 0 0 0-8 8a8 8 0 0 0 8 8m0-18a10 10 0 0 1 10 10a10 10 0 0 1-10 10C6.47 22 2 17.5 2 12A10 10 0 0 1 12 2m.5 5v5.25l4.5 2.67l-.75 1.23L11 13V7z"},null,-1)],rH={name:"mdi-clock-outline",render:function(e,t){return _r(),zr("svg",nH,[...oH])}},iH={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},aH=[Ir("path",{fill:"currentColor",d:"M6.18 15.64a2.18 2.18 0 0 1 2.18 2.18C8.36 19 7.38 20 6.18 20C5 20 4 19 4 17.82a2.18 2.18 0 0 1 2.18-2.18M4 4.44A15.56 15.56 0 0 1 19.56 20h-2.83A12.73 12.73 0 0 0 4 7.27zm0 5.66a9.9 9.9 0 0 1 9.9 9.9h-2.83A7.07 7.07 0 0 0 4 12.93z"},null,-1)],lH={name:"mdi-rss",render:function(e,t){return _r(),zr("svg",iH,[...aH])}},sH={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},cH=[Ir("path",{fill:"currentColor",d:"M12 21.5c-1.35-.85-3.8-1.5-5.5-1.5c-1.65 0-3.35.3-4.75 1.05c-.1.05-.15.05-.25.05c-.25 0-.5-.25-.5-.5V6c.6-.45 1.25-.75 2-1c1.11-.35 2.33-.5 3.5-.5c1.95 0 4.05.4 5.5 1.5c1.45-1.1 3.55-1.5 5.5-1.5c1.17 0 2.39.15 3.5.5c.75.25 1.4.55 2 1v14.6c0 .25-.25.5-.5.5c-.1 0-.15 0-.25-.05c-1.4-.75-3.1-1.05-4.75-1.05c-1.7 0-4.15.65-5.5 1.5M12 8v11.5c1.35-.85 3.8-1.5 5.5-1.5c1.2 0 2.4.15 3.5.5V7c-1.1-.35-2.3-.5-3.5-.5c-1.7 0-4.15.65-5.5 1.5m1 3.5c1.11-.68 2.6-1 4.5-1c.91 0 1.76.09 2.5.28V9.23c-.87-.15-1.71-.23-2.5-.23q-2.655 0-4.5.84zm4.5.17c-1.71 0-3.21.26-4.5.79v1.69c1.11-.65 2.6-.99 4.5-.99c1.04 0 1.88.08 2.5.24v-1.5c-.87-.16-1.71-.23-2.5-.23m2.5 2.9c-.87-.16-1.71-.24-2.5-.24c-1.83 0-3.33.27-4.5.8v1.69c1.11-.66 2.6-.99 4.5-.99c1.04 0 1.88.08 2.5.24z"},null,-1)],uH={name:"mdi-book-open-variant",render:function(e,t){return _r(),zr("svg",sH,[...cH])}},dH={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},pH=[Ir("g",{fill:"none"},[Ir("path",{d:"m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"}),Ir("path",{fill:"currentColor",d:"M10.5 20a1.5 1.5 0 0 0 3 0v-6.5H20a1.5 1.5 0 0 0 0-3h-6.5V4a1.5 1.5 0 0 0-3 0v6.5H4a1.5 1.5 0 0 0 0 3h6.5z"})],-1)],hH={name:"mingcute-add-fill",render:function(e,t){return _r(),zr("svg",dH,[...pH])}},fH={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},vH=[Ir("path",{fill:"currentColor",d:"M5.503 4.627L5.5 6.75v10.504a3.25 3.25 0 0 0 3.25 3.25h8.616a2.25 2.25 0 0 1-2.122 1.5H8.75A4.75 4.75 0 0 1 4 17.254V6.75c0-.98.627-1.815 1.503-2.123M17.75 2A2.25 2.25 0 0 1 20 4.25v13a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-13A2.25 2.25 0 0 1 8.75 2z"},null,-1)],mH={name:"fluent-copy24-filled",render:function(e,t){return _r(),zr("svg",fH,[...vH])}},gH={class:"inline-block",viewBox:"0 0 1200 1200",width:"1em",height:"1em"},bH=[Ir("path",{fill:"currentColor",d:"M0 0v545.312h545.312V0zm654.688 0v545.312H1200V0zM108.594 108.594h328.125v328.125H108.594zm654.687 0h328.125v328.125H763.281zM217.969 219.531v108.594h110.156V219.531zm653.906 0v108.594h108.594V219.531zM0 654.688V1200h545.312V654.688zm654.688 0V1200h108.595V873.438h108.594v108.595H1200V654.688h-108.594v108.595H980.469V654.688zM108.594 763.281h328.125v328.125H108.594zm109.375 108.594v110.156h110.156V871.875zm653.906 219.531V1200h108.594v-108.594zm219.531 0V1200H1200v-108.594z"},null,-1)],yH={name:"el-qrcode",render:function(e,t){return _r(),zr("svg",gH,[...bH])}};var xH={},CH={Aacute:"Á",aacute:"á",Abreve:"Ă",abreve:"ă",ac:"∾",acd:"∿",acE:"∾̳",Acirc:"Â",acirc:"â",acute:"´",Acy:"А",acy:"а",AElig:"Æ",aelig:"æ",af:"⁡",Afr:"𝔄",afr:"𝔞",Agrave:"À",agrave:"à",alefsym:"ℵ",aleph:"ℵ",Alpha:"Α",alpha:"α",Amacr:"Ā",amacr:"ā",amalg:"⨿",amp:"&",AMP:"&",andand:"⩕",And:"⩓",and:"∧",andd:"⩜",andslope:"⩘",andv:"⩚",ang:"∠",ange:"⦤",angle:"∠",angmsdaa:"⦨",angmsdab:"⦩",angmsdac:"⦪",angmsdad:"⦫",angmsdae:"⦬",angmsdaf:"⦭",angmsdag:"⦮",angmsdah:"⦯",angmsd:"∡",angrt:"∟",angrtvb:"⊾",angrtvbd:"⦝",angsph:"∢",angst:"Å",angzarr:"⍼",Aogon:"Ą",aogon:"ą",Aopf:"𝔸",aopf:"𝕒",apacir:"⩯",ap:"≈",apE:"⩰",ape:"≊",apid:"≋",apos:"'",ApplyFunction:"⁡",approx:"≈",approxeq:"≊",Aring:"Å",aring:"å",Ascr:"𝒜",ascr:"𝒶",Assign:"≔",ast:"*",asymp:"≈",asympeq:"≍",Atilde:"Ã",atilde:"ã",Auml:"Ä",auml:"ä",awconint:"∳",awint:"⨑",backcong:"≌",backepsilon:"϶",backprime:"‵",backsim:"∽",backsimeq:"⋍",Backslash:"∖",Barv:"⫧",barvee:"⊽",barwed:"⌅",Barwed:"⌆",barwedge:"⌅",bbrk:"⎵",bbrktbrk:"⎶",bcong:"≌",Bcy:"Б",bcy:"б",bdquo:"„",becaus:"∵",because:"∵",Because:"∵",bemptyv:"⦰",bepsi:"϶",bernou:"ℬ",Bernoullis:"ℬ",Beta:"Β",beta:"β",beth:"ℶ",between:"≬",Bfr:"𝔅",bfr:"𝔟",bigcap:"⋂",bigcirc:"◯",bigcup:"⋃",bigodot:"⨀",bigoplus:"⨁",bigotimes:"⨂",bigsqcup:"⨆",bigstar:"★",bigtriangledown:"▽",bigtriangleup:"△",biguplus:"⨄",bigvee:"⋁",bigwedge:"⋀",bkarow:"⤍",blacklozenge:"⧫",blacksquare:"▪",blacktriangle:"▴",blacktriangledown:"▾",blacktriangleleft:"◂",blacktriangleright:"▸",blank:"␣",blk12:"▒",blk14:"░",blk34:"▓",block:"█",bne:"=⃥",bnequiv:"≡⃥",bNot:"⫭",bnot:"⌐",Bopf:"𝔹",bopf:"𝕓",bot:"⊥",bottom:"⊥",bowtie:"⋈",boxbox:"⧉",boxdl:"┐",boxdL:"╕",boxDl:"╖",boxDL:"╗",boxdr:"┌",boxdR:"╒",boxDr:"╓",boxDR:"╔",boxh:"─",boxH:"═",boxhd:"┬",boxHd:"╤",boxhD:"╥",boxHD:"╦",boxhu:"┴",boxHu:"╧",boxhU:"╨",boxHU:"╩",boxminus:"⊟",boxplus:"⊞",boxtimes:"⊠",boxul:"┘",boxuL:"╛",boxUl:"╜",boxUL:"╝",boxur:"└",boxuR:"╘",boxUr:"╙",boxUR:"╚",boxv:"│",boxV:"║",boxvh:"┼",boxvH:"╪",boxVh:"╫",boxVH:"╬",boxvl:"┤",boxvL:"╡",boxVl:"╢",boxVL:"╣",boxvr:"├",boxvR:"╞",boxVr:"╟",boxVR:"╠",bprime:"‵",breve:"˘",Breve:"˘",brvbar:"¦",bscr:"𝒷",Bscr:"ℬ",bsemi:"⁏",bsim:"∽",bsime:"⋍",bsolb:"⧅",bsol:"\\",bsolhsub:"⟈",bull:"•",bullet:"•",bump:"≎",bumpE:"⪮",bumpe:"≏",Bumpeq:"≎",bumpeq:"≏",Cacute:"Ć",cacute:"ć",capand:"⩄",capbrcup:"⩉",capcap:"⩋",cap:"∩",Cap:"⋒",capcup:"⩇",capdot:"⩀",CapitalDifferentialD:"ⅅ",caps:"∩︀",caret:"⁁",caron:"ˇ",Cayleys:"ℭ",ccaps:"⩍",Ccaron:"Č",ccaron:"č",Ccedil:"Ç",ccedil:"ç",Ccirc:"Ĉ",ccirc:"ĉ",Cconint:"∰",ccups:"⩌",ccupssm:"⩐",Cdot:"Ċ",cdot:"ċ",cedil:"¸",Cedilla:"¸",cemptyv:"⦲",cent:"¢",centerdot:"·",CenterDot:"·",cfr:"𝔠",Cfr:"ℭ",CHcy:"Ч",chcy:"ч",check:"✓",checkmark:"✓",Chi:"Χ",chi:"χ",circ:"ˆ",circeq:"≗",circlearrowleft:"↺",circlearrowright:"↻",circledast:"⊛",circledcirc:"⊚",circleddash:"⊝",CircleDot:"⊙",circledR:"®",circledS:"Ⓢ",CircleMinus:"⊖",CirclePlus:"⊕",CircleTimes:"⊗",cir:"○",cirE:"⧃",cire:"≗",cirfnint:"⨐",cirmid:"⫯",cirscir:"⧂",ClockwiseContourIntegral:"∲",CloseCurlyDoubleQuote:"”",CloseCurlyQuote:"’",clubs:"♣",clubsuit:"♣",colon:":",Colon:"∷",Colone:"⩴",colone:"≔",coloneq:"≔",comma:",",commat:"@",comp:"∁",compfn:"∘",complement:"∁",complexes:"ℂ",cong:"≅",congdot:"⩭",Congruent:"≡",conint:"∮",Conint:"∯",ContourIntegral:"∮",copf:"𝕔",Copf:"ℂ",coprod:"∐",Coproduct:"∐",copy:"©",COPY:"©",copysr:"℗",CounterClockwiseContourIntegral:"∳",crarr:"↵",cross:"✗",Cross:"⨯",Cscr:"𝒞",cscr:"𝒸",csub:"⫏",csube:"⫑",csup:"⫐",csupe:"⫒",ctdot:"⋯",cudarrl:"⤸",cudarrr:"⤵",cuepr:"⋞",cuesc:"⋟",cularr:"↶",cularrp:"⤽",cupbrcap:"⩈",cupcap:"⩆",CupCap:"≍",cup:"∪",Cup:"⋓",cupcup:"⩊",cupdot:"⊍",cupor:"⩅",cups:"∪︀",curarr:"↷",curarrm:"⤼",curlyeqprec:"⋞",curlyeqsucc:"⋟",curlyvee:"⋎",curlywedge:"⋏",curren:"¤",curvearrowleft:"↶",curvearrowright:"↷",cuvee:"⋎",cuwed:"⋏",cwconint:"∲",cwint:"∱",cylcty:"⌭",dagger:"†",Dagger:"‡",daleth:"ℸ",darr:"↓",Darr:"↡",dArr:"⇓",dash:"‐",Dashv:"⫤",dashv:"⊣",dbkarow:"⤏",dblac:"˝",Dcaron:"Ď",dcaron:"ď",Dcy:"Д",dcy:"д",ddagger:"‡",ddarr:"⇊",DD:"ⅅ",dd:"ⅆ",DDotrahd:"⤑",ddotseq:"⩷",deg:"°",Del:"∇",Delta:"Δ",delta:"δ",demptyv:"⦱",dfisht:"⥿",Dfr:"𝔇",dfr:"𝔡",dHar:"⥥",dharl:"⇃",dharr:"⇂",DiacriticalAcute:"´",DiacriticalDot:"˙",DiacriticalDoubleAcute:"˝",DiacriticalGrave:"`",DiacriticalTilde:"˜",diam:"⋄",diamond:"⋄",Diamond:"⋄",diamondsuit:"♦",diams:"♦",die:"¨",DifferentialD:"ⅆ",digamma:"ϝ",disin:"⋲",div:"÷",divide:"÷",divideontimes:"⋇",divonx:"⋇",DJcy:"Ђ",djcy:"ђ",dlcorn:"⌞",dlcrop:"⌍",dollar:"$",Dopf:"𝔻",dopf:"𝕕",Dot:"¨",dot:"˙",DotDot:"⃜",doteq:"≐",doteqdot:"≑",DotEqual:"≐",dotminus:"∸",dotplus:"∔",dotsquare:"⊡",doublebarwedge:"⌆",DoubleContourIntegral:"∯",DoubleDot:"¨",DoubleDownArrow:"⇓",DoubleLeftArrow:"⇐",DoubleLeftRightArrow:"⇔",DoubleLeftTee:"⫤",DoubleLongLeftArrow:"⟸",DoubleLongLeftRightArrow:"⟺",DoubleLongRightArrow:"⟹",DoubleRightArrow:"⇒",DoubleRightTee:"⊨",DoubleUpArrow:"⇑",DoubleUpDownArrow:"⇕",DoubleVerticalBar:"∥",DownArrowBar:"⤓",downarrow:"↓",DownArrow:"↓",Downarrow:"⇓",DownArrowUpArrow:"⇵",DownBreve:"̑",downdownarrows:"⇊",downharpoonleft:"⇃",downharpoonright:"⇂",DownLeftRightVector:"⥐",DownLeftTeeVector:"⥞",DownLeftVectorBar:"⥖",DownLeftVector:"↽",DownRightTeeVector:"⥟",DownRightVectorBar:"⥗",DownRightVector:"⇁",DownTeeArrow:"↧",DownTee:"⊤",drbkarow:"⤐",drcorn:"⌟",drcrop:"⌌",Dscr:"𝒟",dscr:"𝒹",DScy:"Ѕ",dscy:"ѕ",dsol:"⧶",Dstrok:"Đ",dstrok:"đ",dtdot:"⋱",dtri:"▿",dtrif:"▾",duarr:"⇵",duhar:"⥯",dwangle:"⦦",DZcy:"Џ",dzcy:"џ",dzigrarr:"⟿",Eacute:"É",eacute:"é",easter:"⩮",Ecaron:"Ě",ecaron:"ě",Ecirc:"Ê",ecirc:"ê",ecir:"≖",ecolon:"≕",Ecy:"Э",ecy:"э",eDDot:"⩷",Edot:"Ė",edot:"ė",eDot:"≑",ee:"ⅇ",efDot:"≒",Efr:"𝔈",efr:"𝔢",eg:"⪚",Egrave:"È",egrave:"è",egs:"⪖",egsdot:"⪘",el:"⪙",Element:"∈",elinters:"⏧",ell:"ℓ",els:"⪕",elsdot:"⪗",Emacr:"Ē",emacr:"ē",empty:"∅",emptyset:"∅",EmptySmallSquare:"◻",emptyv:"∅",EmptyVerySmallSquare:"▫",emsp13:" ",emsp14:" ",emsp:" ",ENG:"Ŋ",eng:"ŋ",ensp:" ",Eogon:"Ę",eogon:"ę",Eopf:"𝔼",eopf:"𝕖",epar:"⋕",eparsl:"⧣",eplus:"⩱",epsi:"ε",Epsilon:"Ε",epsilon:"ε",epsiv:"ϵ",eqcirc:"≖",eqcolon:"≕",eqsim:"≂",eqslantgtr:"⪖",eqslantless:"⪕",Equal:"⩵",equals:"=",EqualTilde:"≂",equest:"≟",Equilibrium:"⇌",equiv:"≡",equivDD:"⩸",eqvparsl:"⧥",erarr:"⥱",erDot:"≓",escr:"ℯ",Escr:"ℰ",esdot:"≐",Esim:"⩳",esim:"≂",Eta:"Η",eta:"η",ETH:"Ð",eth:"ð",Euml:"Ë",euml:"ë",euro:"€",excl:"!",exist:"∃",Exists:"∃",expectation:"ℰ",exponentiale:"ⅇ",ExponentialE:"ⅇ",fallingdotseq:"≒",Fcy:"Ф",fcy:"ф",female:"♀",ffilig:"ffi",fflig:"ff",ffllig:"ffl",Ffr:"𝔉",ffr:"𝔣",filig:"fi",FilledSmallSquare:"◼",FilledVerySmallSquare:"▪",fjlig:"fj",flat:"♭",fllig:"fl",fltns:"▱",fnof:"ƒ",Fopf:"𝔽",fopf:"𝕗",forall:"∀",ForAll:"∀",fork:"⋔",forkv:"⫙",Fouriertrf:"ℱ",fpartint:"⨍",frac12:"½",frac13:"⅓",frac14:"¼",frac15:"⅕",frac16:"⅙",frac18:"⅛",frac23:"⅔",frac25:"⅖",frac34:"¾",frac35:"⅗",frac38:"⅜",frac45:"⅘",frac56:"⅚",frac58:"⅝",frac78:"⅞",frasl:"⁄",frown:"⌢",fscr:"𝒻",Fscr:"ℱ",gacute:"ǵ",Gamma:"Γ",gamma:"γ",Gammad:"Ϝ",gammad:"ϝ",gap:"⪆",Gbreve:"Ğ",gbreve:"ğ",Gcedil:"Ģ",Gcirc:"Ĝ",gcirc:"ĝ",Gcy:"Г",gcy:"г",Gdot:"Ġ",gdot:"ġ",ge:"≥",gE:"≧",gEl:"⪌",gel:"⋛",geq:"≥",geqq:"≧",geqslant:"⩾",gescc:"⪩",ges:"⩾",gesdot:"⪀",gesdoto:"⪂",gesdotol:"⪄",gesl:"⋛︀",gesles:"⪔",Gfr:"𝔊",gfr:"𝔤",gg:"≫",Gg:"⋙",ggg:"⋙",gimel:"ℷ",GJcy:"Ѓ",gjcy:"ѓ",gla:"⪥",gl:"≷",glE:"⪒",glj:"⪤",gnap:"⪊",gnapprox:"⪊",gne:"⪈",gnE:"≩",gneq:"⪈",gneqq:"≩",gnsim:"⋧",Gopf:"𝔾",gopf:"𝕘",grave:"`",GreaterEqual:"≥",GreaterEqualLess:"⋛",GreaterFullEqual:"≧",GreaterGreater:"⪢",GreaterLess:"≷",GreaterSlantEqual:"⩾",GreaterTilde:"≳",Gscr:"𝒢",gscr:"ℊ",gsim:"≳",gsime:"⪎",gsiml:"⪐",gtcc:"⪧",gtcir:"⩺",gt:">",GT:">",Gt:"≫",gtdot:"⋗",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"⋗",gtreqless:"⋛",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"ℋ",HARDcy:"Ъ",hardcy:"ъ",harrcir:"⥈",harr:"↔",hArr:"⇔",harrw:"↭",Hat:"^",hbar:"ℏ",Hcirc:"Ĥ",hcirc:"ĥ",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",hfr:"𝔥",Hfr:"ℌ",HilbertSpace:"ℋ",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",hopf:"𝕙",Hopf:"ℍ",horbar:"―",HorizontalLine:"─",hscr:"𝒽",Hscr:"ℋ",hslash:"ℏ",Hstrok:"Ħ",hstrok:"ħ",HumpDownHump:"≎",HumpEqual:"≏",hybull:"⁃",hyphen:"‐",Iacute:"Í",iacute:"í",ic:"⁣",Icirc:"Î",icirc:"î",Icy:"И",icy:"и",Idot:"İ",IEcy:"Е",iecy:"е",iexcl:"¡",iff:"⇔",ifr:"𝔦",Ifr:"ℑ",Igrave:"Ì",igrave:"ì",ii:"ⅈ",iiiint:"⨌",iiint:"∭",iinfin:"⧜",iiota:"℩",IJlig:"IJ",ijlig:"ij",Imacr:"Ī",imacr:"ī",image:"ℑ",ImaginaryI:"ⅈ",imagline:"ℐ",imagpart:"ℑ",imath:"ı",Im:"ℑ",imof:"⊷",imped:"Ƶ",Implies:"⇒",incare:"℅",in:"∈",infin:"∞",infintie:"⧝",inodot:"ı",intcal:"⊺",int:"∫",Int:"∬",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"⋂",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"⁣",InvisibleTimes:"⁢",IOcy:"Ё",iocy:"ё",Iogon:"Į",iogon:"į",Iopf:"𝕀",iopf:"𝕚",Iota:"Ι",iota:"ι",iprod:"⨼",iquest:"¿",iscr:"𝒾",Iscr:"ℐ",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"⋴",isinsv:"⋳",isinv:"∈",it:"⁢",Itilde:"Ĩ",itilde:"ĩ",Iukcy:"І",iukcy:"і",Iuml:"Ï",iuml:"ï",Jcirc:"Ĵ",jcirc:"ĵ",Jcy:"Й",jcy:"й",Jfr:"𝔍",jfr:"𝔧",jmath:"ȷ",Jopf:"𝕁",jopf:"𝕛",Jscr:"𝒥",jscr:"𝒿",Jsercy:"Ј",jsercy:"ј",Jukcy:"Є",jukcy:"є",Kappa:"Κ",kappa:"κ",kappav:"ϰ",Kcedil:"Ķ",kcedil:"ķ",Kcy:"К",kcy:"к",Kfr:"𝔎",kfr:"𝔨",kgreen:"ĸ",KHcy:"Х",khcy:"х",KJcy:"Ќ",kjcy:"ќ",Kopf:"𝕂",kopf:"𝕜",Kscr:"𝒦",kscr:"𝓀",lAarr:"⇚",Lacute:"Ĺ",lacute:"ĺ",laemptyv:"⦴",lagran:"ℒ",Lambda:"Λ",lambda:"λ",lang:"⟨",Lang:"⟪",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"ℒ",laquo:"«",larrb:"⇤",larrbfs:"⤟",larr:"←",Larr:"↞",lArr:"⇐",larrfs:"⤝",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",latail:"⤙",lAtail:"⤛",lat:"⪫",late:"⪭",lates:"⪭︀",lbarr:"⤌",lBarr:"⤎",lbbrk:"❲",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"⦏",lbrkslu:"⦍",Lcaron:"Ľ",lcaron:"ľ",Lcedil:"Ļ",lcedil:"ļ",lceil:"⌈",lcub:"{",Lcy:"Л",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",le:"≤",lE:"≦",LeftAngleBracket:"⟨",LeftArrowBar:"⇤",leftarrow:"←",LeftArrow:"←",Leftarrow:"⇐",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVectorBar:"⥙",LeftDownVector:"⇃",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",leftrightarrow:"↔",LeftRightArrow:"↔",Leftrightarrow:"⇔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTeeArrow:"↤",LeftTee:"⊣",LeftTeeVector:"⥚",leftthreetimes:"⋋",LeftTriangleBar:"⧏",LeftTriangle:"⊲",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVectorBar:"⥘",LeftUpVector:"↿",LeftVectorBar:"⥒",LeftVector:"↼",lEg:"⪋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",lescc:"⪨",les:"⩽",lesdot:"⩿",lesdoto:"⪁",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"⋖",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",Lfr:"𝔏",lfr:"𝔩",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"▄",LJcy:"Љ",ljcy:"љ",llarr:"⇇",ll:"≪",Ll:"⋘",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"◺",Lmidot:"Ŀ",lmidot:"ŀ",lmoustache:"⎰",lmoust:"⎰",lnap:"⪉",lnapprox:"⪉",lne:"⪇",lnE:"≨",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",longleftarrow:"⟵",LongLeftArrow:"⟵",Longleftarrow:"⟸",longleftrightarrow:"⟷",LongLeftRightArrow:"⟷",Longleftrightarrow:"⟺",longmapsto:"⟼",longrightarrow:"⟶",LongRightArrow:"⟶",Longrightarrow:"⟹",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",Lopf:"𝕃",lopf:"𝕝",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"◊",lozenge:"◊",lozf:"⧫",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",lscr:"𝓁",Lscr:"ℒ",lsh:"↰",Lsh:"↰",lsim:"≲",lsime:"⪍",lsimg:"⪏",lsqb:"[",lsquo:"‘",lsquor:"‚",Lstrok:"Ł",lstrok:"ł",ltcc:"⪦",ltcir:"⩹",lt:"<",LT:"<",Lt:"≪",ltdot:"⋖",lthree:"⋋",ltimes:"⋉",ltlarr:"⥶",ltquest:"⩻",ltri:"◃",ltrie:"⊴",ltrif:"◂",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",Map:"⤅",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"▮",mcomma:"⨩",Mcy:"М",mcy:"м",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:" ",Mellintrf:"ℳ",Mfr:"𝔐",mfr:"𝔪",mho:"℧",micro:"µ",midast:"*",midcir:"⫰",mid:"∣",middot:"·",minusb:"⊟",minus:"−",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"⫛",mldr:"…",mnplus:"∓",models:"⊧",Mopf:"𝕄",mopf:"𝕞",mp:"∓",mscr:"𝓂",Mscr:"ℳ",mstpos:"∾",Mu:"Μ",mu:"μ",multimap:"⊸",mumap:"⊸",nabla:"∇",Nacute:"Ń",nacute:"ń",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natural:"♮",naturals:"ℕ",natur:"♮",nbsp:" ",nbump:"≎̸",nbumpe:"≏̸",ncap:"⩃",Ncaron:"Ň",ncaron:"ň",Ncedil:"Ņ",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"⩂",Ncy:"Н",ncy:"н",ndash:"–",nearhk:"⤤",nearr:"↗",neArr:"⇗",nearrow:"↗",ne:"≠",nedot:"≐̸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",nexist:"∄",nexists:"∄",Nfr:"𝔑",nfr:"𝔫",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",nGt:"≫⃒",ngt:"≯",ngtr:"≯",nGtv:"≫̸",nharr:"↮",nhArr:"⇎",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",NJcy:"Њ",njcy:"њ",nlarr:"↚",nlArr:"⇍",nldr:"‥",nlE:"≦̸",nle:"≰",nleftarrow:"↚",nLeftarrow:"⇍",nleftrightarrow:"↮",nLeftrightarrow:"⇎",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nLt:"≪⃒",nlt:"≮",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"⁠",NonBreakingSpace:" ",nopf:"𝕟",Nopf:"ℕ",Not:"⫬",not:"¬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"≏̸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"⋷",notinvc:"⋶",NotLeftTriangleBar:"⧏̸",NotLeftTriangle:"⋪",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"⋠",NotReverseElement:"∌",NotRightTriangleBar:"⧐̸",NotRightTriangle:"⋫",NotRightTriangleEqual:"⋭",NotSquareSubset:"⊏̸",NotSquareSubsetEqual:"⋢",NotSquareSuperset:"⊐̸",NotSquareSupersetEqual:"⋣",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"⊁",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"⋡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"≁",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",nparallel:"∦",npar:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"⋠",nprec:"⊀",npreceq:"⪯̸",npre:"⪯̸",nrarrc:"⤳̸",nrarr:"↛",nrArr:"⇏",nrarrw:"↝̸",nrightarrow:"↛",nRightarrow:"⇏",nrtri:"⋫",nrtrie:"⋭",nsc:"⊁",nsccue:"⋡",nsce:"⪰̸",Nscr:"𝒩",nscr:"𝓃",nshortmid:"∤",nshortparallel:"∦",nsim:"≁",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"⋢",nsqsupe:"⋣",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"⊁",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",Ntilde:"Ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"⋫",ntrianglerighteq:"⋭",Nu:"Ν",nu:"ν",num:"#",numero:"№",numsp:" ",nvap:"≍⃒",nvdash:"⊬",nvDash:"⊭",nVdash:"⊮",nVDash:"⊯",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"⧞",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwarr:"↖",nwArr:"⇖",nwarrow:"↖",nwnear:"⤧",Oacute:"Ó",oacute:"ó",oast:"⊛",Ocirc:"Ô",ocirc:"ô",ocir:"⊚",Ocy:"О",ocy:"о",odash:"⊝",Odblac:"Ő",odblac:"ő",odiv:"⨸",odot:"⊙",odsold:"⦼",OElig:"Œ",oelig:"œ",ofcir:"⦿",Ofr:"𝔒",ofr:"𝔬",ogon:"˛",Ograve:"Ò",ograve:"ò",ogt:"⧁",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"⧀",Omacr:"Ō",omacr:"ō",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",omid:"⦶",ominus:"⊖",Oopf:"𝕆",oopf:"𝕠",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",orarr:"↻",Or:"⩔",or:"∨",ord:"⩝",order:"ℴ",orderof:"ℴ",ordf:"ª",ordm:"º",origof:"⊶",oror:"⩖",orslope:"⩗",orv:"⩛",oS:"Ⓢ",Oscr:"𝒪",oscr:"ℴ",Oslash:"Ø",oslash:"ø",osol:"⊘",Otilde:"Õ",otilde:"õ",otimesas:"⨶",Otimes:"⨷",otimes:"⊗",Ouml:"Ö",ouml:"ö",ovbar:"⌽",OverBar:"‾",OverBrace:"⏞",OverBracket:"⎴",OverParenthesis:"⏜",para:"¶",parallel:"∥",par:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",Pcy:"П",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",Pfr:"𝔓",pfr:"𝔭",Phi:"Φ",phi:"φ",phiv:"ϕ",phmmat:"ℳ",phone:"☎",Pi:"Π",pi:"π",pitchfork:"⋔",piv:"ϖ",planck:"ℏ",planckh:"ℎ",plankv:"ℏ",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plus:"+",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",popf:"𝕡",Popf:"ℙ",pound:"£",prap:"⪷",Pr:"⪻",pr:"≺",prcue:"≼",precapprox:"⪷",prec:"≺",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",pre:"⪯",prE:"⪳",precsim:"≾",prime:"′",Prime:"″",primes:"ℙ",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"∏",Product:"∏",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"∝",Proportional:"∝",Proportion:"∷",propto:"∝",prsim:"≾",prurel:"⊰",Pscr:"𝒫",pscr:"𝓅",Psi:"Ψ",psi:"ψ",puncsp:" ",Qfr:"𝔔",qfr:"𝔮",qint:"⨌",qopf:"𝕢",Qopf:"ℚ",qprime:"⁗",Qscr:"𝒬",qscr:"𝓆",quaternions:"ℍ",quatint:"⨖",quest:"?",questeq:"≟",quot:'"',QUOT:'"',rAarr:"⇛",race:"∽̱",Racute:"Ŕ",racute:"ŕ",radic:"√",raemptyv:"⦳",rang:"⟩",Rang:"⟫",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarr:"→",Rarr:"↠",rArr:"⇒",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",Rarrtl:"⤖",rarrtl:"↣",rarrw:"↝",ratail:"⤚",rAtail:"⤜",ratio:"∶",rationals:"ℚ",rbarr:"⤍",rBarr:"⤏",RBarr:"⤐",rbbrk:"❳",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"⦐",Rcaron:"Ř",rcaron:"ř",Rcedil:"Ŗ",rcedil:"ŗ",rceil:"⌉",rcub:"}",Rcy:"Р",rcy:"р",rdca:"⤷",rdldhar:"⥩",rdquo:"”",rdquor:"”",rdsh:"↳",real:"ℜ",realine:"ℛ",realpart:"ℜ",reals:"ℝ",Re:"ℜ",rect:"▭",reg:"®",REG:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",rfr:"𝔯",Rfr:"ℜ",rHar:"⥤",rhard:"⇁",rharu:"⇀",rharul:"⥬",Rho:"Ρ",rho:"ρ",rhov:"ϱ",RightAngleBracket:"⟩",RightArrowBar:"⇥",rightarrow:"→",RightArrow:"→",Rightarrow:"⇒",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"⥝",RightDownVectorBar:"⥕",RightDownVector:"⇂",RightFloor:"⌋",rightharpoondown:"⇁",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"↝",RightTeeArrow:"↦",RightTee:"⊢",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangleBar:"⧐",RightTriangle:"⊳",RightTriangleEqual:"⊵",RightUpDownVector:"⥏",RightUpTeeVector:"⥜",RightUpVectorBar:"⥔",RightUpVector:"↾",RightVectorBar:"⥓",RightVector:"⇀",ring:"˚",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"‏",rmoustache:"⎱",rmoust:"⎱",rnmid:"⫮",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",ropf:"𝕣",Ropf:"ℝ",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",rscr:"𝓇",Rscr:"ℛ",rsh:"↱",Rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"▹",rtrie:"⊵",rtrif:"▸",rtriltri:"⧎",RuleDelayed:"⧴",ruluhar:"⥨",rx:"℞",Sacute:"Ś",sacute:"ś",sbquo:"‚",scap:"⪸",Scaron:"Š",scaron:"š",Sc:"⪼",sc:"≻",sccue:"≽",sce:"⪰",scE:"⪴",Scedil:"Ş",scedil:"ş",Scirc:"Ŝ",scirc:"ŝ",scnap:"⪺",scnE:"⪶",scnsim:"⋩",scpolint:"⨓",scsim:"≿",Scy:"С",scy:"с",sdotb:"⊡",sdot:"⋅",sdote:"⩦",searhk:"⤥",searr:"↘",seArr:"⇘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",Sfr:"𝔖",sfr:"𝔰",sfrown:"⌢",sharp:"♯",SHCHcy:"Щ",shchcy:"щ",SHcy:"Ш",shcy:"ш",ShortDownArrow:"↓",ShortLeftArrow:"←",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"ς",sigmav:"ς",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"⪝",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"←",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",SOFTcy:"Ь",softcy:"ь",solbar:"⌿",solb:"⧄",sol:"/",Sopf:"𝕊",sopf:"𝕤",spades:"♠",spadesuit:"♠",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"⊏",sqsube:"⊑",sqsubset:"⊏",sqsubseteq:"⊑",sqsup:"⊐",sqsupe:"⊒",sqsupset:"⊐",sqsupseteq:"⊒",square:"□",Square:"□",SquareIntersection:"⊓",SquareSubset:"⊏",SquareSubsetEqual:"⊑",SquareSuperset:"⊐",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"▪",squ:"□",squf:"▪",srarr:"→",Sscr:"𝒮",sscr:"𝓈",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",Star:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"ϕ",strns:"¯",sub:"⊂",Sub:"⋐",subdot:"⪽",subE:"⫅",sube:"⊆",subedot:"⫃",submult:"⫁",subnE:"⫋",subne:"⊊",subplus:"⪿",subrarr:"⥹",subset:"⊂",Subset:"⋐",subseteq:"⊆",subseteqq:"⫅",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"⫋",subsim:"⫇",subsub:"⫕",subsup:"⫓",succapprox:"⪸",succ:"≻",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"⋩",succsim:"≿",SuchThat:"∋",sum:"∑",Sum:"∑",sung:"♪",sup1:"¹",sup2:"²",sup3:"³",sup:"⊃",Sup:"⋑",supdot:"⪾",supdsub:"⫘",supE:"⫆",supe:"⊇",supedot:"⫄",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"⫗",suplarr:"⥻",supmult:"⫂",supnE:"⫌",supne:"⊋",supplus:"⫀",supset:"⊃",Supset:"⋑",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"⫔",supsup:"⫖",swarhk:"⤦",swarr:"↙",swArr:"⇙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:"\t",target:"⌖",Tau:"Τ",tau:"τ",tbrk:"⎴",Tcaron:"Ť",tcaron:"ť",Tcedil:"Ţ",tcedil:"ţ",Tcy:"Т",tcy:"т",tdot:"⃛",telrec:"⌕",Tfr:"𝔗",tfr:"𝔱",there4:"∴",therefore:"∴",Therefore:"∴",Theta:"Θ",theta:"θ",thetasym:"ϑ",thetav:"ϑ",thickapprox:"≈",thicksim:"∼",ThickSpace:"  ",ThinSpace:" ",thinsp:" ",thkap:"≈",thksim:"∼",THORN:"Þ",thorn:"þ",tilde:"˜",Tilde:"∼",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",timesbar:"⨱",timesb:"⊠",times:"×",timesd:"⨰",tint:"∭",toea:"⤨",topbot:"⌶",topcir:"⫱",top:"⊤",Topf:"𝕋",topf:"𝕥",topfork:"⫚",tosa:"⤩",tprime:"‴",trade:"™",TRADE:"™",triangle:"▵",triangledown:"▿",triangleleft:"◃",trianglelefteq:"⊴",triangleq:"≜",triangleright:"▹",trianglerighteq:"⊵",tridot:"◬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"⧍",tritime:"⨻",trpezium:"⏢",Tscr:"𝒯",tscr:"𝓉",TScy:"Ц",tscy:"ц",TSHcy:"Ћ",tshcy:"ћ",Tstrok:"Ŧ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",Uacute:"Ú",uacute:"ú",uarr:"↑",Uarr:"↟",uArr:"⇑",Uarrocir:"⥉",Ubrcy:"Ў",ubrcy:"ў",Ubreve:"Ŭ",ubreve:"ŭ",Ucirc:"Û",ucirc:"û",Ucy:"У",ucy:"у",udarr:"⇅",Udblac:"Ű",udblac:"ű",udhar:"⥮",ufisht:"⥾",Ufr:"𝔘",ufr:"𝔲",Ugrave:"Ù",ugrave:"ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"▀",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"⌏",ultri:"◸",Umacr:"Ū",umacr:"ū",uml:"¨",UnderBar:"_",UnderBrace:"⏟",UnderBracket:"⎵",UnderParenthesis:"⏝",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",uogon:"ų",Uopf:"𝕌",uopf:"𝕦",UpArrowBar:"⤒",uparrow:"↑",UpArrow:"↑",Uparrow:"⇑",UpArrowDownArrow:"⇅",updownarrow:"↕",UpDownArrow:"↕",Updownarrow:"⇕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",upsi:"υ",Upsi:"ϒ",upsih:"ϒ",Upsilon:"Υ",upsilon:"υ",UpTeeArrow:"↥",UpTee:"⊥",upuparrows:"⇈",urcorn:"⌝",urcorner:"⌝",urcrop:"⌎",Uring:"Ů",uring:"ů",urtri:"◹",Uscr:"𝒰",uscr:"𝓊",utdot:"⋰",Utilde:"Ũ",utilde:"ũ",utri:"▵",utrif:"▴",uuarr:"⇈",Uuml:"Ü",uuml:"ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"ϕ",varpi:"ϖ",varpropto:"∝",varr:"↕",vArr:"⇕",varrho:"ϱ",varsigma:"ς",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"ϑ",vartriangleleft:"⊲",vartriangleright:"⊳",vBar:"⫨",Vbar:"⫫",vBarv:"⫩",Vcy:"В",vcy:"в",vdash:"⊢",vDash:"⊨",Vdash:"⊩",VDash:"⊫",Vdashl:"⫦",veebar:"⊻",vee:"∨",Vee:"⋁",veeeq:"≚",vellip:"⋮",verbar:"|",Verbar:"‖",vert:"|",Vert:"‖",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"❘",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"𝔙",vfr:"𝔳",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",Vopf:"𝕍",vopf:"𝕧",vprop:"∝",vrtri:"⊳",Vscr:"𝒱",vscr:"𝓋",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",Vvdash:"⊪",vzigzag:"⦚",Wcirc:"Ŵ",wcirc:"ŵ",wedbar:"⩟",wedge:"∧",Wedge:"⋀",wedgeq:"≙",weierp:"℘",Wfr:"𝔚",wfr:"𝔴",Wopf:"𝕎",wopf:"𝕨",wp:"℘",wr:"≀",wreath:"≀",Wscr:"𝒲",wscr:"𝓌",xcap:"⋂",xcirc:"◯",xcup:"⋃",xdtri:"▽",Xfr:"𝔛",xfr:"𝔵",xharr:"⟷",xhArr:"⟺",Xi:"Ξ",xi:"ξ",xlarr:"⟵",xlArr:"⟸",xmap:"⟼",xnis:"⋻",xodot:"⨀",Xopf:"𝕏",xopf:"𝕩",xoplus:"⨁",xotime:"⨂",xrarr:"⟶",xrArr:"⟹",Xscr:"𝒳",xscr:"𝓍",xsqcup:"⨆",xuplus:"⨄",xutri:"△",xvee:"⋁",xwedge:"⋀",Yacute:"Ý",yacute:"ý",YAcy:"Я",yacy:"я",Ycirc:"Ŷ",ycirc:"ŷ",Ycy:"Ы",ycy:"ы",yen:"¥",Yfr:"𝔜",yfr:"𝔶",YIcy:"Ї",yicy:"ї",Yopf:"𝕐",yopf:"𝕪",Yscr:"𝒴",yscr:"𝓎",YUcy:"Ю",yucy:"ю",yuml:"ÿ",Yuml:"Ÿ",Zacute:"Ź",zacute:"ź",Zcaron:"Ž",zcaron:"ž",Zcy:"З",zcy:"з",Zdot:"Ż",zdot:"ż",zeetrf:"ℨ",ZeroWidthSpace:"​",Zeta:"Ζ",zeta:"ζ",zfr:"𝔷",Zfr:"ℨ",ZHcy:"Ж",zhcy:"ж",zigrarr:"⇝",zopf:"𝕫",Zopf:"ℤ",Zscr:"𝒵",zscr:"𝓏",zwj:"‍",zwnj:"‌"},wH=/[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4E\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDF55-\uDF59]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDC3B\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDE97-\uDE9A]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/,kH={},SH={};function _H(e,t,n){var o,r,i,a,l,s="";for("string"!=typeof t&&(n=t,t=_H.defaultChars),void 0===n&&(n=!0),l=function(e){var t,n,o=SH[e];if(o)return o;for(o=SH[e]=[],t=0;t<128;t++)n=String.fromCharCode(t),/^[0-9a-z]$/i.test(n)?o.push(n):o.push("%"+("0"+t.toString(16).toUpperCase()).slice(-2));for(t=0;t=55296&&i<=57343){if(i>=55296&&i<=56319&&o+1=56320&&a<=57343){s+=encodeURIComponent(e[o]+e[o+1]),o++;continue}s+="%EF%BF%BD"}else s+=encodeURIComponent(e[o]);return s}_H.defaultChars=";/?:@&=+$,-_.!~*'()#",_H.componentChars="-_.!~*'()";var PH=_H,TH={};function AH(e,t){var n;return"string"!=typeof t&&(t=AH.defaultChars),n=function(e){var t,n,o=TH[e];if(o)return o;for(o=TH[e]=[],t=0;t<128;t++)n=String.fromCharCode(t),o.push(n);for(t=0;t=55296&&s<=57343?"���":String.fromCharCode(s),t+=6):240==(248&r)&&t+91114111?c+="����":(s-=65536,c+=String.fromCharCode(55296+(s>>10),56320+(1023&s))),t+=9):c+="�";return c}))}AH.defaultChars=";/?:@&=+$,#",AH.componentChars="";var zH=AH;function RH(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var EH=/^([a-z0-9.+-]+:)/i,OH=/:[0-9]*$/,MH=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,FH=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),IH=["'"].concat(FH),LH=["%","/","?",";","#"].concat(IH),BH=["/","?","#"],DH=/^[+a-z0-9A-Z_-]{0,63}$/,$H=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,NH={javascript:!0,"javascript:":!0},jH={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0};RH.prototype.parse=function(e,t){var n,o,r,i,a,l=e;if(l=l.trim(),!t&&1===e.split("#").length){var s=MH.exec(l);if(s)return this.pathname=s[1],s[2]&&(this.search=s[2]),this}var c=EH.exec(l);if(c&&(r=(c=c[0]).toLowerCase(),this.protocol=c,l=l.substr(c.length)),(t||c||l.match(/^\/\/[^@\/]+@[^@\/]+/))&&(!(a="//"===l.substr(0,2))||c&&NH[c]||(l=l.substr(2),this.slashes=!0)),!NH[c]&&(a||c&&!jH[c])){var u,d,p=-1;for(n=0;n127?g+="x":g+=m[b];if(!g.match(DH)){var x=v.slice(0,n),C=v.slice(n+1),w=m.match($H);w&&(x.push(w[1]),C.unshift(w[2])),C.length&&(l=C.join(".")+l),this.hostname=x.join(".");break}}}}this.hostname.length>255&&(this.hostname=""),f&&(this.hostname=this.hostname.substr(1,this.hostname.length-2))}var k=l.indexOf("#");-1!==k&&(this.hash=l.substr(k),l=l.slice(0,k));var S=l.indexOf("?");return-1!==S&&(this.search=l.substr(S),l=l.slice(0,S)),l&&(this.pathname=l),jH[r]&&this.hostname&&!this.pathname&&(this.pathname=""),this},RH.prototype.parseHost=function(e){var t=OH.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)};var HH=function(e,t){if(e&&e instanceof RH)return e;var n=new RH;return n.parse(e,t),n};kH.encode=PH,kH.decode=zH,kH.format=function(e){var t="";return t+=e.protocol||"",t+=e.slashes?"//":"",t+=e.auth?e.auth+"@":"",e.hostname&&-1!==e.hostname.indexOf(":")?t+="["+e.hostname+"]":t+=e.hostname||"",t+=e.port?":"+e.port:"",t+=e.pathname||"",t+=e.search||"",t+=e.hash||""},kH.parse=HH;var WH,UH,VH,qH,KH,GH,XH,YH,QH,ZH={};function JH(){return UH?WH:(UH=1,WH=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/)}function eW(){return qH?VH:(qH=1,VH=/[\0-\x1F\x7F-\x9F]/)}function tW(){return YH?XH:(YH=1,XH=/[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/)}function nW(){return QH||(QH=1,ZH.Any=JH(),ZH.Cc=eW(),ZH.Cf=GH?KH:(GH=1,KH=/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/),ZH.P=wH,ZH.Z=tW()),ZH}!function(e){var t=Object.prototype.hasOwnProperty;function n(e,n){return t.call(e,n)}function o(e){return!(e>=55296&&e<=57343||e>=64976&&e<=65007||!(65535&~e&&65534!=(65535&e))||e>=0&&e<=8||11===e||e>=14&&e<=31||e>=127&&e<=159||e>1114111)}function r(e){if(e>65535){var t=55296+((e-=65536)>>10),n=56320+(1023&e);return String.fromCharCode(t,n)}return String.fromCharCode(e)}var i=/\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g,a=new RegExp(i.source+"|"+/&([a-z#][a-z0-9]{1,31});/gi.source,"gi"),l=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i,s=CH,c=/[&<>"]/,u=/[&<>"]/g,d={"&":"&","<":"<",">":">",'"':"""};function p(e){return d[e]}var h=/[.?*+^$[\]\\(){}|-]/g,f=wH;e.lib={},e.lib.mdurl=kH,e.lib.ucmicro=nW(),e.assign=function(e){return Array.prototype.slice.call(arguments,1).forEach((function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach((function(n){e[n]=t[n]}))}})),e},e.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},e.has=n,e.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(i,"$1")},e.unescapeAll=function(e){return e.indexOf("\\")<0&&e.indexOf("&")<0?e:e.replace(a,(function(e,t,i){return t||function(e,t){var i;return n(s,t)?s[t]:35===t.charCodeAt(0)&&l.test(t)&&o(i="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?r(i):e}(e,i)}))},e.isValidEntityCode=o,e.fromCodePoint=r,e.escapeHtml=function(e){return c.test(e)?e.replace(u,p):e},e.arrayReplaceAt=function(e,t,n){return[].concat(e.slice(0,t),n,e.slice(t+1))},e.isSpace=function(e){switch(e){case 9:case 32:return!0}return!1},e.isWhiteSpace=function(e){if(e>=8192&&e<=8202)return!0;switch(e){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1},e.isMdAsciiPunct=function(e){switch(e){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}},e.isPunctChar=function(e){return f.test(e)},e.escapeRE=function(e){return e.replace(h,"\\$&")},e.normalizeReference=function(e){return e=e.trim().replace(/\s+/g," "),"Ṿ"==="ẞ".toLowerCase()&&(e=e.replace(/ẞ/g,"ß")),e.toLowerCase().toUpperCase()}}(xH);var oW={},rW=xH.unescapeAll,iW=xH.unescapeAll;oW.parseLinkLabel=function(e,t,n){var o,r,i,a,l=-1,s=e.posMax,c=e.pos;for(e.pos=t+1,o=1;e.pos32)return a;if(41===o){if(0===r)break;r--}i++}return t===i||0!==r||(a.str=rW(e.slice(t,i)),a.pos=i,a.ok=!0),a},oW.parseLinkTitle=function(e,t,n){var o,r,i=0,a=t,l={ok:!1,pos:0,lines:0,str:""};if(a>=n)return l;if(34!==(r=e.charCodeAt(a))&&39!==r&&40!==r)return l;for(a++,40===r&&(r=41);a"+sW(i.content)+""},cW.code_block=function(e,t,n,o,r){var i=e[t];return""+sW(e[t].content)+"\n"},cW.fence=function(e,t,n,o,r){var i,a,l,s,c,u=e[t],d=u.info?lW(u.info).trim():"",p="",h="";return d&&(p=(l=d.split(/(\s+)/g))[0],h=l.slice(2).join("")),0===(i=n.highlight&&n.highlight(u.content,p,h)||sW(u.content)).indexOf(""+i+"\n"):"
"+i+"
\n"},cW.image=function(e,t,n,o,r){var i=e[t];return i.attrs[i.attrIndex("alt")][1]=r.renderInlineAsText(i.children,n,o),r.renderToken(e,t,n)},cW.hardbreak=function(e,t,n){return n.xhtmlOut?"
\n":"
\n"},cW.softbreak=function(e,t,n){return n.breaks?n.xhtmlOut?"
\n":"
\n":"\n"},cW.text=function(e,t){return sW(e[t].content)},cW.html_block=function(e,t){return e[t].content},cW.html_inline=function(e,t){return e[t].content},uW.prototype.renderAttrs=function(e){var t,n,o;if(!e.attrs)return"";for(o="",t=0,n=e.attrs.length;t\n":">")},uW.prototype.renderInline=function(e,t,n){for(var o,r="",i=this.rules,a=0,l=e.length;a/i.test(e)}var bW=/\+-|\.\.|\?\?\?\?|!!!!|,,|--/,yW=/\((c|tm|r)\)/i,xW=/\((c|tm|r)\)/gi,CW={c:"©",r:"®",tm:"™"};function wW(e,t){return CW[t.toLowerCase()]}function kW(e){var t,n,o=0;for(t=e.length-1;t>=0;t--)"text"!==(n=e[t]).type||o||(n.content=n.content.replace(xW,wW)),"link_open"===n.type&&"auto"===n.info&&o--,"link_close"===n.type&&"auto"===n.info&&o++}function SW(e){var t,n,o=0;for(t=e.length-1;t>=0;t--)"text"!==(n=e[t]).type||o||bW.test(n.content)&&(n.content=n.content.replace(/\+-/g,"±").replace(/\.{2,}/g,"…").replace(/([?!])…/g,"$1..").replace(/([?!]){4,}/g,"$1$1$1").replace(/,{2,}/g,",").replace(/(^|[^-])---(?=[^-]|$)/gm,"$1—").replace(/(^|\s)--(?=\s|$)/gm,"$1–").replace(/(^|[^-\s])--(?=[^-\s]|$)/gm,"$1–")),"link_open"===n.type&&"auto"===n.info&&o--,"link_close"===n.type&&"auto"===n.info&&o++}var _W=xH.isWhiteSpace,PW=xH.isPunctChar,TW=xH.isMdAsciiPunct,AW=/['"]/,zW=/['"]/g;function RW(e,t,n){return e.slice(0,t)+n+e.slice(t+1)}function EW(e,t){var n,o,r,i,a,l,s,c,u,d,p,h,f,v,m,g,b,y,x,C,w;for(x=[],n=0;n=0&&!(x[b].level<=s);b--);if(x.length=b+1,"text"===o.type){a=0,l=(r=o.content).length;e:for(;a=0)u=r.charCodeAt(i.index-1);else for(b=n-1;b>=0&&"softbreak"!==e[b].type&&"hardbreak"!==e[b].type;b--)if(e[b].content){u=e[b].content.charCodeAt(e[b].content.length-1);break}if(d=32,a=48&&u<=57&&(g=m=!1),m&&g&&(m=p,g=h),m||g){if(g)for(b=x.length-1;b>=0&&(c=x[b],!(x[b].level=0&&(n=this.attrs[t][1]),n},OW.prototype.attrJoin=function(e,t){var n=this.attrIndex(e);n<0?this.attrPush([e,t]):this.attrs[n][1]=this.attrs[n][1]+" "+t};var MW=OW,FW=MW;function IW(e,t,n){this.src=e,this.env=n,this.tokens=[],this.inlineMode=!1,this.md=t}IW.prototype.Token=FW;var LW=IW,BW=hW,DW=[["normalize",function(e){var t;t=(t=e.src.replace(fW,"\n")).replace(vW,"�"),e.src=t}],["block",function(e){var t;e.inlineMode?((t=new e.Token("inline","",0)).content=e.src,t.map=[0,1],t.children=[],e.tokens.push(t)):e.md.block.parse(e.src,e.md,e.env,e.tokens)}],["inline",function(e){var t,n,o,r=e.tokens;for(n=0,o=r.length;n=0;t--)if("link_close"!==(a=r[t]).type){if("html_inline"===a.type&&(b=a.content,/^\s]/i.test(b)&&h>0&&h--,gW(a.content)&&h++),!(h>0)&&"text"===a.type&&e.md.linkify.test(a.content)){for(c=a.content,g=e.md.linkify.match(c),l=[],p=a.level,d=0,g.length>0&&0===g[0].index&&t>0&&"text_special"===r[t-1].type&&(g=g.slice(1)),s=0;sd&&((i=new e.Token("text","",0)).content=c.slice(d,u),i.level=p,l.push(i)),(i=new e.Token("link_open","a",1)).attrs=[["href",v]],i.level=p++,i.markup="linkify",i.info="auto",l.push(i),(i=new e.Token("text","",0)).content=m,i.level=p,l.push(i),(i=new e.Token("link_close","a",-1)).level=--p,i.markup="linkify",i.info="auto",l.push(i),d=g[s].lastIndex);d=0;t--)"inline"===e.tokens[t].type&&(yW.test(e.tokens[t].content)&&kW(e.tokens[t].children),bW.test(e.tokens[t].content)&&SW(e.tokens[t].children))}],["smartquotes",function(e){var t;if(e.md.options.typographer)for(t=e.tokens.length-1;t>=0;t--)"inline"===e.tokens[t].type&&AW.test(e.tokens[t].content)&&EW(e.tokens[t].children,e)}],["text_join",function(e){var t,n,o,r,i,a,l=e.tokens;for(t=0,n=l.length;t=i)return-1;if((n=e.src.charCodeAt(r++))<48||n>57)return-1;for(;;){if(r>=i)return-1;if(!((n=e.src.charCodeAt(r++))>=48&&n<=57)){if(41===n||46===n)break;return-1}if(r-o>=10)return-1}return r`\\x00-\\x20]+|'[^']*'|\"[^\"]*\"))?)*\\s*\\/?>",JW="<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>",eU=new RegExp("^(?:"+ZW+"|"+JW+"|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|<[?][\\s\\S]*?[?]>|]*>|)"),tU=new RegExp("^(?:"+ZW+"|"+JW+")");QW.HTML_TAG_RE=eU,QW.HTML_OPEN_CLOSE_TAG_RE=tU;var nU=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","section","source","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],oU=QW.HTML_OPEN_CLOSE_TAG_RE,rU=[[/^<(script|pre|style|textarea)(?=(\s|>|$))/i,/<\/(script|pre|style|textarea)>/i,!0],[/^/,!0],[/^<\?/,/\?>/,!0],[/^/,!0],[/^/,!0],[new RegExp("^|$))","i"),/^$/,!0],[new RegExp(oU.source+"\\s*$"),/^$/,!1]],iU=xH.isSpace,aU=MW,lU=xH.isSpace;function sU(e,t,n,o){var r,i,a,l,s,c,u,d;for(this.src=e,this.md=t,this.env=n,this.tokens=o,this.bMarks=[],this.eMarks=[],this.tShift=[],this.sCount=[],this.bsCount=[],this.blkIndent=0,this.line=0,this.lineMax=0,this.tight=!1,this.ddIndent=-1,this.listIndent=-1,this.parentType="root",this.level=0,this.result="",d=!1,a=l=c=u=0,s=(i=this.src).length;l0&&this.level++,this.tokens.push(o),o},sU.prototype.isEmpty=function(e){return this.bMarks[e]+this.tShift[e]>=this.eMarks[e]},sU.prototype.skipEmptyLines=function(e){for(var t=this.lineMax;et;)if(!lU(this.src.charCodeAt(--e)))return e+1;return e},sU.prototype.skipChars=function(e,t){for(var n=this.src.length;en;)if(t!==this.src.charCodeAt(--e))return e+1;return e},sU.prototype.getLines=function(e,t,n,o){var r,i,a,l,s,c,u,d=e;if(e>=t)return"";for(c=new Array(t-e),r=0;dn?new Array(i-n+1).join(" ")+this.src.slice(l,s):this.src.slice(l,s)}return c.join("")},sU.prototype.Token=aU;var cU=sU,uU=hW,dU=[["table",function(e,t,n,o){var r,i,a,l,s,c,u,d,p,h,f,v,m,g,b,y,x,C;if(t+2>n)return!1;if(c=t+1,e.sCount[c]=4)return!1;if((a=e.bMarks[c]+e.tShift[c])>=e.eMarks[c])return!1;if(124!==(x=e.src.charCodeAt(a++))&&45!==x&&58!==x)return!1;if(a>=e.eMarks[c])return!1;if(124!==(C=e.src.charCodeAt(a++))&&45!==C&&58!==C&&!jW(C))return!1;if(45===x&&jW(C))return!1;for(;a=4)return!1;if((u=WW(i)).length&&""===u[0]&&u.shift(),u.length&&""===u[u.length-1]&&u.pop(),0===(d=u.length)||d!==h.length)return!1;if(o)return!0;for(g=e.parentType,e.parentType="table",y=e.md.block.ruler.getRules("blockquote"),(p=e.push("table_open","table",1)).map=v=[t,0],(p=e.push("thead_open","thead",1)).map=[t,t+1],(p=e.push("tr_open","tr",1)).map=[t,t+1],l=0;l=4)break;for((u=WW(i)).length&&""===u[0]&&u.shift(),u.length&&""===u[u.length-1]&&u.pop(),c===t+2&&((p=e.push("tbody_open","tbody",1)).map=m=[t+2,0]),(p=e.push("tr_open","tr",1)).map=[c,c+1],l=0;l=4))break;r=++o}return e.line=r,(i=e.push("code_block","code",0)).content=e.getLines(t,r,4+e.blkIndent,!1)+"\n",i.map=[t,e.line],!0}],["fence",function(e,t,n,o){var r,i,a,l,s,c,u,d=!1,p=e.bMarks[t]+e.tShift[t],h=e.eMarks[t];if(e.sCount[t]-e.blkIndent>=4)return!1;if(p+3>h)return!1;if(126!==(r=e.src.charCodeAt(p))&&96!==r)return!1;if(s=p,(i=(p=e.skipChars(p,r))-s)<3)return!1;if(u=e.src.slice(s,p),a=e.src.slice(p,h),96===r&&a.indexOf(String.fromCharCode(r))>=0)return!1;if(o)return!0;for(l=t;!(++l>=n||(p=s=e.bMarks[l]+e.tShift[l])<(h=e.eMarks[l])&&e.sCount[l]=4||(p=e.skipChars(p,r))-s=4)return!1;if(62!==e.src.charCodeAt(_))return!1;if(o)return!0;for(h=[],f=[],g=[],b=[],C=e.md.block.ruler.getRules("blockquote"),m=e.parentType,e.parentType="blockquote",d=t;d=(P=e.eMarks[d])));d++)if(62!==e.src.charCodeAt(_++)||k){if(c)break;for(x=!1,a=0,s=C.length;a=P,f.push(e.bsCount[d]),e.bsCount[d]=e.sCount[d]+1+(y?1:0),g.push(e.sCount[d]),e.sCount[d]=p-l,b.push(e.tShift[d]),e.tShift[d]=_-e.bMarks[d]}for(v=e.blkIndent,e.blkIndent=0,(w=e.push("blockquote_open","blockquote",1)).markup=">",w.map=u=[t,0],e.md.block.tokenize(e,t,d),(w=e.push("blockquote_close","blockquote",-1)).markup=">",e.lineMax=S,e.parentType=m,u[1]=e.line,a=0;a=4)return!1;if(42!==(r=e.src.charCodeAt(s++))&&45!==r&&95!==r)return!1;for(i=1;s=4)return!1;if(e.listIndent>=0&&e.sCount[E]-e.listIndent>=4&&e.sCount[E]=e.blkIndent&&(O=!0),(_=GW(e,E))>=0){if(u=!0,T=e.bMarks[E]+e.tShift[E],m=Number(e.src.slice(T,_-1)),O&&1!==m)return!1}else{if(!((_=KW(e,E))>=0))return!1;u=!1}if(O&&e.skipSpaces(_)>=e.eMarks[E])return!1;if(o)return!0;for(v=e.src.charCodeAt(_-1),f=e.tokens.length,u?(R=e.push("ordered_list_open","ol",1),1!==m&&(R.attrs=[["start",m]])):R=e.push("bullet_list_open","ul",1),R.map=h=[E,0],R.markup=String.fromCharCode(v),P=!1,z=e.md.block.ruler.getRules("list"),x=e.parentType,e.parentType="list";E=g?1:b-c)>4&&(s=1),l=c+s,(R=e.push("list_item_open","li",1)).markup=String.fromCharCode(v),R.map=d=[E,0],u&&(R.info=e.src.slice(T,_-1)),k=e.tight,w=e.tShift[E],C=e.sCount[E],y=e.listIndent,e.listIndent=e.blkIndent,e.blkIndent=l,e.tight=!0,e.tShift[E]=i-e.bMarks[E],e.sCount[E]=b,i>=g&&e.isEmpty(E+1)?e.line=Math.min(e.line+2,n):e.md.block.tokenize(e,E,n,!0),e.tight&&!P||(M=!1),P=e.line-E>1&&e.isEmpty(e.line-1),e.blkIndent=e.listIndent,e.listIndent=y,e.tShift[E]=w,e.sCount[E]=C,e.tight=k,(R=e.push("list_item_close","li",-1)).markup=String.fromCharCode(v),E=e.line,d[1]=E,E>=n)break;if(e.sCount[E]=4)break;for(A=!1,a=0,p=z.length;a=4)return!1;if(91!==e.src.charCodeAt(C))return!1;for(;++C3||e.sCount[k]<0)){for(g=!1,c=0,u=b.length;c=4)return!1;if(!e.md.options.html)return!1;if(60!==e.src.charCodeAt(s))return!1;for(l=e.src.slice(s,c),r=0;r=4)return!1;if(35!==(r=e.src.charCodeAt(s))||s>=c)return!1;for(i=1,r=e.src.charCodeAt(++s);35===r&&s6||ss&&iU(e.src.charCodeAt(a-1))&&(c=a),e.line=t+1,(l=e.push("heading_open","h"+String(i),1)).markup="########".slice(0,i),l.map=[t,e.line],(l=e.push("inline","",0)).content=e.src.slice(s,c).trim(),l.map=[t,e.line],l.children=[],(l=e.push("heading_close","h"+String(i),-1)).markup="########".slice(0,i)),0))},["paragraph","reference","blockquote"]],["lheading",function(e,t,n){var o,r,i,a,l,s,c,u,d,p,h=t+1,f=e.md.block.ruler.getRules("paragraph");if(e.sCount[t]-e.blkIndent>=4)return!1;for(p=e.parentType,e.parentType="paragraph";h3)){if(e.sCount[h]>=e.blkIndent&&(s=e.bMarks[h]+e.tShift[h])<(c=e.eMarks[h])&&(45===(d=e.src.charCodeAt(s))||61===d)&&(s=e.skipChars(s,d),(s=e.skipSpaces(s))>=c)){u=61===d?1:2;break}if(!(e.sCount[h]<0)){for(r=!1,i=0,a=f.length;i3||e.sCount[c]<0)){for(r=!1,i=0,a=u.length;i=n))&&!(e.sCount[s]=u){e.line=n;break}for(i=e.line,r=0;r=e.line)throw new Error("block rule didn't increment state.line");break}if(!o)throw new Error("none of the block rules matched");e.tight=!c,e.isEmpty(e.line-1)&&(c=!0),(s=e.line)?@[]^_`{|}~-".split("").forEach((function(e){bU[e.charCodeAt(0)]=1}));var xU={};function CU(e,t){var n,o,r,i,a,l=[],s=t.length;for(n=0;n=0;n--)95!==(o=t[n]).marker&&42!==o.marker||-1!==o.end&&(r=t[o.end],l=n>0&&t[n-1].end===o.end+1&&t[n-1].marker===o.marker&&t[n-1].token===o.token-1&&t[o.end+1].token===r.token+1,a=String.fromCharCode(o.marker),(i=e.tokens[o.token]).type=l?"strong_open":"em_open",i.tag=l?"strong":"em",i.nesting=1,i.markup=l?a+a:a,i.content="",(i=e.tokens[r.token]).type=l?"strong_close":"em_close",i.tag=l?"strong":"em",i.nesting=-1,i.markup=l?a+a:a,i.content="",l&&(e.tokens[t[n-1].token].content="",e.tokens[t[o.end+1].token].content="",n--))}wU.tokenize=function(e,t){var n,o,r=e.pos,i=e.src.charCodeAt(r);if(t)return!1;if(95!==i&&42!==i)return!1;for(o=e.scanDelims(e.pos,42===i),n=0;n\x00-\x20]*)$/,RU=QW.HTML_TAG_RE,EU=CH,OU=xH.has,MU=xH.isValidEntityCode,FU=xH.fromCodePoint,IU=/^&#((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,LU=/^&([a-z][a-z0-9]{1,31});/i;function BU(e){var t,n,o,r,i,a,l,s,c={},u=e.length;if(u){var d=0,p=-2,h=[];for(t=0;ti;n-=h[n]+1)if((r=e[n]).marker===o.marker&&r.open&&r.end<0&&(l=!1,(r.close||o.open)&&(r.length+o.length)%3==0&&(r.length%3==0&&o.length%3==0||(l=!0)),!l)){s=n>0&&!e[n-1].open?h[n-1]+1:0,h[t]=t-n+s,h[n]=s,o.open=!1,r.end=t,r.close=!1,a=-1,p=-2;break}-1!==a&&(c[o.marker][(o.open?3:0)+(o.length||0)%3]=a)}}}var DU=MW,$U=xH.isWhiteSpace,NU=xH.isPunctChar,jU=xH.isMdAsciiPunct;function HU(e,t,n,o){this.src=e,this.env=n,this.md=t,this.tokens=o,this.tokens_meta=Array(o.length),this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache={},this.delimiters=[],this._prev_delimiters=[],this.backticks={},this.backticksScanned=!1,this.linkLevel=0}HU.prototype.pushPending=function(){var e=new DU("text","",0);return e.content=this.pending,e.level=this.pendingLevel,this.tokens.push(e),this.pending="",e},HU.prototype.push=function(e,t,n){this.pending&&this.pushPending();var o=new DU(e,t,n),r=null;return n<0&&(this.level--,this.delimiters=this._prev_delimiters.pop()),o.level=this.level,n>0&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],r={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(o),this.tokens_meta.push(r),o},HU.prototype.scanDelims=function(e,t){var n,o,r,i,a,l,s,c,u,d=e,p=!0,h=!0,f=this.posMax,v=this.src.charCodeAt(e);for(n=e>0?this.src.charCodeAt(e-1):32;d0||(n=e.pos)+3>e.posMax||58!==e.src.charCodeAt(n)||47!==e.src.charCodeAt(n+1)||47!==e.src.charCodeAt(n+2)||!(o=e.pending.match(vU))||(r=o[1],!(i=e.md.linkify.matchAtStart(e.src.slice(n-r.length)))||(a=i.url).length<=r.length||(a=a.replace(/\*+$/,""),l=e.md.normalizeLink(a),!e.md.validateLink(l)||(t||(e.pending=e.pending.slice(0,-r.length),(s=e.push("link_open","a",1)).attrs=[["href",l]],s.markup="linkify",s.info="auto",(s=e.push("text","",0)).content=e.md.normalizeLinkText(a),(s=e.push("link_close","a",-1)).markup="linkify",s.info="auto"),e.pos+=a.length-r.length,0))))}],["newline",function(e,t){var n,o,r,i=e.pos;if(10!==e.src.charCodeAt(i))return!1;if(n=e.pending.length-1,o=e.posMax,!t)if(n>=0&&32===e.pending.charCodeAt(n))if(n>=1&&32===e.pending.charCodeAt(n-1)){for(r=n-1;r>=1&&32===e.pending.charCodeAt(r-1);)r--;e.pending=e.pending.slice(0,r),e.push("hardbreak","br",0)}else e.pending=e.pending.slice(0,-1),e.push("softbreak","br",0);else e.push("softbreak","br",0);for(i++;i=s)return!1;if(10===(n=e.src.charCodeAt(l))){for(t||e.push("hardbreak","br",0),l++;l=55296&&n<=56319&&l+1=56320&&o<=57343&&(i+=e.src[l+1],l++),r="\\"+i,t||(a=e.push("text_special","",0),n<256&&0!==bU[n]?a.content=i:a.content=r,a.markup=r,a.info="escape"),e.pos=l+1,!0}],["backticks",function(e,t){var n,o,r,i,a,l,s,c,u=e.pos;if(96!==e.src.charCodeAt(u))return!1;for(n=u,u++,o=e.posMax;u=h)return!1;if(f=l,(s=e.md.helpers.parseLinkDestination(e.src,l,e.posMax)).ok){for(u=e.md.normalizeLink(s.str),e.md.validateLink(u)?l=s.pos:u="",f=l;l=h||41!==e.src.charCodeAt(l))&&(v=!0),l++}if(v){if(void 0===e.env.references)return!1;if(l=0?r=e.src.slice(f,l++):l=i+1):l=i+1,r||(r=e.src.slice(a,i)),!(c=e.env.references[SU(r)]))return e.pos=p,!1;u=c.href,d=c.title}return t||(e.pos=a,e.posMax=i,e.push("link_open","a",1).attrs=n=[["href",u]],d&&n.push(["title",d]),e.linkLevel++,e.md.inline.tokenize(e),e.linkLevel--,e.push("link_close","a",-1)),e.pos=l,e.posMax=h,!0}],["image",function(e,t){var n,o,r,i,a,l,s,c,u,d,p,h,f,v="",m=e.pos,g=e.posMax;if(33!==e.src.charCodeAt(e.pos))return!1;if(91!==e.src.charCodeAt(e.pos+1))return!1;if(l=e.pos+2,(a=e.md.helpers.parseLinkLabel(e,e.pos+1,!1))<0)return!1;if((s=a+1)=g)return!1;for(f=s,(u=e.md.helpers.parseLinkDestination(e.src,s,e.posMax)).ok&&(v=e.md.normalizeLink(u.str),e.md.validateLink(v)?s=u.pos:v=""),f=s;s=g||41!==e.src.charCodeAt(s))return e.pos=m,!1;s++}else{if(void 0===e.env.references)return!1;if(s=0?i=e.src.slice(f,s++):s=a+1):s=a+1,i||(i=e.src.slice(l,a)),!(c=e.env.references[PU(i)]))return e.pos=m,!1;v=c.href,d=c.title}return t||(r=e.src.slice(l,a),e.md.inline.parse(r,e.md,e.env,h=[]),(p=e.push("image","img",0)).attrs=n=[["src",v],["alt",""]],p.children=h,p.content=r,d&&n.push(["title",d])),e.pos=s,e.posMax=g,!0}],["autolink",function(e,t){var n,o,r,i,a,l,s=e.pos;if(60!==e.src.charCodeAt(s))return!1;for(a=e.pos,l=e.posMax;;){if(++s>=l)return!1;if(60===(i=e.src.charCodeAt(s)))return!1;if(62===i)break}return n=e.src.slice(a+1,s),zU.test(n)?(o=e.md.normalizeLink(n),!!e.md.validateLink(o)&&(t||((r=e.push("link_open","a",1)).attrs=[["href",o]],r.markup="autolink",r.info="auto",(r=e.push("text","",0)).content=e.md.normalizeLinkText(n),(r=e.push("link_close","a",-1)).markup="autolink",r.info="auto"),e.pos+=n.length+2,!0)):!!AU.test(n)&&(o=e.md.normalizeLink("mailto:"+n),!!e.md.validateLink(o)&&(t||((r=e.push("link_open","a",1)).attrs=[["href",o]],r.markup="autolink",r.info="auto",(r=e.push("text","",0)).content=e.md.normalizeLinkText(n),(r=e.push("link_close","a",-1)).markup="autolink",r.info="auto"),e.pos+=n.length+2,!0))}],["html_inline",function(e,t){var n,o,r,i,a,l=e.pos;return!(!e.md.options.html||(r=e.posMax,60!==e.src.charCodeAt(l)||l+2>=r||33!==(n=e.src.charCodeAt(l+1))&&63!==n&&47!==n&&!function(e){var t=32|e;return t>=97&&t<=122}(n)||!(o=e.src.slice(l).match(RU))||(t||((i=e.push("html_inline","",0)).content=o[0],a=i.content,/^\s]/i.test(a)&&e.linkLevel++,function(e){return/^<\/a\s*>/i.test(e)}(i.content)&&e.linkLevel--),e.pos+=o[0].length,0)))}],["entity",function(e,t){var n,o,r,i=e.pos,a=e.posMax;if(38!==e.src.charCodeAt(i))return!1;if(i+1>=a)return!1;if(35===e.src.charCodeAt(i+1)){if(o=e.src.slice(i).match(IU))return t||(n="x"===o[1][0].toLowerCase()?parseInt(o[1].slice(1),16):parseInt(o[1],10),(r=e.push("text_special","",0)).content=MU(n)?FU(n):FU(65533),r.markup=o[0],r.info="entity"),e.pos+=o[0].length,!0}else if((o=e.src.slice(i).match(LU))&&OU(EU,o[1]))return t||((r=e.push("text_special","",0)).content=EU[o[1]],r.markup=o[0],r.info="entity"),e.pos+=o[0].length,!0;return!1}]],qU=[["balance_pairs",function(e){var t,n=e.tokens_meta,o=e.tokens_meta.length;for(BU(e.delimiters),t=0;t0&&o++,"text"===r[t].type&&t+1=e.pos)throw new Error("inline rule didn't increment state.pos");break}}else e.pos=e.posMax;t||e.pos++,l[o]=e.pos}else e.pos=l[o]},KU.prototype.tokenize=function(e){for(var t,n,o,r=this.ruler.getRules(""),i=r.length,a=e.posMax,l=e.md.options.maxNesting;e.pos=e.pos)throw new Error("inline rule didn't increment state.pos");break}if(t){if(e.pos>=a)break}else e.pending+=e.src[e.pos++]}e.pending&&e.pushPending()},KU.prototype.parse=function(e,t,n,o){var r,i,a,l=new this.State(e,t,n,o);for(this.tokenize(l),a=(i=this.ruler2.getRules("")).length,r=0;r=3&&":"===e[t-3]||t>=3&&"/"===e[t-3]?0:o.match(n.re.no_http)[0].length:0}},"mailto:":{validate:function(e,t,n){var o=e.slice(t);return n.re.mailto||(n.re.mailto=new RegExp("^"+n.re.src_email_name+"@"+n.re.src_host_strict,"i")),n.re.mailto.test(o)?o.match(n.re.mailto)[0].length:0}}},oV="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф".split("|");function rV(e){var t=e.re=(XU?GU:(XU=1,GU=function(e){var t={};e=e||{},t.src_Any=JH().source,t.src_Cc=eW().source,t.src_Z=tW().source,t.src_P=wH.source,t.src_ZPCc=[t.src_Z,t.src_P,t.src_Cc].join("|"),t.src_ZCc=[t.src_Z,t.src_Cc].join("|");var n="[><|]";return t.src_pseudo_letter="(?:(?![><|]|"+t.src_ZPCc+")"+t.src_Any+")",t.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",t.src_auth="(?:(?:(?!"+t.src_ZCc+"|[@/\\[\\]()]).)+@)?",t.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",t.src_host_terminator="(?=$|[><|]|"+t.src_ZPCc+")(?!"+(e["---"]?"-(?!--)|":"-|")+"_|:\\d|\\.-|\\.(?!$|"+t.src_ZPCc+"))",t.src_path="(?:[/?#](?:(?!"+t.src_ZCc+"|"+n+"|[()[\\]{}.,\"'?!\\-;]).|\\[(?:(?!"+t.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+t.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+t.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+t.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+t.src_ZCc+"|[']).)+\\'|\\'(?="+t.src_pseudo_letter+"|[-])|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+t.src_ZCc+"|[.]|$)|"+(e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+",(?!"+t.src_ZCc+"|$)|;(?!"+t.src_ZCc+"|$)|\\!+(?!"+t.src_ZCc+"|[!]|$)|\\?(?!"+t.src_ZCc+"|[?]|$))+|\\/)?",t.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',t.src_xn="xn--[a-z0-9\\-]{1,59}",t.src_domain_root="(?:"+t.src_xn+"|"+t.src_pseudo_letter+"{1,63})",t.src_domain="(?:"+t.src_xn+"|(?:"+t.src_pseudo_letter+")|(?:"+t.src_pseudo_letter+"(?:-|"+t.src_pseudo_letter+"){0,61}"+t.src_pseudo_letter+"))",t.src_host="(?:(?:(?:(?:"+t.src_domain+")\\.)*"+t.src_domain+"))",t.tpl_host_fuzzy="(?:"+t.src_ip4+"|(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%)))",t.tpl_host_no_ip_fuzzy="(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%))",t.src_host_strict=t.src_host+t.src_host_terminator,t.tpl_host_fuzzy_strict=t.tpl_host_fuzzy+t.src_host_terminator,t.src_host_port_strict=t.src_host+t.src_port+t.src_host_terminator,t.tpl_host_port_fuzzy_strict=t.tpl_host_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_port_no_ip_fuzzy_strict=t.tpl_host_no_ip_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+t.src_ZPCc+"|>|$))",t.tpl_email_fuzzy='(^|[><|]|"|\\(|'+t.src_ZCc+")("+t.src_email_name+"@"+t.tpl_host_fuzzy_strict+")",t.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_fuzzy_strict+t.src_path+")",t.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_no_ip_fuzzy_strict+t.src_path+")",t}))(e.__opts__),n=e.__tlds__.slice();function o(e){return e.replace("%TLDS%",t.src_tlds)}e.onCompile(),e.__tlds_replaced__||n.push("a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]"),n.push(t.src_xn),t.src_tlds=n.join("|"),t.email_fuzzy=RegExp(o(t.tpl_email_fuzzy),"i"),t.link_fuzzy=RegExp(o(t.tpl_link_fuzzy),"i"),t.link_no_ip_fuzzy=RegExp(o(t.tpl_link_no_ip_fuzzy),"i"),t.host_fuzzy_test=RegExp(o(t.tpl_host_fuzzy_test),"i");var r=[];function i(e,t){throw new Error('(LinkifyIt) Invalid schema "'+e+'": '+t)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(t){var n=e.__schemas__[t];if(null!==n){var o={validate:null,link:null};if(e.__compiled__[t]=o,"[object Object]"===ZU(n))return function(e){return"[object RegExp]"===ZU(e)}(n.validate)?o.validate=function(e){return function(t,n){var o=t.slice(n);return e.test(o)?o.match(e)[0].length:0}}(n.validate):JU(n.validate)?o.validate=n.validate:i(t,n),void(JU(n.normalize)?o.normalize=n.normalize:n.normalize?i(t,n):o.normalize=function(e,t){t.normalize(e)});!function(e){return"[object String]"===ZU(e)}(n)?i(t,n):r.push(t)}})),r.forEach((function(t){e.__compiled__[e.__schemas__[t]]&&(e.__compiled__[t].validate=e.__compiled__[e.__schemas__[t]].validate,e.__compiled__[t].normalize=e.__compiled__[e.__schemas__[t]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,t){t.normalize(e)}};var a=Object.keys(e.__compiled__).filter((function(t){return t.length>0&&e.__compiled__[t]})).map(eV).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+a+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+a+")","ig"),e.re.schema_at_start=RegExp("^"+e.re.schema_search.source,"i"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function iV(e,t){var n=e.__index__,o=e.__last_index__,r=e.__text_cache__.slice(n,o);this.schema=e.__schema__.toLowerCase(),this.index=n+t,this.lastIndex=o+t,this.raw=r,this.text=r,this.url=r}function aV(e,t){var n=new iV(e,t);return e.__compiled__[n.schema].normalize(n,e),n}function lV(e,t){if(!(this instanceof lV))return new lV(e,t);var n;t||(n=e,Object.keys(n||{}).reduce((function(e,t){return e||tV.hasOwnProperty(t)}),!1)&&(t=e,e={})),this.__opts__=QU({},tV,t),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=QU({},nV,e),this.__compiled__={},this.__tlds__=oV,this.__tlds_replaced__=!1,this.re={},rV(this)}lV.prototype.add=function(e,t){return this.__schemas__[e]=t,rV(this),this},lV.prototype.set=function(e){return this.__opts__=QU(this.__opts__,e),this},lV.prototype.test=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return!1;var t,n,o,r,i,a,l,s;if(this.re.schema_test.test(e))for((l=this.re.schema_search).lastIndex=0;null!==(t=l.exec(e));)if(r=this.testSchemaAt(e,t[2],l.lastIndex)){this.__schema__=t[2],this.__index__=t.index+t[1].length,this.__last_index__=t.index+t[0].length+r;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&(s=e.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||s=0&&null!==(o=e.match(this.re.email_fuzzy))&&(i=o.index+o[1].length,a=o.index+o[0].length,(this.__index__<0||ithis.__last_index__)&&(this.__schema__="mailto:",this.__index__=i,this.__last_index__=a)),this.__index__>=0},lV.prototype.pretest=function(e){return this.re.pretest.test(e)},lV.prototype.testSchemaAt=function(e,t,n){return this.__compiled__[t.toLowerCase()]?this.__compiled__[t.toLowerCase()].validate(e,n,this):0},lV.prototype.match=function(e){var t=0,n=[];this.__index__>=0&&this.__text_cache__===e&&(n.push(aV(this,t)),t=this.__last_index__);for(var o=t?e.slice(t):e;this.test(o);)n.push(aV(this,t)),o=o.slice(this.__last_index__),t+=this.__last_index__;return n.length?n:null},lV.prototype.matchAtStart=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return null;var t=this.re.schema_at_start.exec(e);if(!t)return null;var n=this.testSchemaAt(e,t[2],t[0].length);return n?(this.__schema__=t[2],this.__index__=t.index+t[1].length,this.__last_index__=t.index+t[0].length+n,aV(this,0)):null},lV.prototype.tlds=function(e,t){return e=Array.isArray(e)?e:[e],t?(this.__tlds__=this.__tlds__.concat(e).sort().filter((function(e,t,n){return e!==n[t-1]})).reverse(),rV(this),this):(this.__tlds__=e.slice(),this.__tlds_replaced__=!0,rV(this),this)},lV.prototype.normalize=function(e){e.schema||(e.url="http://"+e.url),"mailto:"!==e.schema||/^mailto:/i.test(e.url)||(e.url="mailto:"+e.url)},lV.prototype.onCompile=function(){};var sV=lV;const cV=2147483647,uV=36,dV=/^xn--/,pV=/[^\0-\x7F]/,hV=/[\x2E\u3002\uFF0E\uFF61]/g,fV={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},vV=Math.floor,mV=String.fromCharCode;function gV(e){throw new RangeError(fV[e])}function bV(e,t){const n=e.split("@");let o="";n.length>1&&(o=n[0]+"@",e=n[1]);const r=function(e,t){const n=[];let o=e.length;for(;o--;)n[o]=t(e[o]);return n}((e=e.replace(hV,".")).split("."),t).join(".");return o+r}function yV(e){const t=[];let n=0;const o=e.length;for(;n=55296&&r<=56319&&nString.fromCodePoint(...e),CV=function(e,t){return e+22+75*(e<26)-((0!=t)<<5)},wV=function(e,t,n){let o=0;for(e=n?vV(e/700):e>>1,e+=vV(e/t);e>455;o+=uV)e=vV(e/35);return vV(o+36*e/(e+38))},kV=function(e){const t=[],n=e.length;let o=0,r=128,i=72,a=e.lastIndexOf("-");a<0&&(a=0);for(let s=0;s=128&&gV("not-basic"),t.push(e.charCodeAt(s));for(let s=a>0?a+1:0;s=n&&gV("invalid-input");const a=(l=e.charCodeAt(s++))>=48&&l<58?l-48+26:l>=65&&l<91?l-65:l>=97&&l<123?l-97:uV;a>=uV&&gV("invalid-input"),a>vV((cV-o)/t)&&gV("overflow"),o+=a*t;const c=r<=i?1:r>=i+26?26:r-i;if(avV(cV/u)&&gV("overflow"),t*=u}const c=t.length+1;i=wV(o-a,c,0==a),vV(o/c)>cV-r&&gV("overflow"),r+=vV(o/c),o%=c,t.splice(o++,0,r)}var l;return String.fromCodePoint(...t)},SV=function(e){const t=[],n=(e=yV(e)).length;let o=128,r=0,i=72;for(const s of e)s<128&&t.push(mV(s));const a=t.length;let l=a;for(a&&t.push("-");l=o&&tvV((cV-r)/s)&&gV("overflow"),r+=(n-o)*s,o=n;for(const c of e)if(ccV&&gV("overflow"),c===o){let e=r;for(let n=uV;;n+=uV){const o=n<=i?1:n>=i+26?26:n-i;if(e=0))try{t.hostname=BV.toASCII(t.hostname)}catch(n){}return LV.encode(LV.format(t))}function UV(e){var t=LV.parse(e,!0);if(t.hostname&&(!t.protocol||HV.indexOf(t.protocol)>=0))try{t.hostname=BV.toUnicode(t.hostname)}catch(n){}return LV.decode(LV.format(t),LV.decode.defaultChars+"%")}function VV(e,t){if(!(this instanceof VV))return new VV(e,t);t||zV.isString(e)||(t=e||{},e="default"),this.inline=new FV,this.block=new MV,this.core=new OV,this.renderer=new EV,this.linkify=new IV,this.validateLink=jV,this.normalizeLink=WV,this.normalizeLinkText=UV,this.utils=zV,this.helpers=zV.assign({},RV),this.options={},this.configure(e),t&&this.set(t)}VV.prototype.set=function(e){return zV.assign(this.options,e),this},VV.prototype.configure=function(e){var t,n=this;if(zV.isString(e)&&!(e=DV[t=e]))throw new Error('Wrong `markdown-it` preset "'+t+'", check name');if(!e)throw new Error("Wrong `markdown-it` preset, can't be empty");return e.options&&n.set(e.options),e.components&&Object.keys(e.components).forEach((function(t){e.components[t].rules&&n[t].ruler.enableOnly(e.components[t].rules),e.components[t].rules2&&n[t].ruler2.enableOnly(e.components[t].rules2)})),this},VV.prototype.enable=function(e,t){var n=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){n=n.concat(this[t].ruler.enable(e,!0))}),this),n=n.concat(this.inline.ruler2.enable(e,!0));var o=e.filter((function(e){return n.indexOf(e)<0}));if(o.length&&!t)throw new Error("MarkdownIt. Failed to enable unknown rule(s): "+o);return this},VV.prototype.disable=function(e,t){var n=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(t){n=n.concat(this[t].ruler.disable(e,!0))}),this),n=n.concat(this.inline.ruler2.disable(e,!0));var o=e.filter((function(e){return n.indexOf(e)<0}));if(o.length&&!t)throw new Error("MarkdownIt. Failed to disable unknown rule(s): "+o);return this},VV.prototype.use=function(e){var t=[this].concat(Array.prototype.slice.call(arguments,1));return e.apply(e,t),this},VV.prototype.parse=function(e,t){if("string"!=typeof e)throw new Error("Input data should be a String");var n=new this.core.State(e,this,t);return this.core.process(n),n.tokens},VV.prototype.render=function(e,t){return t=t||{},this.renderer.render(this.parse(e,t),this.options,t)},VV.prototype.parseInline=function(e,t){var n=new this.core.State(e,this,t);return n.inlineMode=!0,this.core.process(n),n.tokens},VV.prototype.renderInline=function(e,t){return t=t||{},this.renderer.render(this.parseInline(e,t),this.options,t)};const qV=vd(VV),KV={xmlns:"http://www.w3.org/2000/svg",id:"Layer_1",viewBox:"0 0 442.19 323.31"},GV=Ir("path",{d:"m72.8 140.45-12.7 145.1h42.41l8.99-102.69h.04l3.67-42.41zM124.16 37.75h-42.4l-5.57 63.61h42.4zM318.36 285.56h42.08l5.57-63.61H323.9z",class:"cls-2"},null,-1),XV=Ir("path",{d:"M382.09 37.76H340l-10.84 123.9H221.09l-14.14 161.65 85.83-121.47h145.89l3.52-40.18h-70.94z",class:"cls-2"},null,-1),YV=Ir("path",{d:"M149.41 121.47H3.52L0 161.66h221.09L235.23 0z",style:{fill:"#ffbc00"}},null,-1),QV={render:function(e,t){return _r(),zr("svg",KV,[Ir("defs",null,[(_r(),Rr(Qn("style"),null,{default:hn((()=>[Dr(".cls-2{fill:#000}@media (prefers-color-scheme:dark){.cls-2{fill:#fff}}")])),_:1}))]),GV,XV,YV])}};var ZV=(e=>(e[e.PENDING=0]="PENDING",e[e.PROCESSING=1]="PROCESSING",e[e.CANCELLED=2]="CANCELLED",e[e.COMPLETED=3]="COMPLETED",e[e.DISCOUNTED=4]="DISCOUNTED",e))(ZV||{});const JV={0:"待支付",1:"开通中",2:"已取消",3:"已完成",4:"已折抵"},eq={month_price:"月付",quarter_price:"季付",half_year_price:"半年付",year_price:"年付",two_year_price:"两年付",three_year_price:"三年付",onetime_price:"一次性",reset_price:"流量重置包"},tq=["innerHTML"],nq={class:"w-16 flex justify-center"},oq={class:"text-gray-500"},rq={class:"w-16 flex justify-center"},iq={class:"text-gray-500"},aq=["onClick"],lq={class:"w-16 flex justify-center"},sq=["src"],cq={class:"text-gray-500"},uq={class:"p-2.5 text-center"},dq={class:"font-bold mb-3"},pq={class:"mb-5 space-x-4"},hq={class:"text-center"},fq={class:"mt-2.5 text-center"},vq={class:"mb-1 md:mb-10"},mq={key:0,class:"mb-2.5"},gq={class:"font-bold"},bq=["onClick"],yq={class:"carousel-img flex flex-col justify-between p-5",style:{background:"rgba(0, 0, 0, 0.5) !important"}},xq={class:"text-xl"},Cq={class:"text-base font-semibold color-[hsla(0,0%,100%,.75)]"},wq={class:"text-block mb-4 pt-5 text-xl font-semibold"},kq={key:0,class:"mb-4 text-sm text-gray-500"},Sq={key:1,class:"mb-4 text-sm font-semibold text-red-500"},_q={key:2,class:"mb-4 text-sm text-gray-500"},Pq={class:"text-gray-500"},Tq={class:"flex items-center justify-between"},Aq={class:""},zq={class:"text-base"},Rq={class:"text-sm text-gray-500"},Eq={class:"flex items-center justify-between"},Oq={class:"text-base"},Mq={class:"text-sm text-gray-500"},Fq={class:"flex items-center justify-between"},Iq={class:"text-base"},Lq={class:"text-sm text-gray-500"},Bq={class:"flex items-center justify-between"},Dq={class:"text-base"},$q={class:"text-sm text-gray-500"},Nq=zn({__name:"index",setup(e){const t=e=>jf.global.t(e),n=function(){const e=Po(M_,null);return ai((()=>{if(null===e)return qz;const{mergedThemeRef:{value:t},mergedThemeOverridesRef:{value:n}}=e,o=(null==t?void 0:t.common)||qz;return(null==n?void 0:n.common)?Object.assign({},o,n.common):o}))}(),o=new qV({html:!0}),r=_N(),i=BN(),a=navigator.userAgent.toLowerCase();let l="unknown";a.includes("windows")?l="windows":a.includes("iphone")||a.includes("ipad")?l="ios":a.includes("macintosh")?l="mac":a.includes("android")&&(l="android");const s=Et(!1),c=Et();$n((()=>{}));const u=Et(!1),d=Et(!1),p=Et(""),h=Et(["auto"]),f=[{label:"自动",type:"auto"},{label:"全部",type:"all"},{label:"Anytls",type:"anytls"},{label:"Vless",type:"vless"},{label:"Hy1",type:"hysteria"},{label:"Hy2",type:"hysteria2"},{label:"Shadowsocks",type:"shadowsocks"},{label:"Vmess",type:"vmess"},{label:"Trojan",type:"trojan"}],m=Et([]),g=()=>{var e;const t=null==(e=b.value)?void 0:e.subscribe_url;if(!t)return;const n=h.value;let o="auto";n.includes("all")?o="all":n.includes("auto")||(o=n.join(",")),p.value=((e,t)=>{if(!e)return"";const n=new URL(e);return Object.entries(t).forEach((([e,t])=>{n.searchParams.set(e,t)})),n.toString()})(t,{types:o})},b=ai((()=>i.subscribe)),y=ai((()=>{var e;const t=null==(e=b.value)?void 0:e.subscribe_url,n=encodeURIComponent(r.title||"");if(!t)return[];const o=encodeURIComponent(t),i=(a=t,btoa(unescape(encodeURIComponent(a)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"");var a;return[{name:"复制订阅链接",icon:"icon-fluent:copy-24-filled",iconType:"component",platforms:["windows","mac","ios","android","unknown"],url:"copy"},{name:"Clash",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAADAFBMVEVGpf9Do/8AZ+VIp/83m/1Lqf8AZeQmkfkymPs7nf4Cg/L1+f48n/80mvwtlfrx9/4cjPcZivX3+v4BaeQBgPEAbeg+oP/v9v4BauYBfvDn8f3u9f1Bov8/of8AZeMqlPr4+/4Oh/Qjj/ggjvcAXsoAcOnt8/0BcusIhfM4nf7l8PwSh/QAe+4AduwAee3k7/zz+P/6/P4BYMwBfO/i7vwvlvsAdevp8f3h7Prk7vsAYtLp8/0Wivb9/v7g7P0BZ+Dd6/zc6vwAYM77/f6Ns9yUuOAAZNXr9P7a6PmcvuIBaOKXu+CPtt+RtNva6fzS4vYAZdnV5fjA1++20OvY5/re6vzX5vjI3fS50u0AZdzU4/euyuixy+elxOWXudzL3vK91e2dvN+Ut9sAYdCzzemoxube6vnG2/HF2e+qyOmgweTW5vrK3/XC2fGsyOeMstvs8vvS5PnP4fXM4PXO3/PH3PPE2/O20e6zzuywzOoAcObC2O+jw+agwOGbu91AfdGmxugHYa3z9/zQ4/fN4faiweMAbuKaveEAZt4CX63Y6Py50+/B1usBdun////o8Png6ve91vG71PC80+qty+oAeOoAc+fY5PTS4fPJ2+8Bf+260ekAbeWsx+QAad4AbNjf7P3i7Pjd6PbA1/MAe+yyzesAduYwlPcZiPErkvYmj/WoxeOkwuJDn/wijPMNhPAXhO4AfOm3z+iFrNkAadIvdNBWqP1ztfwuiOoAcd44edElbs/W5PakyfVdo/IrjvF+sO1QmOtTkOC32f1OpP2Cu/q51veu0PeKuvI4kfCbwO6Su+4hie4KgOwGdeITZ80caLKgzP7C3v1erP3L4/xyrvNHmPGvzu5yqOw8kesQf+kggehGjuaBrOIeeeFnmdpXjdcCaNYQZK+TxfzB2vc6l/Vnp/GkxvBjouxbmumIsuhknOQ4g+Iuf+J6pNdzoNIcas5omMwmbbSax/hGnPVTn/MRd+d1pOF9qOBDht4NZc0yfNgYc9hfkcg4d7owc7j13NKGAAAKFElEQVRo3uzUP2gTURzA8RMjJlzj6RsM5BRPhQPjkGQIyXFGBIdzURDESRzEQVDw/LOJQw6XiFwEBwUR/DPkjyQGhMSliZI/rRohSRvBNbXipNjW0T+/e7kber73ajNkEL+06aP58fvwrn+4TRNoMsjGCTQhhIMPy1rHgRsdOPcBPvGQ68D9b31tmED/ELJjAnE7JxC3fa2mnMP4U9zUFEzAy5TrAOHDxrkNo4P9HvEAUzsIbzkbAWHm6wUaFd9aQ5VGosoY4nzsmodMc76yjz20oYFQjzGzBuKpItM0+xxT2bdgIKlfZCD7WPn8C2YS6vkYQ565gxJChyoe6gTnYbbYsBBTqPrpM8WGhCQkVr3UCTbiXzkGCCg3m1TFXxWRJCFjYVzEWxMBsepRjWIfWQiaWaQjflbZajQ5Sq56ySPeloEQGOjGCkyQYyLe7LJ9kcPJfpE8UpxHOD7xPUtFvKyybRMTEN+KkSZiLYPHhqEPsrQ1HNNYvGQCMep8MxaL+X3FZrMyV6k0i0WPF74BF+ERDxnGbH485HsYiFFRaXmu1WvM33wYDgaD4YPH5vszC9VKKwDACJnOxmhIjFH+k5C0CUhQUdRKghB+QUIozttFjI+LWcoebgu9bKEVdQic5IRG8fhJOcjxlTxlEROpLyejQDi5CAw4REQQHtXGQfL1djJKINyCELGMgD4o7KIgu+jlX99Irn0LEMAARHxbz5MXcQyj8D7xtwRGZqjIZmr5Uk12EVQBIx9fF8ibGEihNOAlN0EGgAgExOPvx0A6sy6BQYAh366VxkCmo/TnJKwiMJIZlApkZA+1Ur0dRSQBWg2AAMn6bKdA3MRCXl+SkGPAfVyCQwgRARuarE93SmRkL7Xc+4RzCySeO3VVIF5CPvfgWhyuAenteom4iY5szdV0+zmhzNfucOmo+IcgBjLPl4ZLXxRR1jRVv/JhGxnZSq08MOx/gOh0KpVKd+/zf/wghKfDdCo1vB6QVVXPHHmV20vaREdK5VneTvyRtpTnEZtwDOgrfuebCsVDjz7ltq4PyZWnkY0EHMRFyLKDxMGIh5SX5W1EZButXKeN7N8n/vownU4v3YqsEiBNPNWFd7pPtXg8GAxl3pRzpFUM5MUFAKyEiP78V/fnddEWbEDTZFUOnvnZ/XVRAQIQZaazTqT84YRhCTjx3q27LkKWVav41TtXg6PCypMXZOQApdyzV4rghP/kRMgW4BMD1kNSNdW6BRRWLn94tp+wi9tP691n3RZwWNDsxyQ7Ai5kpyROvnpGWsXtJgfIS9FFiJiAr2dPgeQmwmEl8fjTu/2EZb8pJ3uYJsIADDu7uJgY4+RijLE41JC7mJB20glT6A8pxmpCTgyotaD8NHFA4oC59DBcr1w00uPayaQ2cShJUWBQgcBosVQmI/g3OKiDDr7f992f7d3AE0rb5Xnu/e564DhK9OX8gP+ljfWJI4eaCyfO55/03fvx43LvM8EunKGc5TlpacOaAg+DRDwo1RcnzAKw7gT/5Na9ePXqrZscEo4CgZPW6iW3JSc9KG2/njhmjmDgPoDz53BS5HfhmEATHR2cUNsuubg8I2pl0DnC9V6zBCuAuYgwXVHdIgc9UN+HmkZYBccGu4AGIrH3qovLK3JYXeao3n5e3RPUTl5zgUDkwsVl9fA+IuW9DBJGAdin5NzAcfB3BCKRABKB4IXqXnlfka1k0jqm1gKPAMAOYgdBQlhZco0cdkctv00CFByHxJ/BH8/ziLAAJpj+zmBn51Q4ul5WW2Xekd2k85QAj4ZVmHNOQIIwNTUQ3a3vI6LX3yTNDQB65rdOiWyIBFmDBqbC4fBAfGRbP9oaOeqOvj2ftBNWo8OxIUhhE5AgjYH4fKXcKmuK+J+vvnuFd1WuTJ6yn1ZWMCawDdBTTD/ldvxOo6x6R1ji5ZuQEPvpP+qXG1HehD2qSESApYfZkkMfCt0G9xOfZZeI38HqIpfJZKRPfr8uLmt5nucMcPGCEAwKFyhEHo1GB0KAuOPETpicHEpsFXV/M87Iu4+ZDJ9JbdV1v17ck/IcEAhBAXoK7IDZnXIwBAZjiSW3yGmL1Y+ZfD5fa2wWZV0vbkmSACy9KY8D2C8CyFOGnBADd66tb+qnm7EjzxfRkNZ3ni6gIhffSpqmWXrTDjXk91Op1GSKuWPUDe4SbqTXdmTdM9L2UstL0trfFy+eLiCyuaZFTb9lh97DDv2NeULX9e9iW0ukzWBjF42uP2iQiPhrV6tGq9WqqU+BoWGqTxj2a8wN4J8mPAJj38S2ZsyIrxLD+XxgDVEu7owoDv/w8NDwYCJB9JDbdly5ZX9I6RltZGWvSPtyVdOUFaPhy36fzgHoCQkCuXZA3Ol0ugtQOVOPmHR3r2R9LREfI/tZUZQcIgtZ0eeTs9/6c7h8pocc9Pf3Q0/tV64we08Ps48SarXRQq1Q6Ps6DsH/GBFxnESUr6yBr41ZGjD1adBF/QBy2LsBkRcKhbGZsRmD3r7fXpF28cFKTskpXxbGxXby9fHKbGKW+W096CEYesgJvTO9121uXvqwmW1vjvyjjIx5EwXjOPwp+g007gwdHI2YWDXpeMkBF6AmvQ52adKEVHQpLm42jQSkH0AnPZOLLk3Hu4H1kosFx7NXz6lVr0N/7ytCQBz6DCR/As/z8ueQcquR/bQvnxVvfNJ9f6C/DOlvNvZ6mMoMkQh+5O1r++LLxezFG191+JtU3wpOf0L1n73Dl8v1Os9fheDLxUdlJ5KiKNrdsq3r+un971TqEOPktAl9CwGD+E8A0YNKpVIGPE/812dR+MKjkorgR6b/P+lkRT/+fH/BOGu2jEDPcdQe6GGHPx9DtfGs3O6L3H1zdL1JuPl5/+vpyuhTP+f5ff01qFar+XwDFHYRxb9mMjaSRCRnTxBpUQyj7/tB4D+DHn6qZ2MpiCttJ5LcoFlTebFEBP4+LWzP34W+B7+v9/zFeFh1pSnJMNuIaU3TmbVbRgUNDo1Op9Pt8r0eAsF2BJaViD675fw8G6IoqQ9H+yKKZuVkhhk7LGcY6HAcjXTRwB8QRbGhqoIgSKBUIu6ALO3gbglIgvhgmfsipnVMKow9cp3XyUDkQAeQTg8ZgAwgmQgSQQAqkFa7kQMPU8PCSCWRSOA6rrnOfDnIFllBFX1UQEtezQviwwaDwXz+z3Hd2nBqmQdhENlWjqzjtJxhNiRoa23bi/F4PASj0agWYQSGAE8sFra93rwm5+IjQSWXluVMxs98HIZ5724OkRgIYSgMdyp6gRhUD4LJDAIRFRu9l8mx+8os7LAMSMR+/r0fEZpGUCF2zTlGlErqsv69pHREXUcCCbuZolRSkHrdHzRHgVHOJkMk9IhEmNm9pE5xKTeqauZC4QaRAQFS4H/W6I1VXjCIEIVpZOyAVDwnFZ3CGKENXu8NHhT5bLAn8t3gB5tRcTnQFMqEAAAAAElFTkSuQmCC",iconType:"img",platforms:["windows"],url:`clash://install-config?url=${o}&name=${n}`},{name:"Clash Meta",icon:"data:image/png;base64,UklGRiYGAABXRUJQVlA4WAoAAAAQAAAATwAATwAAQUxQSJ4CAAABkAVJsmlb8847eLZt27Zt27Zt27ZtG9e2bdv39tNZe++17vNPREwA/dOZo6hWhOxFssnRaNra4w+M3CJNqvLX1D7cxeDukVWTazDpXKDXrxFvXaOg9x1TDg99iOzM17Ak6Ddgc2dA0hCeZoL1k2zImMbPGvABrORlP7jBHi40l8ARzquVy/MEXOFhLqWKGYAzfCqiTGV7cAfbCko09IUA8KonX8cICIGwdnINToQgiO8vz9QMCIP0iXKsgNx8AEuk7YZg2C5BfQ7C4ZSKJdcDZAK4UyR7iSq1a1Uuri3+EZkCgt0jk1JTE8OdfJFJ8PoTsW7ZP5APx45dffiYRFTTlQfjkkQb+RhJRKXNlXuej4iW8TGaiKjAa6Wu6oiIVnBE2W8qc4h+yBVlOa7EehKBaLN8s0kQWiBT8ggShsak6ktL1xfdjQSiXhEIfLFzUrdm9es37zlt37sw+DQjoahCu0LEXLxDCRJM6f84fDIDYybV/XTx0o4xkab6sL0fQwRY+aOA19v6V8rK9sPCrRccPHhoT2meah08ePDArKYFiP+ClSqUlEXc0h5J8fGDuWozdpTE0YNys5WKAjCSLfeg0aMkjm3DVAsybmCjdYCxmm0tZKzFUtQg0E+iv98gCfm90YPY+/v6+0kMNCjKQup8eaXmJKm1e5DUnHml5lPTL7y21f4PrZVq9WF/Ky0n6qbb7AFsVWorAPttTdWKqRpusAYAx+1FlSq63REArDc0VClRZ5VZOgC3/W11xKGu7X43AOlmq+rIVGOJYSoAr6OdchC3OTod9QKQarikhqTi8z8kA/A70yM3cZ67xxk/AMkf5hdnUhkBCLrULx8Jma/fpSAARioWuhR+c0ghErjQkJvhl4hZXYCEL6Bm+5cSVlA4IGIDAAAwGQCdASpQAFAAPkEaikOioaEa2ed8KAQEtgBbJur/YPxm64bFPaPyH5r3ezvr+QGYz+G/on+Z/p35Z9rD8o+wB+lvmZ+p3+Af3D+5ewD9b/2v94D0Af9X1AP8H/uvVU/zfsMfsV7AH7O+mR7Gn7ifuB7V2Yn/RLToBFaF49vT657i4FNhTFMPtqGBnLHb4B0mdEFIcp89CJvbbCPD4/QeZhwQQzZ8BxgBYJstiZqMBJD6z585YDHszJsSre6r3yMDyPrDGOzaYTcIIILf8uoSangA/uHNmzlTvvlp4WxismwIwhrpTbKk5HA99Zt/tjf//B1f/wjF//4Oz7Ro8qdwrGruK80gZGdfcjEjVmeAY3UNq/bKHbPJeZyPGePUJYsf1pTxUT+M/1yY9sp5QEaUI/nWbM+hrV4Wv2GCz8YHB1EU6uczvWjFJmo/ILHBjfR2dpCGtC7aaJrcU2802eJTgxsCLzPMTBp+iLQAcf1z34AZndAHu/MsTUnzhvX5iBLRl0rcsyt8px9H3DpVdPqz9F30dKwOAKELHB71muyZVCqSi6Ijvf/Z3WEYi+Jy9gg4gwMX75I/kfFsZTr7B6AUO5g/bTvaEq7oh9QTCrGVLPJY2tIyTiFf6+rnBPHuJQFG2ntz1V2ZE3kFqOf1JYkNtmTx5bM42JZLzDv8lK+cZlqBMuGj5tTqsUlkszMA9vYVj/+YQXiow3o8IGtvSD8Z9yp7r5vAB/RBYfyMXHGCD2/Vj9Krhqkp9w11usppHaLv4fZw8b3KwrMeg4xklboK6/9Fk8fH9jbQr2Gh3gBR1O00KEtl0DoRpGMbFooOH7dbaaubWVWnZJSKjwKIyP/s2PwjLOOynzDVSVfh9QzyYBAtiUl2qfMRoRAekN+1zwxjUnBZz1zVVnum4pxFz4O/ytYWZA4AKd06/BG2+/aqSmflFZELL5IvsKadrnEUwQiAtJkrfXIu0S5ATyAZ8U7ztY9txpPVO65FVvH6NJPkeoxN4DJMkkeJyGkxeZyTOKOXTYLyG410M+lef83/R1x+Fufa2JlrS4UJj9uQp/8XdI+6n2yYec5INem5wZ3l+51bAhgdYqwdZhQ4nrP/8zviDM+SQAmVegbwNZIXMtlySH9p0fzgvNUc4nPYjSzoYgAAAA==",iconType:"img",platforms:["mac","android"],url:`clash://install-config?url=${o}&name=${n}`},{name:"Hiddify",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAB7FBMVEVFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lFX+lHcExr5uSJAAAApHRSTlP/9/1D/u1CWC8ucnhZ6/xaRFdxb+p5Avtw6Ol3+AT65ezz5w8VgE0G9dkeJgvM1u/uUj16FyqyydO529+RLX0QU4ufvOCS+ZfkxWJnKGsJNxwrDDA1OgHy16j0W0UWNMPm44Gv2Jrd4qmP9rjHjtGYg4i2u6HKz10+JDMZXBh/HUEiSyxQX0Buc1QgSU4aoMTxq7UFbHtRMjwDCA0SShHOtCc2AIfjeMgAAAAJcEhZcwAALiMAAC4jAXilP3YAAALsSURBVGje7dnnUxpBFADwBwgYMIaIgEhTukiJAmqMMfYWW4w9lvTeeze9996b/2gYFO7EW7LIvsuY8L4ww+7jd+yyFZgXISCH5JBVi3xT6mQWXEQ/1WqAkBoVudkQglhMIiL+niDEoxAPGdhjBWTkmssIgIt4qmqBCykKsq3FC8jIlpJNALiIXtYqBWTk0aEQpEYeW6T/bh0AMjLzsgOQkS9hGwAy4pgAQEdGJkVAqgxADGbIemkO+fvIR8drdGSmJTDxHhdx6rpi2d1ORMTifh7P9n5IvqVkjfTuK1/Ilsi4wVjIFHH01SeyJQpuzbWTCHvmyOi9I9wz8xC91mY2myWpYZbU3ckY8TXLeQ/JQ+b1vZopjUYWD8XCiyYWN3yZbrj7rx5f0hJ8hNWu/vJBMyAjznBXap+yRiy3Zpf/cBgjlZ1jkB7xv9OFdTp1LEwmdSJMalU17SHo89YKwSHAQz61W4WHidxEh0RrCGsrD6kuJw3GMSpk6BUpn4esyyNVojs6RIspkB1ExECFbN8gApIvF6G5ckgOISMFRIRwZvQ8bnBVIiP5Z2Pz0NE5TMSp2xUvu5AhIqVHLO7uxTItGlJ5IjljZ4goaZEh/tpUgoOMLFmbUJArJ0uXlBWxQrjTr+fhuZQy9sjtZ97UMhVjZNkVFXtkVHFGqAJTZLBZeAlniOi/BghlHLImW+Qt8QOoEBkVQtxSsUTKxEDW/gdI0apCIA1SIgai/WeQ+2IgpmT+8As05LQ/ka8CNMTaHo1Epqcjvh4bHgJg3DseDI63WQET+XNQIsU5hBFyoLPx+m5X46lA1kgpsaStejF9cCMNUrAi5Fgy/0kHGpLhBEk+zsUGHKtFa2UI1Q6SDiFefsMDdt/ETrNbKcsS2UmBbM4WsVF0PCKiorkelFFc4KRrrj6Kjj98MVmpKc1grCGO0gHuXxnivHL+abLSpQpSpe/w84fwwmd8o+dO38O1wm1R7+bdAjTtFz7Fz/76DY+rJdzy4R8QAAAAAElFTkSuQmCC",iconType:"img",platforms:["mac","android","windows","ios"],url:`hiddify://import/${t}#${n}`},{name:"SingBox",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABdCAYAAABTl8MxAAAAAXNSR0IArs4c6QAAEtRJREFUeF7tXXlwlOd9flZ7n1rtSisJ3UIcuk8kGSHMZYNjY+PaLq3bSYbU6djTdNKZpjOdNq2dmU4zjRN3WjexY5ohxsZxjF2gNGDuUwgE4j5tjO77lvaSVrtb/16xqgwrfceeYvr7B2b0fd97PO/z/s73XYnX6/UiisXtdqOlswcH687h5t0m9A8Ng3ocZ9AjJyMVjy8rRXHuIsik0igeBf+uSaIVEFonYzY7dh44iqP1jZhwuWYd1eLMNLz6R88j2RI/74GJSkBo8ls6uvHeJ3vQ3NHFa3mplUo8/+QqPLF8GTRqFWIkEl7vRdtDUQUIsWJkzIaDdWex91gdnOMTguZLGhODjJQk/MWfvIhEswlKhVzQ+9HwcNQAQqxoau/CB3v2405Tq+i5kUgkiNVpsXHNCtSWlyBWr0VMTIzo74X7xagAZHBkFKcbr2DPkVMYtdqCMgfEjsWZ6Xhpwxpkp6XMG7ZEFBCXaxJ329rx2YFjuHm3Ga7JyaCA4fsI6RGT0YAnaqqwuqoMRr0OxKBologBMjJmxdGzjThY1wBiiMfjCdk8qZQKLMlMx+an1yEzJRlymSxkbQX64bADMj7hQmtXN3buP4rbTS1wOMcDHQOv94ktcbEGrKkux7cefww6jYbXe+F+KGyAEAOGx2y43dSOzt4+HDh1BsOjY+EeL4gt6cmJ+PPNm5CxICns7XM1GBZAJt1uNHX0YGLSDY1KBbfHg+7+Afz34eNo7ewGeePhFNIjeq0GT69ajm89vhwqhSKczc/ZVsgBsdqd+KqtCwa9HjExkmml6vF6MT4+jrrGKzjRcAF2hzPsk0K6JH1BEl7dvAmZqclhb99fgyEFpHtgGCNWB1QqJWazbWgr6xkYxKf7D6GlowvhDq0RWzQqJV54cjXW11ZBIY+sMxkSQMicbe8bYmzg65TRtnbiXCMO1dWD3g+3UD+XZKXjlRc3Ii05CZGyjoMKCG1DI6M2tPYMsJWu12kgk8oEDa6rdwA79x9Ee3dP2HULLYJYvQ6b1q3E2uoKZgCE228JCiA0+bTC23oGMDJmBwHjExqUVqMWFOybcE2irvEyjp87z3RLKH0Uf0yUSqUoWJSN72x6CkkJ5rD6LQED4vF4YXeOo6W7D+PjLvhLrkilMcyqoZwF3xXHdEv/ID77/DAzk8cnhAUag7HlmWINLPRSVZwPnUbNu++BtC0aECIBsaJ/aBS9Q6Ps/3MJAUFsUauUoKgsX7E5nGi4ch1nGi9jZGyMmczhFLlMirycbPzps+uRmmQJeb5FFCC0JZGH3dE7BJqwmVsU12TJZFJo1Sq2DfBlC4Hd0d2Lw3XncK+tHc7x8Hj3M8eSYDLiubUrsby0kLE9VCIYENekG/faOtDS1QejwQDab4UKhTGUAtlCjLTabGi8cZuxZWg0tPEvf2OiRVSWv4QBk522QBDT+c6RIEAopXqorgGH6y+wyOyyogLk5Sxk25BQIbOSwNSqVJDL+bOF2u3o6cOx+vP4oql5ztSu0D7xfd5ijsP6FRRBLg86W3gDQqzYsfcgvmhqhfO+giWdkJmagtplZYiLjRVkSfkGT2xRKORsG+Prs5BVZ7U7cP2Luzh0+izTLeEWGnvewiy8vPFJpCVZePedq5+cgFAa9fCZ8/j9iToMDI8+5EnTKifbfVVVBbLSUkWZiMQWAoMsGSGe8uSkG32DQ9h/4jRufdUUfvM4JoblWzbUVmNNdQXrf6AyJyAdvX147+PduNfWOc0Kfw2SclYqFMjJSMPqxyqhVYvr2NR35IL8FjKzHU4nY8u+Y6cxZgtOxpHvxPr6vCgjDVteeAYplgQWsxMrfgGhfbr+0nXs2HsAw2NW3iuP/A2DTodnVq/EgkQLbyvqwc4TW/RaYWxxuz0YGhnF7kNHcaephXefxU7cg++RKW/QafHSU2uxorwIVAUjRh4CpHdgCO/v2ofLt7+Ey+Xf0Zvb3wALl1QU5jPdwlcvPPjNKb9FDo2av5dPltiEawIXb9zGgZNnws4WGgP5Lfk52XjlpWdByp+vae8b/zQg5HA1XL2J7bv3s+rAYEhivBnPrl0Fc5xR9Odo5ekEsoWUfv/gMHYfPo4795rCHkGmwVJt2JY/eBq1FSWCzGOJx+Px0rb0u32HcfL85aAXGpBJu7KiHKUFuczMFbO7+rx8oQVwlC5uuHoDR+rOwmq3hx0YGmtp3hK89vLziNXpeQVZJR09fd63tv0WLZ3dolcx14u0bWWkLMCGlTXQaTWCVszMb1MsjCwZ8vb5bgXEFoqF7T1yAs0dnZh0TfqNt3GNIZC/xxtj8eMffA8WUxznZyR/99a73i+axRemcbYw4wFSequrK5GdnsrMW76TOrONaUtMgN9C71O4hdhy/Ox5VjMc7ghyTVkR/uo7mzmnS/LyX/+jl8Ld4RICYnF2JmrKS5hFJiTQ+BBbtGpBEeQptvRjz6FjaOvqDquXTxWU77z+N5BzZCQlL3z/b70SAdHXYABHqzw+zojaZeVsKxNbgysmJkb9p8DomYtXcPbyVQwOjwRjSJzfUMqk+Ke//C7S09PntDwlG1/5gVep0XJ+MBQPqFUqFC1djPKCXOi1WlFbGPWLdItWIyyCTGyhipdDdWfxVUtbSNlCbXnGHfiHV7+NoqKiOQOykhXP/qHXlJQCuVIlekICAYssL4vZhJqKUmSlpojewhhbFPfzLVIB+Ra7Axeu3cSpCxeZYxls8bjdcNptkHrdeP37f4aSkpK5AanesMkrlcmhizNBazAiRkQ4PRiDoErCgsU5qCopBDFHrIhhiy/fcvB0Pe61tgeFLcQKt2uCgeGenGTb8ht8AKla/xzLusbESKHUaGBMSIIsQoVjNJnElrU11UhJtIjFhEWdKYJMfgtfo4GSbDa7A1du3cHR+gZQ7bFY8Xo8DAjXxDjo/ySCAaGXSNlKZTIYLclQabQIt7L3TQAVRVQWFaA0L5dNrBghp4y2Q6F+C8Xx+gaGsOvgUbR2dnGmph/sm3vSBYd1DLRVzawxEwWI7+PkyKl0esYWAigSIpPJmCP11KpaJPBwqGbr47SXr6J8C784AbHF6aSqyss4ef4i7A4H5xTQ5E847Bh3OqZZMfOlgABhH5JIQJNCCj9SVhirKlSrmHlM+iWQk7YUiSY9xbKTnNM79QDVHFOw9ROqE+vqBlXY+BNiA7FictIFdkTYjwQOiO+jVJhsNMGYkMhAioSQHsjJTMeax6pYMiwQoWJvjVopyKKkRNj+k3U41dD4zaoXYsW4E06blTNOFjxA7o+ezGJTcgoUSvEWUCATSe8aDXqsrq5i59PFFFf42qd3Kd8i9OBOa0cXduzdh4GhEaYjHDYrXOP8isSDDohP6RvMFujjTBFT+LSN5i9ayHItlJkUEw/zAUNMIcYI+cbk5CQrDD964hQ8Hv7HKEICiA8UhUrNdItUZIAwUKbQ+wkmE9avrEFivEnwSp/ZPuklqkGmbZELGFIPbo+bVVS+/i8/EzSMkAEyTXuZDLHxiVDr9BFzJskzLyvIY6GXQI6o+Y4kUCXJbBlOUuhUzkqFgRQL+/GbP48uQBhbYmKg1uphMCcwZ5JrhQkagYCH6fjA41UVSEoQf7UGmSu0HZIPJJN+ky1kbVHZkS8qTpU4UQmIb84IjFizhfkuYnPoAubf76NUrFddUsz0CyXBxAr1nw7wsAi0RIKJCRerwJ9ZUxz1gNDgKQamMcRCHxcPWYROIZH1RAHK6tJiJCfEg3wPoeJzCmnSqeaKWP/gqa55AYhP4csUSsTGW6DWkb8Qfr9l6kCnFsuK8pG/KIc5lnyFtqbB4VF0DwyyVK/JGIvUZMtD8bB5A4hv4DFSGTQGA2LNCaD/R0JI4VPya0VFKUuGzbWVsrCHy4W2rl5WDEE1XiSUcs5KXcBy+DNl3gHiYwuZxeakVChEVjIGCuRU8ZoONRVlWJSZ7jczSWDQ/SqtnT2s4mbm9vRIATKTLXqTCTqjOSIKn7Ywil8tycrEiooytup91iDlQVo7ujFqs02zYuYieCQBmalbzMmpkIsssQwGW3RaLdbX1rAz6XanE/daO1l4fbaj2I8sIP/HFikzj7VG4SWWgQIy7dBKpVhVtQxSqYwzIDgbIONfX6z2RjQ6hmImieUntDrEWZJZ6CW84oXTZmPZyNLiEk5HdlZAvj4f88ZPo9BTD2QyCYw4SxIDRyIR7i8IbdvjnpzKWbhcyMrIRFlJAIB8fcbxjTffEtSFkMeyBPVmlodZ6IVlJhPvZyaD77eQfnA5HXA67NOZvEABGRoZwU/f/qWgKZgXgPhGRM6kOXkB5ApVUMP6UzmLMbjpWMWMTF6ggJy9cBF7Pj/w6AJCI6PQizY2juVaqCwpEGE3S1AJjs3KEkkPSiCAjIyOYusHH2FgaEhQF+cVQ2aOTKnWsNCLQq3hVLr+ZoQAGHfYWSZvNnNWDCD0rb7+AfzPoSP48t49QWDQw/MWkCm2yKAzxkFnJLbwD70QCAQGFabNJUIBoczgV80tOHDsOHr6+gWDMe8B8TmTVBtmiLdwlrlSMRoBQQUHvsK0YABCmUSzQY/TDQ24euMWO1wq9j6vec2Q6cmkwj0KVOoNLAnmr8x1qjDNCvqXr3AxhAp57HY72tvbMDg0yFhBufRA5NEA5P4MUH4lPiXDb9iFtin7mLAi6ayMDJSWlPq96IAmvrevDzdu3WRXeQTrPshHChCyuuJT0/2WILmcTtitwgBZkJSM6srKbwQ7aSuyO+y4fuMmunt7GCPEbk/+mPT/gMyxv1DufMO6J6C6X2VPLOjo7MTVG9cxMTERkuNuvAGh4wjBXAmB7LOzvRtshlA7xlgjMjPSGRNa29phJZ8lhHdxqRRyfudDap950TvXj6WEYoKFfjMUgAjtQ6DPU6Xkj17bwn2C6pmXt3j7BgYDbS+k7z8KgKRazPjh976NpUuXzn3G8LUf/r334rXrIZ3QQD/+KABSU5yL7/7xS0hNTZ0zAiE5Wd/g/dFPfsZumY5Wme+AKOQybHluPZ5ctxZ6vX7OaZYMjYx4//lf/wNnL1wKytm6UIA6nwGRS6Uoz8vB5o1Pse2Kq2qf3XVCnui7v9mBhouXMRyBuwy5QJyPgNA5R51GhdysNGxYuZydvtVquY+fT98GZLPbcenqDXzw6S7c/vIuKG8cLTLfAFHK5chISsDy0gIU5C5BVlYWdDp+v+7zjfuyyA7v7R/A7n0HsOfzQxgeefhKv0iANF8AoXqBWJ0G5UtzUF1WjKVLFsNkMnFepzFzTv3eKEeHHK/duoN3tn2AL+81By2eIxbM+QCIQiZDRnICakoLUVpUgLS0NGg0GsG1aLPeuUhsIf+E2PLx7r1wROD3PXwARjMgxAqdWoXK/MWoLClEbu5SmE0mdrRBzPEMzltJKQdALPnJv/0CTS1tYhd5QO9FKyBUXb8g3oR1VaUoLixERkY61HQlYQCX+XACQjNJbBm1WvGb3+7Ef/3+AAvAhVOiERC1UoGqgsXs6ENeXm5ArODUIbNNNsW8rt24jTd/+Ss0t7aHDZNoAyQlYYoVpcVFyMzMZKwQsz35m0BeDHnwxTGrFe9t/wi79x0M+h2N/joZLYCQk1dZsBgrK8uQn5cHs9nM6egJXbWiAKFGKGxdf+ES3t667f4tB6H7GYlIA0JOXqIpFmurSlFeUsz8imCyQvSW5Q/t7t4+bP3wYxw/fYadxQtFbiVSgLD7HeUylC9dyK4kzM/Pg8ViCTorggoIfYyOC1M139v/+T4IoEALAh4EPhKA0Pn1BKMBq5YVoby4CNnZ2Sz0ESxdMdtWJnrL8vfBzu4e/PvWbWi8cg1WW/DuyQ0nILQ9qVUKFC7MxPLyIhTm5zNWcF1eKVRXhAUQaoSuNTpWV4/tn3yGlraOoKRFwwUIsSLRbMSKknyUFRUiOzuLhcsD8SuEAhVUhsxsvLm1De9//BlOnWtgbAlEQg0Iu8mBfg8kOx015cUoyMtFYmIiFBG4DCFkgBAA5OUfO12PD3fuQnNb26z3TXGBFUpAaPUvMMfhsZI8lBcXIjtrihVceQuuPov9e0gB8Xn55ER++OkunDhzDhTmFyqhAoQqQYoXZaG6tBCF9y0opVLYXVpCx8L1fMgBoQ6wnyiy2dBw6Qre3fYh81uEmMfBBoS2KPIraksLUF5SFHFWBN3s5ULd93f6jdvu3l68+/4OnGloZFsaHwkmIORXFOZkoKasGEWFBcyCioSuCJuVxTXB7DcQHQ6crG/Ar7Z/hJ7ePk62BAMQOiwXbzRgTWUxSgoLsCgnh2XxwmlBcc0N/T0sW5a/jrAf9OofwM/f2YpzjZfmjIkFCghd5VeQnYbaZSUoKihAUlIS8ytC7eTxAeDBZyIGiE+3UF3t3oNH8Itfb2d6xp8EAojZoMPKskJUlhUjJycn7H6FUFD+F99EwWJISrZpAAAAAElFTkSuQmCC",iconType:"img",platforms:["android","mac","ios"],url:`sing-box://import-remote-profile?url=${o}#${n}`},{name:"Shadowrocket",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAC+lBMVEX///////7+//79//+ZfOn//v+UfumXe+f9/f9crPCZe+n7/P5Lu/Fmo+/8/v6cd+b5+f339/1ooO32+v16lOuYfeqgduqZeedQuPFVs/Bfqe92q+1xme10l+1+j+yOg+qdd+n6/v7x9/318PxStfJJvO9Qte93l+uQf+nq9PzX0PRiqu5vnO2Bje2IiOv8+v719f3y9P3y7ftPtvJNt/BVsu9Lue6Hiu2RgOubfeqbeeqXgOideuigd+f69/7w+v3z+f3n6/vf8Prp4vnH6fjp3/jA5/fXyfRZr/JHvvFXr/FrwO9Rve/Due9fsu9lpu9fvu5Yse61o+xum+uLheqJkemgdOmnkuiUe+j3/P71/P75+/7u8/318vvj7/rc6/jb1/VXsvLHwPFzye5ipu2Mhu2ymux7kOyEjOmeg+emieagfuXo9/zk9fvn8Pvt6frw6PrY7/nV7Pnr5vnJ5Pfm3PfE1/a72PTO0fSz0/NKvfJeru+Twu5roO66su1osO3Eru2Nq+1qq+yMpetyoOuCm+utneqajOipjeehiOeihOeLhualfebx8Pvp7vrN7Pni5fnV5/fQ5vfg3vfV4/bD4Pa54vXPy/OCz/KnyfK0xvLTw/K+x/GzvvGHyfCpwfB6xu+guu/Kue+Mte97ku9pue7ItO68qe5ztO2ur+1Yu+yXsuxuseuKmOuqpOqWouqtk+mWk+iKjObs9/3n5fm55fa+3vbX3Pbk1/a02/Xg1PWv3/Oc3POt1/PWwPOS1vKj1PKd0fKtyvLPw/GcyPCKw/B7ve9Cve+Hue9pxO6Cwu5zwe6Dt+62sO6Zr+6ese1/r+2Eoe2Truyfq+yapuyCpuy6n+uOnuuZm+uYleuwlepypumhkeiSi+iRhefQ1/XR3PTay/SWy/O60PKZz/KPyvLIxvCVuu9bt++Lve13ue2zt+2msO2Ro+2ms+ysqux6luzDp+t5oOqmmuqNhuqxoumkn+m1nemviufl+fqo2/S6v/Cuue2yuOxBqZCiAAAHmUlEQVRo3u2ZZ1ATQRSAd+9ISALpxJgASYBAAoGIIEW6ShdRpAjYBQTF3nvvYu+9995777333nvvbcbNJSjqDBIu+0u/yeQ2JHnfvvf2lpsLsAG4QYr/4IYkAW6aNcPrQNEz1x3Kis4AWGm+I+FAXMKOZgAf5Igs17i4uC5xWW0gwITT+BquXbrEoYfroUgewILTlsW2trauroaHbY1oLO1HjrJIkjBxYoK/ra0/BgsJWeMXVyhbtkKNz+GZ46v6+6NRpMUlILKGwVE10glCRuuZboZxa0tbKlet4FvW7UgbBoQkAzSf7qb29Z3pbtk1Fj5dplarj7SG0HTaI4vabVeGJS2MbX5qtaxofZrPlKllD6MJyzlgm3kymWxeJFnkT63n+TWUHWluOUnmPRQwcROrSGoQjkv0S0ra6WSpPMC4xIYNG94W/3be7Eryi59nsf3F+TA7Nv5xZfhbA9z3x8fG3raxUCbru7LZXddDgvh11qhgsbE921ho+T5is9n7wiEC/IK4G7sre69lutJOx2b3bAf/lIDRPbuyl1a2yFXTXpRIN7FJQqKVxSjcazK7yeXybZZofb2Fcrmu3c9FlZHBKpSA0X3k8qPOgD4ddTr5QWdIUmGbj98xceLW6GYkMqBHxMFsXZ/R9B21u2XrsjsTVD8ytxzy93f1t03IWpdhmkF29exJBKRdLXtudfv6VD/C7/q5VfB1c/NVuz3cmQmh8V2uBeo1jlud+4g62XnbusbGJx7edXd/YlLDxE3U/DO6GaZA10FMQpLOhKEj9ZfK2Y/bRRC8NZ2XstkLK0OqXlyuqCNNB4yYw+VeGWUYEbuzq9uPIiCDALzOOl31zkiMzPbdRXt5NCUt7Lmik+5GnUg0iSBIBlpVa46KuA+oGkbM6S46GEFTMrZfd9GD2tR+eM6zXwdIFY7kzUj27E31m5wkEtm3oCnpkOyZvJsqTItTjpqbJonTDEfH3uWNi1jUvd9YQI/dyZ6eHahR+Wdz7aYaJUB83G7uceP/F5Rqvw40N65ZDp6vxxqHT728ztYBFBtSvRTTWNTyGn7O02EypCURL0GSesZxp9UBAYuGoQFrzMWAnNQNxl3ZubeDwwweSrDUUCHOuQNqpt7LlTlD399v4NFjYEqO8piAcgDBEgeHWTa0UqnTF3XYGRrjrb2qZWqZwcHMwEDtm5bwR0EdlohpSYafcnQ8LTZJQNSAYGsOh2PFCX7XFpggZ6BPeNOSVDqlcTwuAIXUndJ4SH5+0/Mx3uAH0zSavuUBLUl/jeZEbVAIhGGtoqJahRWd+GQ7+hI7uxPFXvUgiV3fOnQluSiTv0gu0JQsU+SeKV4yLdfuGb1yDV+mUJwVg2IgvubOXUFPUueCQrHCu9iN5zmahpDeEr7gpSh+noIzXl4rhtGRlF+U57WsU7Gri9cp1Svvm7D0DsGTvIDUTiwGo7ie8Cak5uV9KfXFPby/Wh8wlQeRpNhcpq4OWD2htJIxq/T6F8K/XrpB4SJ9wKWRsFR9ES4fOnTVyJJ8de2qgJRFAkiWQuIxNGVoNaIkEkajnJxrDUAp8H6fknKpEigRLS+9TVkuKIWkwXWl8hgLlAhWD+Xba2PMd7COKQOve5gzo2rm92TY1cDAgS1BCWk5UKt9421+tYIDtQOEJT5tl2sDB1YxW9KIydT2MLUEAlDOpe1vxnJ1yxVtipbJjIHmtmQBk8lsZIzmUsWjWs1aAz0A/CXVWjWreVRxMZogmlN6NbO3rQFMCXMCyxi/SbB1OpM5oMg1CQTCd+np6RUHF5omMCWcBSwzJXVrSSTMxii+dXq6tbVVRYlEEtyI8VNC7Am2tkJYI5rUqjl7AfpAYzPPFOgzGMXgSK2tJFYca4MFvWoSw4KF73sMNhnQgcORSjkVKwatNHfDbzVEKg0JCUHf5+cPvrxgT0xTDidoyOxhpjxnD+FU5Kyc0qtx0yZ8qTQoKCgESQa5mClpiyR8fn7Txr2mbGwZRkA4n68KUfFXzt8cFbV59kopPyjIKgYwwnzax/T6MKhJiJSvUpkvaUrFbxWG+gARwOcyP0RVRsUPLcjn81VIqDqP3jMABcg0/8Og/EE+ZvZEGOUj4KEj9YJ6rnKZH1pGFVqmTCg/DT2FnvcpXNIkOjLQQt8sBLRp9aoARUeElklLK5jvQsktTrmNrz4WpKWlhRZ87BVlQxBIggOWT/tb27ffau/CMpURG7AwPE6HUQLx5QE/rdu6NTocp4R0isxyPXAgIWsEga9UZHQNf1uEa9UR+IpVuSr6eWNxgm9Z6lcNPPB2+smS7o2InI4OmwAm1uyPj98XDqD74aT4fWKAh/o9DTehAQnX62IXugM8oLvAC1tQvekjRze28TCqD/cklQC6nWdfD+Chnr3oSn3KdkV0sg7AQ8Qcz+Q7EEBwJzl5lg3AxFSNZokzAM6nHTUdAC5G9rfrfxOAGy9f9h0OcFH7uSL3aZj3mdy503gEwAW695h6o1OeYtlIiE0CBS/0+osX9fonNgQ+CRhzLUefoly1FiAJNmx6aAOVykYsAqcEtL0qYQ6oC5EDJ3usBrcHuBHW3M4A2KkrBNiBEAL8QPAP8x0ZyfbHp+5ubwAAAABJRU5ErkJggg==",iconType:"img",iconClass:"rounded-md",platforms:["mac","ios"],url:`shadowrocket://add/sub://${i}?remark=${n}`},{name:"QuantumultX",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAClFBMVEUaI1MeJVcdJ1geKFscJVQfKV0eKVkbJFYbJVUeKFoZI1EfJlgcJVcaJFIdJlcCgeoAdeQeKVseJlUAcOIAZt0Ae+gdJFUBU9MCh+4ATtEcI1IBlPMDjO4Aat8BVtUBbeMAWNYAQ8wAZNsAWtgAUNEAfegAeecdKFkaI1QBf+cBXdkdJFQBjvEAd+YAc+QAP8kDiewAZd0BYNoATdEASs8ARcwAhOwAbOEAYtwCWNcCSc0BmvYCgusBZ94BUdIEkfECiu4BgOgZJVcAYNwAlvUCj+8Ehe0AfOobKV0BhuwBXdoBW9gATM8CkvIAkPEBVNQAR80BQsoAQcoZKFwYJVUCnfcFj/EAaN8ZLGUYK2EcI1MCceQAauEYL2cAmPcCX9sBXdcAR8sWO3sWL3kCnvoAmfUAdOIAV9QXKW0cKWEYKl0YJVsaJ1oDYdQCStAPfc0BPcUEQL8WVZgYPHcZN3EZM2sFl/UClfUBfuoGadcBSc8HY84FO7cPVK4SOpEVLnMBi/AAcOQJiuESYrITXK8MQqoLOqcOQKQOMY0XN3sYP3oXNXUXJl8Bh/AEle4IkOYJgN8EctwPgNEMYL4Sc70RTaoQRqQSUZ8LNJ0URYEWPYETNX0WOHkWJ2MCmvgJhOQAcuIHfOEIed4LhtoLgNkGbdgKX8cESccRd8YNasMSbsIJVL8QZ74CObwNW7kTZ7YUaa4KRK4QWasQS6cSWKUUWaAOQJ0SP5YQN5YWRIkSOIcSLIEXMncVKWgXMWMKiugEddgCVNAFW88EWM0GVskGUMUFTMMFQsIMU7cIP7IHOa8UYKkNM6QPOp4QTZ0USJUYTpMRM4UUK3gVMW0AeukPhNEPdNAET8UCSL8IRroKTbkOMJjfGjaeAAAJHklEQVRo3u2Z91/TQBjGA1ZoC01pkFIDyhBoS5GNokVGWUVxtihDUBmCgKAMle3ee++999577739Z3wvZ5u2pLVq+M2Hz901b+7yzfNeEjIIWW9u9QJZrZOxrV3JuFtOiEyGIdCysX+B9HbgpDc/AoiVA6chMhtXMoeQXqwgPb2cE/S06e9wJEB6XBgiElmGuCOO1+OYiHtJRKDKzc12AEQcQ9xEjmPsEvxy4MQxxXEML7FOnBZOwd+IE8LtSzatqqpKjyM8Q9gZyv/Rfqr9aj4i8grBLnCZtnVGVNS4qKu9e/EOYY+zslPAGBc0s+wvnOA9ZfcZ1bYFajACjLFBQQmv9G4ii75Ma1nYsLkQaATeW6ihoJotrJ+ymYBISEiY8rXMDUbgYVDjM4ItoG5RAkJoCAj/wjVbsNymLR6LGGOGxucu0Zt64fVKaNzMRenq6qqEqOUGAOKUyp4gxJj43Ny4I2UO+gEEZF7Cjm0gSu6RYCQIuYjPjevbN3mJzMNZCHJpgih/hTghSsYIMOoYRHLIo5uU0g5ByUIstke4OiE3USfYqIuL6xuSnOw7uv9mPdqY83IOol9SVwcuQkJ8fSsrAwc83Em5ufIOmXYOu0CIwAEjwi/oPXoCkgwuIFGVlYAIH35/mYeI/3Qt8kVzEcjYGB4bm7hJxr8Tt07f0cAYghCTgBEzfRnFO8S161F/YECmYtMBEdNnQkc1yTcEDq/AQOwiODimz7AJE6eXuor4hICUHjcfTEYuMKLfxIDBHdVKXiEgSnYhfVJ6IsPo129wgPe8Q6Uk3xCSXHY/0WQDGEWRXh3VFMkrBGS4EBwMCHAR4O3tHenlta6UcuUbQq1o6zOByVRR0dSpXgPDfBbKeHfi0WsTQmAXXmE+Ptl3l1N8Q0TUshbIFHYR5pMdGipdoCfJv4E4uu7pO8AF2BgINkLT1Oq0fdvllHMQD0uRLghkJbSMW5edLV4mF3OkUumgVQtrSYvutkMhQpJMS7g4KXSAdQxkXGSHpkmlSYOyVn0uJl1JJ8b+AURE7jwU5hMKk5EGiByFYu7aJiHPEFDteR+fNOwiJzo1NWJPqdw+hCT/HOIB0jTeVacxCEV0qn/EqD2NjiB/7gTLcP5XpqL9/SMyUw42e/KZLixSWLo/K0uhSAUb40eN8juuJ/mHUFSvBatQpiBVKSNHprx1cekJiKZxrb8/sjHSz8+vvhhml28IiKxdEBEBNvz8ZmesbsAxniEg4fI9kClAzNKuv01BgH8IqHYBZCpjVkXFrAZPNsozhG6cPzujQqvNW18sxxHeISDRmZKKCm2eljXSAxB503pteXnexgLSAwf4h4Co5hf1RxsK4CpPOnfcuxCetoI4rjkFqwQqo95g9LQ+RX4NNQ82b8qFcvEk3LtJIEC1RX6w0PErMPUQMq2c2RaKy5llTwq2CK2QWc+2BG6whLYQudBTX1Dc3NTUuLxJCc5tBmN5ujDLQgSBXYcWL1tAUIsK1CDUsrWu+e3Zo/XrD8yfv3r1mrPFGnCAB7L7ai9itYZgfgvQb2iETItrgaag4WAmnHglJSXa3XnleWd19L9CULHyISw+Ex3BXApnZ5RUaHeXr2kibAaDWIADEAtB67ERDMk/mZqKLrgp6DKCzvHV2/4G4o4h3V1AkRsuwv9Y/N8JY7RrGmmBzQYwio2gJc50CbAT3F+Ia6FAdW0fuluIBgrGzC7ZqEfr8UY0co1GI5SjP7lAIIfKMYRTdPExaVJS0iCFQgE5Qxi/+aUAwHInapubi/MLDLU6IxygEgldqNEIgCxHDAySC1jZgRi3ZKvTkqSAyVJgN5kN7i7m1cs3HjhwsP7o8ZMLFl7c8uba9tIVTQg6zaAkSUpDCQQURf0Wotq+Ljt7jpS5xcrJUaTOnRtxvMA8jlg+P09bkuGXMn5uqiIpTb1v/7pDLW2HD5942rFp8+Yr17sMABH8DkLnb/AK+5idrVbjOzl0s9hYaB6n21iep62YPTLTPzo6S5qm9gnzivQOmDghJjFx0vABgfceLyqrFfwWInztDbfWA9Gt9RyEycnJueRpTrKm+hNAMvwQRJElVYcCZK/34H7D+iROCh8wpL9vSNyRTsPvIMTOlnlFpmcEdRpgco7ly9mZrK4HyCy/UTaQPmZI37qhrwyOIXTB6YCAefC4gzEwN0n7S1WWPS7lIUhKpr/CChITHDsZQfr2jatLWKqjHUGMb1r6BaDHz0jmoQoo6i06tMLkRXO7vlybYQWJtIbktrbO7HLgxL1wRdsweOUwGNwUIQxQTuZrrPqIt63ZXQIQSNcgBBmIJh5DcLpyh04JWrzSPkS162kMPK4Pw4/rRYiybjubLGxF8GJWRndIHzMkvjUhKGqpu12I8crkdPatAPOsC8myESTMxgme+PTwXxBwEtTeZQ9Cr3gIr4JiLTCRG/LZ1WzC7nBChrOQsVEvV5oglhIINdWbJqNXc+nYDUzNvJZSmugu46XMkbbpYiG5DGTGDZWk+0hIVuc906szRIGZ6ffakwtCV58ZP747JNwSErXVnRPSdYJ5Qcdg0hPRAXC6QCVUcVAk+QvvRKSuYtOFJ94K8pITYljEvmpElGB4DyjmhhAq3bZja3OykqRzsuE8KQpAl5XY8AGB5jkZF7VU0h1SKLmemxyS7Is4KGnD0xMTN+tQPy4nhERcu+Jaw8XzC09v2HC4ra1tOmhyYCC+rIxJCBr3/JaYA1L1LY55UQ6U/phyIl8CccKOJIU0sHQ1huqCguIPH27u2HG9s/PK5ctLFp171t6+tUrFMaRmcSt8uWBeM2MzIx7sEAPCvlRCdNJoCAlILJbQYjFNqFRGXU3Nyl0rdVy7Rb8bmzAFvizEMxRfMFN52VhY6BDS/TzAQlRouIwEwfcL5jsMwoweXfn9FsGzJLva4bsY4oAblDPfLzuMvENWPodPY+jDFcaEPH7nTvAu4dIZUfgL3JjW1vgji7pUEv4hkpqlp2aAZoKeXS2roWkI8k/RVd248f59VdWtXTUSGkT0iGgNHHnokKfFNG3UqYgekUBl5tE6gPS4VCriv/7rvxiJf4krbmq5fkvEEmhx0LblDcLVsgEeIBLkxA5EYr0xCer7E/a5ifsRqUkJAAAAAElFTkSuQmCC",iconType:"img",iconClass:"rounded-md",platforms:["mac","ios"],url:(()=>{const e={server_remote:[`${t}, tag=${r.title||"订阅"}`]};return`quantumult-x:///update-configuration?remote-resource=${encodeURIComponent(JSON.stringify(e))}`})()},{name:"Surge",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAC8VBMVEX///////7+//+2afRfle+yavNhlO9yjPBrj/Cic/CYefC3aPP9+/2ncPGWevD8/f5kk/CfdfCqb/Bdlu6vbfNvjfBbmO7+//79/P53ifB/hfBpkO+5aPRYmPCTe/B8hvB0ivClc/COffGxa/OcdvBnkvBclO58hO7+/f+tvPOKf/Cmb+78+P/BsfN6h/GFgvCRfPB5iO9nju5ziO6Ree6/1PaAgu+xau9Ylu52h+7fw/isbfOwafK0yvHRuvGad/FjlPBsjvCddfBvju+Xeu+fce+Ifu5lkO36/v71+v2vuvPJrfO3ZvOsb/GCg/CqbfC4Z/DMme9nke+ic++/oe5wi+6Gg+5tjO2VeO25ZPS8tPO7xfGBhPB2iu6adO6kcO5qje3z6PrEr/Oua/OwbfG0aPF5iPCgdPDInO/Fju9+hu+Oe++MfO6QuO2Wt+2cs+1XmO34+/74+Pzk7vqyufS1t/TatvO4tfPNvPF3he/Dnu6DgO6Xd+6hsO1dlOygc+z38Py3tvTbs/SpvvOrb/OxzPG4x/G+w/HXt/FflPHHi/CHgfC2afCBsO+Ere6Xte2qbu12m+z5/f719/3v9f359P3K3fe6tfPHrvOkcvGdc++6o+5xiu6Ofe6ubO5gku1gmuz9/f3W5/jozvfa2/bH2Pbdu/W10/PLrPPJv/K/s/LMq/KtzPGgvPHVufHLpfFkkPGpb/FbmPBdlvDNkfC6jO+Hse6kru6wfu6enO2HqOx0qOvy8fzp8fvn5fry4Prv2frq1frl4vnq2vnR4PjW4ffW0/fk0vfkyPbhy/XUwvXev/W9zvPGr/PBw/JVmvK9a/HRnfDAdPCLtu+rr+9tjO+Tve7AfO6Cou1mou1mle2ciu2ocu23be2ml+y0e+yJjuq0hOrd6/vb6/ng2ffYpvS4aPS6v/PIuPPNq/PIzPKixPGWsPG5qvFpkPHCgfHEmvDEhPCZg/Coy++4pu9rpe+8ie+pmu5tlex8oOuVkusvF8k2AAAGQklEQVRo3u3ZZVhTURzH8XOvTKcwhpswVFKHTKc4EAGdioJiMUBULERRFAWDsDGwAwu7u7u7u7u7u1tf+T/n7t5d1MdHONvz+GLfl7zgw7mX83sIZM2aNWv/VwzDIEsHiF1ZjYUdNn7Qpk2P92mQJdv3vhl04ZgllcyOUS4uLk/Cr+5FlmvhbUCgqEHIci0ZHeVia+vi0uy8M7JYg9pF2RKkgXkR58mT5SYkcLQtzryI5uDazZvXHtSwJiQfFG5GhGGOb9Pq9ZHbjiOuHoGj85kdib+vHa8wGAz343kkMJ9EYl4EndDekikMQwzjl+RE2uVEWCpkWSutbMh6hUL7XEAkOBOiyty/cG9ZKqR+K0+ZTKaQaVcakTkJORDG7uTbc+cunI+nQhp6egIjICPmJJQsWVIiqcIjJ2+H4wHomEmD1AIFEpDxhiIE6cAh8VfD4W6CsoQOaViwYEHPVst4JLIIjkcWcnfTpdkmO3MiCj1G2hqRyu1s82EkahTFu59Wq3AiIA0b1kcsQbQKvV4vQiRwM4GhQk7P4JBaRmSNVjFErx+iF5CSHBJOg7SZURiXKEYUCoWhbQc7hiBV4CgQIGzekbiQEKwAggjSSrZeAUX2ERCJhBrpW1ONlRnTEKnaHxAyMx0pkHJ9axYKVhcOiTMhkBjBt0ZCjxRSh5iQAb8j5HJSIU1rghKsjjvNI54ynIAEJpAFCKRGoLg2PFJwOl4zLY/MSShSBBi6k/i7YaRmXxECA+CpbcQhR28ZEvgFoED83ezt7QnCCgjMDCD4A5mbI4mScAzRIDqM2DfFCIuRxIJ4A6YDwmL16LZIfdu2c9ZOzt2PDvKJaRrEIK4VyTqdGzD+5YyPK04NtwbOYnxcrGb/u7t3v/SYnKtfKOYvurR9+9Q9yFjzTrNBsXcDhOWQ4BCM1Gpkp0JcafHxmfBVQf96Ds2LewEBGRk/FiGWYTgkuYTOTUB6xQXDrUlMNCEsy336XBzlwD3v2NiIiIDtu/DXRpASJUok6wSkabBaDU+MIHntaW+fYsViI2JjjiDyXqaGAYIZAYGbCWs2gwKZfwkQKCLgDHf8nmFhZTDSaQWP2BcKxoehQORbe/skJbm7F/sbglPPzDviDIivLyDew3kkpgyuU3OExEgwHdIeI+4CkhGThZEwI7Lc3x6iQ+T9KrVs7+ODEUSQgRkxMVlZYsSNKH1n2jF5Ryq0LFq0vQgJiClfvnxWlgiBgPlEiYDiI0agmLCpiHREpyNK02cMFeKBlUotOKR7QGwERjKMyK47ZMz8bx6gQWYV8MCKgHgnxUYAk9HTOJ+LbnZK1iV/OCFX5ebFs5PSd6cyKgFpDIqHgHTzToIFgJ0xIirVnuZf77x+5Ixyg0ysei07+9VhOSI5d23cuADkUUGMABMwEPFp0tJy+dLTVt9wcFi3LnsKjzQJ/QVxJ4qAkGem0uQKWXyjenUHBy+vHZO4xwWIo6MIqVcJriYg3t1RXmPkXUrlJ8i3QwhXp2trqSNWGpsQXx/3YkkUCErbAggo0dEVjScBBMqJwALQIHXHlhoJCMQjflJpjRqOjk2G8UhLsyD5cWJEKkJKk5lp79u7GzUCGZFxfsWxUqO1CIF8K1EiNhCP1AEEJxUhHngCqJAxgOAEpHZ0TmRWgQIEqUeDBG1UKm2UNjYmxMsLI1WNCF4AOEsFMyBKZU6kuB+PkAXwKECLODk5mZAutaPx3RQjoXA3aZENThCPyLvMdcCIV20xAsosCiT1omuKqysg86bwCFkAARncWhpKFqA0yntLgzakABK0M53hkFI2eMwcBMQPX03HUCpk0hZX1w0pKfOWskhAIIwggnAzc7kJDYLSr1x3mrdzaSqH1CEINFdAyAKE0iEo9eGUQ7s1jIpDOpci91+MEOUyHcIwoj9UTjAiNv2HItIpWABAirc+hSgDQ4zA/ReQ9O+1i7+RSv2upSPqxIhSjKgWZ0evi/bLXsyaEwlSwq3hEUizYPWOHasPs2ZE5J2DnDCi7L9KeGfzJ06UI8isCCyAEiMMI3xnMGZF6lwhCBxmqMX+18cwq1LOuuJgy8CwUA8+c8jYSchisaqK112DUlJ2LkCWTLXg5ZaLH9NVlv6faGqqcWwsH4usWbNm7f/pJ40LCPiotLN6AAAAAElFTkSuQmCC",iconType:"img",iconClass:"rounded-md",platforms:["mac","ios"],url:`surge:///install-config?url=${o}&name=${n}`},{name:"Stash",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAADAFBMVEX////9/f38+/v5+fj6+vr09fT29vb49/f8/Pz3+Pju7u7y8vIHWenx8fDt7ewJZOvw8PAVfvH08/MUe/EGYOkPcO4HXekmsfwWgfILZ+wJbu4kpfsQc+7m5uYJae0hmvcJWuspuP4ekvYbkPQLa+zq6ukjxf8nwv8hl/fr6+smyP8kvf8LXukotf4oz/8HZusJYerh4eAjofkMa+8Mcu4Zh/URde8ag/Po6OgUePHd3d0s1v8djvYObu4HV+fW1tcq0/4nqfwknfgelvYfivQWw/8mrv4hqPsDgO0mq/0Nee8iyv8drPsnp/sNr/ohovoBnvUClPIjwP8jrf0Fh+nj5OM94P8z2P8jzf843f4Uyf4GqPkcifUOgfAEiO8MYusG2f0Dg/AOfe8v0f4C0/0Muvwgn/kXhPQCjvARdvAD3/4Ds/IWr/gCeesD1vcUh/Dk6e6gwOza2toA2f0Tv/0Fo/gBmfMYjPIGjunx+PvW4OoDgePS09MJ5f8W0P9C4f7x8/SHr+sgeOrg5ughZ+LP0NAx2/8i0/8Mzv8E0fcfnffr8/Y2pfXV5fTp7vCTuOs0gOkLZuZ0oeU4eeNK5v8T1//l9fsQtfvZ7/hPv/MumvLP4vDD3O+pzu/K3e7d4+zH1ehUiOLO1twj2P4ou/y53vdMsPV+wvMQsfOV2fK20O9truy71+p8r+o/m+lmlOIg4/9FyfmE3/VuyvXh6/TF6PRZzvMxsvGTw+8tjuwFcOoVcOlNkOhEgeYxcuOAqd/FztEywfkEufnK7fih3PSVzfIMu/Lb5/C35O+Fuu4jcegVYeTHx8kw5P142fe91fGr1/APnvACqu9Moe6sxuofgul70uilvti6y9Y76f8P3v4Gxfhauvfm8PZq4vaP4vVBuPWAzPFxvPFAqu5moexfreq1xuDZ3d+Uu9ubtMhC5v8MyP5P2vtW4vkem/ir5vWe5PWr3vU1zvM7ku9s0uhboeas0t04id2xwtNI1/OUz+Bsrd96odS6wcVzvuN1vNeI7X81AAAQf0lEQVRo3uzXfUzMcRwHcNzd7552t5/zBxvNaB1p2mo7TtfmyMOMPJRKedhZp4idJk9nyNaUW/FXcSGsRwlTuS7/6QFnUwtj8tCDh6EHkZiYh/f3+z1dHqbu5D9vf/in/V69P5/vt9/dsP/5nwEz/Mf8G2DYj/+GmmLAz2HUkBH9BMkIFsnQDq6PoA8XCqQCRMgYZ8UhqoECQrPVVlCLFNis6Rwg6jBoKGrAMNcUl2dXTcjP9/OrqiotL7Y94sRSIRiWoRiVuaC8ZfTUqRMm+Pn5hU0KU6vVpeWORxwHhhKeKzDYtIQ15XYQMAgyaZJ6klo9ZUp3vU0h4gRCCVP+7nJIzLUt4aNHQ6FI2Hdk4cLS14dUKCNhP/dXTQ632heFE+UXZEVs2UWm0HhcBEZRaHj4vHn9EAQGlBX+/vVWucjjLqwGM2YtAPJTExDE8I+tv9ineIQgEvOV0PmzZi1gCBQgMICwIrH62OZDcg6KZ1XYHXyTMTYUSPgCF8JWAoMi+t43vGv7Hm3EWrhkbCibF0Py/cLCnAg1kI4ahUgsYIon19BcFDiWIlBolan5SJjahej0uqiyRCjkVqK9B9MqyABC5wUEyW690tBQ3Fo6Rf29iE4XFdXrkPUt3/1pmR/HBQYyhKy+pfV4itbHxzfxWHGp2oWkptZfVABxvwq96gUZcYFLnFXmLShsSNH6KpUymYz3dVRQREeKREd3OHgV2YoHTSTSoldAoNDVPzzrpfWV8QqVSqRS8LYKen5hpEZHR5cdYlXcVMg7yvrkVVzcEraV8MKzXj5KXi7ixGIxx4nkUPTOIpGRncdkck5AEHdXIsmZ/CpudiDdSmhGjpeWGuS9KJWKOXlXqZ4Z0cmRyV1K3rl6t9ceMn72bChkLUUniYEH4T0lIe9gLr0slhooEplclsiTU+z+uKy3Q8aPBxMHpvA49gFDwF6FVKnpAMKM5KZjMhWZF+LeSgqOhkyGQssURfjAELPHMEW8rxlLZ0Zyp0Op4DyY14hK78kIYeIysHVehSJ4ikup6UhlRmZm5msfnpwvd5uYS7xDnMjsZ7u8lAoVfaOzkLVw6U1OAylLlKncWgoMKIdve0+c6FQqI7RKBU6vVNAXHLB9r6MjwbS1AWm6ppRzbiL4XQ++BcKYo2ci2PntH5FIYUuuq6trbGxra2vstOG3ELjZBHu/5O1NEOT2qQDcRNx2Bc/LeB7/K3hEebDzAkKcxkyHVqaSCt1sMiJnjjcUJCTk2fOggJRdNseb2tpiktoGx4ldKVptSlPCHhpIXdgaJ5C4hYwQVq4KJiFMyfuPd3squvV6vNRXxMTMnRtW1ZJd3ppzFghLQkKzly/uoztVhksE0uub58wJDqZt7N0Wi9FozMpKS/P3XxgT0z5369Z11dWj7fb2pARnygJ8eHaRBl9EwJ3bPAfZEQxoo4UgSTDS9KiCLkRZd2Tx4k2Lqx9YTAaDIenu8whfBTvkg96IdF9JvGYVFNTxvmqxmJKSDAaC6Mm8gKyjyOKVSPUDo8nYkxvkpZRTZLCXRChOfxo/XaNZBWhV8FWLyQQjKy9PFxu792dk29JtGx9YKi7PDPCRiQTC4cggatBpPXo3Zjqi0WiAwHAiev3eAxTZyhAY25Yit7I/70cVFdkKMpBBj5bY2nyUIvHxGs0OiiTdvw9ER5CYX5Dty0I+fUAVnv6VxCMGMjAsc1en5ebIMTTx0zcTxJBw/0JeXpRO1w9xGduXLdt5/kVuhBJ/4Ab6MOn8WmVtTk0y3hxJQqWrOD8ESU2NitLtZfNyIcxYtn79jk+ncO05bIXmT99HJMKapiyDgSIsQGAQBEnbazzQ3u5C2LAosmb5+RzcSOkfv3yxT7/mro68LAOajOrLaZMJRl1vRc9dBJc/u7Rqa3U1EGcRQgBZs/xSZaLij1++2Lk63Byly8PlsjiRGchNU3dvz8f39y7v3717JpKbe+/9xy/Z9k20CBsWDGTtnXPXVBw7YmB+u3MY+HigS8vKMhpP0+dPm7Zhw7hLX19+vrF/98wtW4KCAmiCtmyZefnDVztBmEEEZPXmkoP4SAOF5vfGt17NNabNKozjSunAt6Ut0FZa24LSGqs1LbbY2gjEMUWJZlFG4ochEZcRsi1QPshkC4HSxQUGpW2iDhgXo9uEAVGJjruXIAGMqIOwxJnoNnGLiVGXOeMt/p9zerUx0S/+tyVLOO/7e//n8pznebZPkHpyyL6a+yEdpAoRo6TRXVCp17uY9PrKAnfJ+a/2fQ4jMQYgVVWXuyTMC7eSvB4vfULF03Mvthzds+ejGgI4VNDKVTDcuFPMLEnFpSLIZK5K98/vfrBv3+fPxxiPPgpIJiixmzrRCPOBDJfU0rJnzxf1Dh0A2qKiIuPvHx9pLHDJhAyJmAuZqmCq/PX0y4xCEIbgkMzLSxJxhPJ3Iy+8RVUHCqinW44ePfrFQZVDVVRkhwzDHx926ynxitzylEIK7Rf373/5NCifPwkGCFBNTU1mTfYYKhYRp8QxSKk/vH831ZvgtLQA8tl0ERhGu0Fjnf+lBIEJSbUoNTXSxhEr+k4+vn8/IMA8uQuAAwcOgJGN315/tyQtkcKNpH/4JpWbxMG9dPS7n0ZX7HajQaORy+WrCwSR8BNASk8ViRWfPlQBCsOcvn7jAKkcwq6s6xlRiEGJnzBer799J4mBQHnz5/Mhg1GjsYKhVA+z20Icl9yJxN0Xyx4HBJTT+y5dvQoKh2Df15XPLkUTzngjP7yPehN9AMLc+fA7Z9wlE1bGqLYp83ybblx8YchNHNJ/qawMGGbmtz8+3rwxzSA4Wdj5M35Kz+OtsDr3OLoAJAI9/E4/TtyVGY0cDItSLS2eKClwCQhLMSetpzp23gYIYR6/grO6NTftgHR85/cM8GoVijJS30DxDHHS8TOVOHdDIbJhYxDflhtxHNlIdE36Lu3duRNWSJd+OXK4sWC9hygqrpmx7oiVOCP3kAh0z1Pvf6pHAWpqD9rkNqXaYpFKmRUTRdiokeXcDlC4lz/fA0Pv8ni1Wi0RsC21sEL3ZBwk9Ud05CJ67et2HApBZl732ZRKtToPEKwKv13TuZFb+sfv4hQszLHNwyUUDyaD01qS3Uh//ArKXqIQxPe30W3guuf24wOoeCgTHRwmBmxI89TYYEgUMM3pxEibHL0DkDDlm/dKGispjx3oMRiNeD/JMNsQl+2R+w+/4gTqbNy7qDcJGWKxRCJ4VtU0VcV5UotldYFlVpgBpADixd7S0u87OjqoOj52BUEHj2RJMtoChqhW1li1GoP8iLffG9bxAZS5WUjbs2AFJjBXeRabzTq/VaCX0e5HKdc/np9fWprLvcAIiwd4pC9k1YRlDfgFKryjENEi+ibQraRTDWaBytw0sUThWWUMtdImt1qHkdwLrIBYul7fRPkryorvO3qvHG6sNOOzUHq3Tiix661M8qBMwaY3UlW9fWtMJ1xUVeGDEQQHh6URiMZgnKBlkWSJJ0dzagubgMkvhZgRqlrxRJonYKuulpOqlcPtAnZKFNJ5gXqMj0AVj7x7xsTCbSqqQrGw7gPEYoERQGaCoMiEhrGNnBxnIWGg3k0yQt+MJ0RrPqUFstkQjOYGsSgxyEsXKgBgkIqTWBJcOoihoEgagsXwoayWazTYlzML2Krtbd5ySpVqkVwC8xs3QsETT3TNq9VqJWSzKUPN8ZCULy9UVDyG9hxUdrLZRDU5QjpOg1gYmGOThdkCxOFdePXVBa+jLptRnKCMny9xV5p4awVndHI+T81ksahDzbJESBkQD1HLqQxOqOeDSwPPpElk6z4lLbuBToDK4Q1OzGgduvvLaximduoKnXX2Vawo7pqX0tkFRP13SOcFdDMBKYOOYU0ieTOsKBoWVpVyBilSqXR1RissgfJANrlxLhxhNw0/pHjT2qqUC3FibjABIr4ICBC3kc668DPe30intW+esMgDNFsUlKxKq5VwDl15OW7a65gsimmornm4aStmwo7Mk2J3xSB41+ITEADQzpPN/KJNZ8XQtgzz0JxSrrHbYUSDqYYrWh6ilM/+wiYrnJyk35wyOQeAlGOKg6YESOrrx8IQdJ62nzULbAunh62YtkJWgmiUiC9qzJ0mTLmxySYLFwfbJ2jDtVmkzAShVj0yIStuulJeOclM7CRGxyWkzfw5FqYyZPr1kMFu1CgpkLGtZqDJc4SuUoR3CQwCpYjWfbTghIGd+SFzwolPaT11H9PevXu35+aOexqokyIiIbgI5nb/isEQsFHQpzBms1phxYtsjIwIEgoPmN9O/wodEYs6j1Q80SBLiF3pIjRnibGdGLmlvaMjk9RLgQBZWhubm7Frqm2YCwR9Cz8204du/L756rd6mSKDtQy/HJnw4QMA4W5WPWYBX5oSg6R2XtwOABFy0ecoze/9ZvlEf1dXV9+aZ2zWq9NqjRq5DXdkMZsxRKfpQ9DGbNB/or+vr6//RNvsipUiVjW80CkpHm42K/hRiGaosLI9xkAXIr9px9S1a9emNjay6+7XqbR2jbyaFoVBlGCQKKHz9kDeGY0B0Z3nNpDF4vOY2WylxyDpos5TuQmMBx/cXVhYW5tTVYMaRacqsmsCcnwjD8qBA4cO7dq1i+WlSIIcDi2sGjUGRqm2kJmJQbMirsPK86htXefCCMbIf3D37t2FtTk5yGwfqGMQPl8UNAJE4BD6BDh1IDE3UrrJKHATGjIxI/GJBLb4tk+PAcEZ6HXsAKPQSZDyB+4niAHzhSAOyPSug5xR9WxVGAJKEZIHg4FTbD4PMeK73rysFrUu9tJUkQ8OYYzMbOSEBMHKy202tSVw6CBEkHpWJ2Rn1xFFBQpSZ547+9pwu0bbifFJvajzLLKDCGMHJotDsnW6MASLMv/7wvhB0jP1z9RX0c8zuRVUMlQCMIpvYdAsk/DQlJgMI4J0Lvbm35EPEYMg/B1YWLwCkMB8cOvwkfNjhKmHnDlEoeKVVoUVMzBjXWkbNAmsLwUG9LeGSuvIuXzqcYGBBeFG8KG0rlprIBTc+tbtdjd+e2ZsvKm+sN5JI6owIhsQjAEGkMCcp4GlO9xHohOipGV1LV9ramoiSHS2UAFji64M+4cq9UiRSZVDZ69P1dc6a8kKUSiRBwSrMh8cMrOaTJRUNeKvnCLu7F8e3+EsZAwnN+LQeWfHRppNZrPZROkrZDYNri2fm3JWVRGEBsEucoCe4Eg7hmQkMTgF4pSs7j7P6LmpHc4c2jqZGz2zl/0jzS4Tb6dKWrOy8FsikSi6l9bOjs72bGDVYFbn9c5d9gy4ZDKFohU3BTGSK9OoF1S23Uv9JzzLy21tfs/IUHODWSYDQSHJopjJhfySGrfdgwMjHn8b5F8/0+wym1Eh0yhRMoMU7cGjsEVSmyEI3QIkY++XSAiwjbW4UyFW/m6jz5EgL2ejumUCxD+E8ilO4IxkLyhs6XG8N4OLAGSBAOwCZKMgkFDRhwdSR7pVzAlAJPpI9oIX3ML/TYkEQLhJDwAfETeQOcLUkTCOIzAuyUeymZTwlECgiSIOksQregzkIqv/4j80xD/OUfQUFCNgTKLww5SIooSkbleymX8Q/1lsWPLAf4Ug0Zh/9SSn/ncElPihyR6SvyjpFwb+//oLYHj/LyqNdWsAAAAASUVORK5CYII=",iconType:"img",iconClass:"rounded-md",platforms:["mac","ios"],url:`stash://install-config?url=${o}&name=${n}`},{name:"NekoBox",icon:"data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFwAXAMBIgACEQEDEQH/xAAbAAACAgMBAAAAAAAAAAAAAAAABgQFAgMHAf/EAEAQAAEDAwIDBAUJBAsAAAAAAAEAAgMEBRESIQYxURNBYXEigZGhwQcUFSMkkrHC0TI1UvAWQkNiY3J0orKz4f/EABkBAAMBAQEAAAAAAAAAAAAAAAACBAMBBf/EACQRAAICAQIFBQAAAAAAAAAAAAABAhEDEjETITJBUQQUIiMz/9oADAMBAAIRAxEAPwDuKEIQAKPHXUctVJSR1cD6mLHaQtkBezIyMt5jYqQuAfKXTPj45rJImuMk0jCzTnVq0tG3q0rqVgd/QuY8HniqkhY2turuyxnsJgJXNHi87jyyUydtcnHP0jL91g/Kn4UjJ5ooakJZZd7hSnM2iqjHMEBr/URt6setSqbi/h+cY+laaJ42dHO8RuaehDsJXBrcaOSMti8QodPdbdUuDaavpZnHkI5muJ9hUxKOCEIQAIQhAAkS822E8WVNdLpdoa1zHfwEsAd/ta37xT2UlXU9pPWu73zaDnpqDT7gVpj6rM8rajS7mAlw+GAbOly946NGPjgepWGsDRnm7l7MqipnOkuUs2fQDQxu3QnPwU+onDaiJo/s2F59mB/PiqE01aI5RcXTN1PIJJaiM7gP29g/Vcy+USkNHeY5Ym7VTMkDveDg+4tXQbUXmaR7xjtJHOH+UgYUHiC1S3O70HYQ9o+HtHAZGxIbvulk/jaNMUfsSYqcD8Jurr/QOrgfqnCqdGP6rWEEZPUu0jHn0XdUr8D2qS3w1ktc0MrppcOYDnRE3IYM9+fSd5uI7k0KW7LZVfx2BCEIOAhCEACQ65xllcRqx28kp08yGh5/HSnKouNDTP0VNZTwv56ZJWtPsJXPpL5aKZ9LV1dRE5gB1BkrS5pc7ngHfHRMr0uheWuOrYvG2csL2Ubi9sEun03Z15jaSQfPO3LcjZaJbTUR656naJoc+TS7LnAD9kePTyVtbrrZ5aRj6K4URgI9HRK0AeruWVbXUUtO+NtdS4cMHEzf1WGuSN+HGTK6UTNuPZyRRsMQDD2ZOnIaDgZ6BwHq8cDGC40lFd/tNRHFJ2ZLBIcBwzvvy7lFdc4hWfaK2l0mQ+mZAMkjJPPHIAexVf0hb5uI6jtqmkfFoY0apGlp5k/iqsVOFEmZaMtjZaby2634imb9njp3gyA+i92pnLrj4pjStaLla2Vxd8/o2hsJA+uaAMkePgmhrg5oc0gg7gjvSSSTpD423G2eoQhKOC1VM8dLTy1E7tMUTC97ugAyVtSn8ode6ntUdIzI+cP+sdjYMbgnfxOkeWUsnpTZ1K3RTWCrkuD7lVzjEktXqI56fq2YHqGB6la6W9B7FQ8HkOo6xzSCDVHcH/DYr9eRNty5lVIx0MPNjfYsTHHjJjZ90LYsJTiN58Cks6VNJUht/qKfYRSsbob3BzQDt4kE/dVxgdEtVYdG19fG0l9PVB3ojJIDGZA826h61cm7W8HHzuP3p2r2AmFocMEDCsuF6smKW3SHMlMRo8Yzy9nLy09UuS323xjaVzz0ZG4/BRrfdKgXSG40tHWvjDtLhHSyODmHZwyG+GfMBbencoz25CTScTpSF4F6vTJgXhXqCgBNd+9ruetWP+qMLNYP/e93b3irHvhjPxWa8fN+jKo9KBRbjKIqV3V2wW+WWOFmuV7WN6kpcuNf87m9DIjbs0H8VmOkWdoayakn1tDmumO3kAPgpLqamGQ2Fod1I2UTh1wdRPHf2r/xKnuJaSzSXuJyOi69wCOnjG5hYD3eir7hhwdb5C3l28g96U7jcexYYm7Snng50j9UxcDHNhaes8v/ADKq9H1syzdIwoQheiTghCEAUlbw82orJ6qKuqKd85a57WNYWlwaG53GeQHf3KDNwtXPyGXtzR40/wCjgmlCzeKEnbQynJdxGm4Hr3nUbrDIer4HD85Wh3BN1b+xPRu83ub+UroCEvt8fgbiz8iRRcK3imiAE1G14c47SOI3JP8ACpEvDl6mbg3CljH9xjv/ABN6Fz22LwHFkIv9Bq487nTg/wCncfzpn4etbrPa2Uck7Z3Ne9xe1mgHU4nlk9eqs0J44oQdxQrm5bghCFoKf//Z",iconType:"img",platforms:["android"],url:`clash://install-config?url=${o}&name=${n}`},{name:"Surfboard",icon:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAAw1BMVEUD2sUAAAAC3McA2sUI28YBBAMA3cgCCAcDCwoGtaMFExIB1MAHIx8C18IDzroEwK0Nd2sGFxUB0LwGxLEKn5ADx7MLSEELQDoJNTAIJiIGHxsJ3cgLkoQNZlwMXVQI1cEJsaAKo5QKnI0LjoAMhnkLc2cMYFcKRj8JMSwILSgHGxkEEA4CzLgHuqcLin0LfXEMbGELTUYH0r4CyrcDvqsHrZwKqJgNWVELv6wNgnYMVU4Cwq8Mb2QKOzUJOTMIKiYMl4iTAtRRAAAC2ElEQVRo3u2Y6W7qMBCFkxnjQGmahUDZoQtQUgqUQgvd6Ps/1a10E0ImNdK1fX9U8vl7pDOx83k8iWVkZGRkZGRk9JsFvNPh8LPV+baYegmGXtf3u55VzAK3MvcXUR1QdRn1Xrtk26X2sgbUqrwG35Yz3a2YWo31pZ3oYw1566qVOKX+hUoVDBv2QZPZcRRs9wenNFLZMN51bFHU0s50fw3yRdxH+0hnURbF6vtja+qBNFn1ln2suyyKb+5z1ktVdsfAi+2c+oeozrWTc5xbUFxJFjVPo/hzOW+1h7JVBg07r/MKJPVrLWJdzpgkXTcOiWocjkSPONIcY/hIo/zDMd0T60yWY6ic06grSKyvjK8MPinBTZlENdMm5mbHUZFjdHs0qj/4G8Wekr6mzjHUmySqnHLMN7GtiWN+dSbiGBclXRxbfknAMa4edXHMLhpCjh9aFL6I6+OYJ1a3TDl+08dxPeW4CJ8sx4MxjRoPMIHvo8Axk1xK7Y5yfAOJ9Rzo4hj+ieMnjRyjiON3S5bjCY26BSHHG239eFK1RBwvJYtglR7JzxDTUYDW76PkSuYObexpklsAfMcld6tNN/4ZRLs1rTFN731E+pf6PIkjYScujgFLV+5OiYRn0fI1nUXwhF2F6+oqWH0p9EcXk6bWVL7nKb10YkF3rGligWG7eP0J6fVAE73+CXq5pv47ofRmWqL+iUgPvRSf03divJGkty++3T813e5sXpy3hXPKqyS9D+Ivh+t7PfRa7F1EL64munpvSLe9EbJ0jQGdHlB23DoX9d5O5Gia5+GtXaBXUCQecun/N036jZVdxwGlV1bYEw3sGF6q0ps9b0z+32RW1zmidw2WgnaHw+gscgausl4QREo10N0FSdCC7AibjZMHaH1ZakJr2GvGwV1vYyGx2CB6nQbxx2jNLFUBXnjbGcIPFsen7TYEsDQIAfCUZWRkZGRkZGT0f/UHAS86LuyGKlcAAAAASUVORK5CYII=",iconType:"img",platforms:["android"],url:`surfboard:///install-config?url=${o}&name=${n}`}].filter((e=>e.platforms.includes(l)||"unknown"===l))})),x=e=>{var t,n;(null==(t=b.value)?void 0:t.subscribe_url)&&("copy"===e.url?Xf(b.value.subscribe_url):(n=e.url,window.location.href=n))},C=()=>{var e;p.value=(null==(e=b.value)?void 0:e.subscribe_url)||"",d.value=!0},w=ai((()=>{var e,t,n;const o=null==(e=b.value)?void 0:e.transfer_enable,r=(null==(t=b.value)?void 0:t.u)||0,i=(null==(n=b.value)?void 0:n.d)||0;return o?Math.floor((r+i)/o*100):0})),{errorColor:k,warningColor:S,successColor:_,primaryColor:P}=n.value,T=ai((()=>{const e=w.value;return e>=100?k:e>=70?S:_}));function A(){return v(this,null,(function*(){var e,n;if(!(yield window.$dialog.confirm({title:t("确定重置当前已用流量?"),type:"info",content:t("点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。"),showIcon:!1})))return;const o=null==(e=yield MN())?void 0:e.data,r=null==o?void 0:o.find((e=>e.status===ZV.PENDING));if(r){if(!(yield window.$dialog.confirm({title:t("注意"),type:"info",content:t("你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?"),positiveText:t("确认取消"),negativeText:t("返回我的订单"),showIcon:!1})))return void qN.push("order");{const e=r.trade_no;if(!(yield FN(e)))return}}const i=null==(n=b.value)?void 0:n.plan_id;if(!i)return;const{data:a}=yield LN(i,"reset_price");a&&qN.push("order/"+a)}))}const z=Et([]),R=Et([0,0,0]),E=Et(),O=Et(),M=()=>v(this,null,(function*(){const{data:e}=yield EN.get("/user/notice/fetch");z.value=e;const t=e.find((e=>{var t;return null==(t=e.tags)?void 0:t.includes("弹窗")}));t&&(s.value=!0,c.value=t)})),F=()=>v(this,null,(function*(){const{data:e}=yield EN.get("/user/getStat");e&&(R.value=e)})),I=()=>v(this,null,(function*(){const{data:e}=yield ON();if(!e)return;E.value=e;const t=[...new Set(e.map((e=>"hysteria"===e.type&&2===e.version?"hysteria2":e.type)))];O.value=t,m.value=f.filter((e=>t.includes(e.type)||["auto","all"].includes(e.type)))})),L=()=>v(this,null,(function*(){yield Promise.all([M(),i.getUserSubscribe(),F(),I()])}));return Dn((()=>{L()})),(e,t)=>{const n=RI,a=YB,f=yH,v=mH,k=XB,S=DI,_=oO,E=vO,M=GO,F=h$,I=uE,L=qR,B=BO,D=P$,$=lL,N=r$,j=hH,H=uH,W=lH,U=rH,V=tH,q=Zj;return _r(),Rr(q,{"show-footer":!1},{default:hn((()=>{var q,K,X,Y;return[Lr(n,{show:s.value,"onUpdate:show":t[0]||(t[0]=e=>s.value=e),class:"mx-2.5 max-w-full w-150 md:mx-auto",preset:"card",title:null==(q=c.value)?void 0:q.title,size:"huge",bordered:!1,"content-style":"padding-top:0",segmented:{content:!1}},{default:hn((()=>{var e,t;return[Ir("div",{innerHTML:(t=(null==(e=c.value)?void 0:e.content)||"",o.render(t)),class:"markdown-body custom-html-style"},null,8,tq)]})),_:1},8,["show","title"]),Lr(n,{show:u.value,"onUpdate:show":t[3]||(t[3]=e=>u.value=e),"transform-origin":"center","auto-focus":!1,"display-directive":"show","trap-focus":!1},{default:hn((()=>[Lr(E,{class:"max-w-full w-75",bordered:!1,size:"huge","content-style":"padding:0"},{default:hn((()=>[Lr(k,{hoverable:""},{default:hn((()=>{var n;return[(null==(n=O.value)?void 0:n.includes("hysteria2"))?(_r(),Rr(a,{key:0,class:"p-0!"},{default:hn((()=>[Ir("div",{class:"flex cursor-pointer items-center p-2.5",onClick:t[1]||(t[1]=e=>{var t;return It(Xf)((null==(t=b.value)?void 0:t.subscribe_url)+"&types=hysteria2")})},[Ir("div",nq,[Lr(It(SF),{size:"30"},{default:hn((()=>[(_r(),Rr(Qn(It(QV))))])),_:1})]),Ir("div",oq,oe(e.$t("复制HY2订阅地址")),1)])])),_:1})):$r("",!0),Lr(a,{class:"p-0!"},{default:hn((()=>[Ir("div",{class:"flex cursor-pointer items-center p-2.5",onClick:C},[Ir("div",rq,[Lr(f,{class:"text-3xl text-gray-600"})]),Ir("div",iq,oe(e.$t("扫描二维码订阅")),1)])])),_:1}),(_r(!0),zr(yr,null,eo(y.value,(t=>(_r(),zr(yr,{key:t.name},[t.platforms.includes(It(l))?(_r(),Rr(a,{key:0,class:"p-0!"},{default:hn((()=>[Ir("div",{class:"flex cursor-pointer items-center p-2.5",onClick:e=>x(t)},[Ir("div",lq,["img"===t.iconType?(_r(),zr("img",{key:0,src:t.icon,class:J(["h-8 w-8",t.iconClass])},null,10,sq)):(_r(),Rr(It(SF),{key:1,size:"30",class:"text-gray-600"},{default:hn((()=>["icon-fluent:copy-24-filled"===t.icon?(_r(),Rr(v,{key:0})):(_r(),Rr(Qn(t.icon),{key:1}))])),_:2},1024))]),Ir("div",cq,oe("复制订阅链接"===t.name?e.$t("复制订阅地址"):e.$t("导入到")+" "+t.name),1)],8,aq)])),_:2},1024)):$r("",!0)],64)))),128))]})),_:1}),Lr(S,{class:"m-0!"}),Ir("div",uq,[Lr(_,{type:"primary",class:"w-full",size:"large",onClick:t[2]||(t[2]=t=>e.$router.push("/knowledge"))},{default:hn((()=>[Dr(oe(e.$t("不会使用,查看使用教程")),1)])),_:1})])])),_:1})])),_:1},8,["show"]),Lr(n,{show:d.value,"onUpdate:show":t[4]||(t[4]=e=>d.value=e)},{default:hn((()=>[Lr(E,{class:"w-75"},{default:hn((()=>[Ir("div",dq,oe(e.$t("选择协议"))+":",1),Ir("div",pq,[(_r(!0),zr(yr,null,eo(m.value,(t=>(_r(),Rr(M,{key:t.type,value:t.type,checked:h.value.includes(t.type),onClick:e=>function(e){if("auto"===e||"all"===e&&h.value.includes("all"))h.value=["auto"];else if("all"!==e||h.value.includes("all")){const t=h.value.includes(e);h.value=t?h.value.filter((t=>t!==e)):[...h.value.filter((e=>"auto"!==e)),e];const n=function(e,t){if(e.length!==t.length)return!1;const n=[...e].sort(),o=[...t].sort();return n.every(((e,t)=>e===o[t]))}(m.value.map((e=>e.type)).filter((e=>"auto"!==e&&"all"!==e)),h.value);n?h.value.push("all"):h.value=h.value.filter((e=>"all"!==e))}else h.value=m.value.map((e=>e.type)).filter((e=>"auto"!==e));0===h.value.length&&(h.value=["auto"]),g()}(t.type)},{default:hn((()=>[Dr(oe(e.$t(t.label)),1)])),_:2},1032,["value","checked","onClick"])))),128))]),Ir("div",hq,[Lr(F,{value:p.value,"icon-src":It(r).logo,size:140,color:It(P),style:{"box-sizing":"content-box"}},null,8,["value","icon-src","color"])]),Ir("div",fq,oe(e.$t("使用支持扫码的客户端进行订阅")),1)])),_:1})])),_:1},8,["show"]),Ir("div",vq,[R.value[1]&&R.value[1]>0||R.value[0]&&R.value[0]>0?(_r(),zr("div",mq,[R.value[1]&&R.value[1]>0?(_r(),Rr(I,{key:0,type:"warning","show-icon":!1,bordered:!0,closable:"",class:"mb-1"},{default:hn((()=>[Dr(oe(R.value[1])+" "+oe(e.$t("条工单正在处理中"))+" ",1),Lr(_,{strong:"",text:"",onClick:t[5]||(t[5]=e=>It(qN).push("/ticket"))},{default:hn((()=>[Dr(oe(e.$t("立即查看")),1)])),_:1})])),_:1})):$r("",!0),R.value[0]&&R.value[0]>0?(_r(),Rr(I,{key:1,type:"error","show-icon":!1,bordered:!0,closable:"",class:"mb-1"},{default:hn((()=>[Dr(oe(e.$t("还有没支付的订单"))+" ",1),Lr(_,{text:"",strong:"",onClick:t[6]||(t[6]=e=>It(qN).push("/order"))},{default:hn((()=>[Dr(oe(e.$t("立即支付")),1)])),_:1})])),_:1})):$r("",!0),!((null==(K=b.value)?void 0:K.expired_at)&&((null==(X=b.value)?void 0:X.expired_at)||0)>Date.now()/1e3)&&w.value>=70?(_r(),Rr(I,{key:2,type:"info","show-icon":!1,bordered:!0,closable:"",class:"mb-1"},{default:hn((()=>[Dr(oe(e.$tc("当前已使用流量达{rate}%",{rate:w.value}))+" ",1),Lr(_,{text:"",onClick:t[7]||(t[7]=e=>A())},{default:hn((()=>[Ir("span",gq,oe(e.$t("重置已用流量")),1)])),_:1})])),_:1})):$r("",!0)])):$r("",!0),fn(Lr(E,{class:"w-full cursor-pointer overflow-hidden rounded-md text-white transition hover:opacity-75",bordered:!1,"content-style":"padding: 0"},{default:hn((()=>[Lr(B,{autoplay:""},{default:hn((()=>[(_r(!0),zr(yr,null,eo(z.value,(t=>(_r(),zr("div",{key:t.id,style:G(t.img_url?`background:url(${t.img_url}) no-repeat center center;background-size:cover;`:`background:url(${It(r).$state.assets_path}/images/background.svg) center center / cover no-repeat;`),onClick:e=>(s.value=!0,c.value=t)},[Ir("div",yq,[Ir("div",null,[Lr(L,{bordered:!1,class:"bg-orange-600 text-xs text-white"},{default:hn((()=>[Dr(oe(e.$t("公告")),1)])),_:1})]),Ir("div",null,[Ir("p",xq,oe(t.title),1),Ir("p",Cq,oe(It(Vf)(t.created_at)),1)])])],12,bq)))),128))])),_:1})])),_:1},512),[[Mi,(null==(Y=z.value)?void 0:Y.length)>0]]),Lr(E,{title:e.$t("我的订阅"),class:"mt-1 rounded-md md:mt-5"},{default:hn((()=>{var n,o,r,a,l,s,c,u,d,p,h,f,v,m,g,y;return[b.value?(null==(n=b.value)?void 0:n.plan_id)?(_r(),zr(yr,{key:1},[Ir("div",wq,oe(null==(r=null==(o=b.value)?void 0:o.plan)?void 0:r.name),1),null===(null==(a=b.value)?void 0:a.expired_at)?(_r(),zr("div",kq,oe(e.$t("该订阅长期有效")),1)):(null==(l=b.value)?void 0:l.expired_at)&&(null!=(c=null==(s=b.value)?void 0:s.expired_at)?c:0)It(qN).push("/plan/"+It(i).plan_id))},{default:hn((()=>[Dr(oe(e.$t("续费订阅")),1)])),_:1})):w.value>=70?(_r(),Rr(_,{key:4,type:"primary",class:"mt-5",onClick:t[9]||(t[9]=e=>A())},{default:hn((()=>[Dr(oe(e.$t("重置已用流量")),1)])),_:1})):$r("",!0)],64)):(_r(),zr("div",{key:2,class:"cursor-pointer pt-5 text-center",onClick:t[10]||(t[10]=e=>It(qN).push("/plan"))},[Lr(j,{class:"text-4xl"}),Ir("div",Pq,oe(e.$t("购买订阅")),1)])):(_r(),Rr($,{key:0},{default:hn((()=>[Lr(D,{height:"20px",width:"33%"}),Lr(D,{height:"20px",width:"66%"}),Lr(D,{height:"20px"})])),_:1}))]})),_:1},8,["title"]),Lr(E,{title:e.$t("捷径"),class:"mt-5 rounded-md","content-style":"padding: 0"},{default:hn((()=>[Lr(k,{hoverable:"",clickable:""},{default:hn((()=>[Lr(a,{class:"flex flex cursor-pointer justify-between p-5 hover:bg-gray-100",onClick:t[11]||(t[11]=e=>It(qN).push("/knowledge"))},{default:hn((()=>[Ir("div",Tq,[Ir("div",Aq,[Ir("div",zq,oe(e.$t("查看教程")),1),Ir("div",Rq,oe(e.$t("学习如何使用"))+" "+oe(It(r).title),1)]),Ir("div",null,[Lr(H,{class:"text-3xl text-gray-500-500"})])])])),_:1}),Lr(a,{class:"flex cursor-pointer justify-between p-5 hover:bg-gray-100",onClick:t[12]||(t[12]=e=>u.value=!0)},{default:hn((()=>[Ir("div",Eq,[Ir("div",null,[Ir("div",Oq,oe(e.$t("一键订阅")),1),Ir("div",Mq,oe(e.$t("快速将节点导入对应客户端进行使用")),1)]),Ir("div",null,[Lr(W,{class:"text-3xl text-gray-500-500"})])])])),_:1}),Lr(a,{class:"flex cursor-pointer justify-between p-5",onClick:t[13]||(t[13]=e=>It(i).plan_id?It(qN).push("/plan/"+It(i).plan_id):It(qN).push("/plan"))},{default:hn((()=>{var t;return[Ir("div",Fq,[Ir("div",null,[Ir("div",Iq,oe((null==(t=b.value)?void 0:t.plan_id)?e.$t("续费订阅"):e.$t("购买订阅")),1),Ir("div",Lq,oe(e.$t("对您当前的订阅进行购买")),1)]),Ir("div",null,[Lr(U,{class:"text-3xl text-gray-500-500"})])])]})),_:1}),Lr(a,{class:"flex cursor-pointer justify-between p-5",onClick:t[14]||(t[14]=t=>e.$router.push("/ticket"))},{default:hn((()=>[Ir("div",Bq,[Ir("div",null,[Ir("div",Dq,oe(e.$t("遇到问题")),1),Ir("div",$q,oe(e.$t("遇到问题可以通过工单与我们沟通")),1)]),Ir("div",null,[Lr(V,{class:"text-3xl text-gray-500-500"})])])])),_:1})])),_:1})])),_:1},8,["title"])])]})),_:1})}}}),jq=tj(Nq,[["__scopeId","data-v-2492d831"]]),Hq=Object.freeze(Object.defineProperty({__proto__:null,default:jq},Symbol.toStringTag,{value:"Module"})),Wq={class:"inline-block",viewBox:"0 0 1024 1024",width:"1em",height:"1em"},Uq=[Ir("path",{fill:"currentColor",d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448s448-200.6 448-448S759.4 64 512 64m0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372s372 166.6 372 372s-166.6 372-372 372m159.6-585h-59.5c-3 0-5.8 1.7-7.1 4.4l-90.6 180H511l-90.6-180a8 8 0 0 0-7.1-4.4h-60.7c-1.3 0-2.6.3-3.8 1c-3.9 2.1-5.3 7-3.2 10.9L457 515.7h-61.4c-4.4 0-8 3.6-8 8v29.9c0 4.4 3.6 8 8 8h81.7V603h-81.7c-4.4 0-8 3.6-8 8v29.9c0 4.4 3.6 8 8 8h81.7V717c0 4.4 3.6 8 8 8h54.3c4.4 0 8-3.6 8-8v-68.1h82c4.4 0 8-3.6 8-8V611c0-4.4-3.6-8-8-8h-82v-41.5h82c4.4 0 8-3.6 8-8v-29.9c0-4.4-3.6-8-8-8h-62l111.1-204.8c.6-1.2 1-2.5 1-3.8c-.1-4.4-3.7-8-8.1-8"},null,-1)],Vq={name:"ant-design-pay-circle-outlined",render:function(e,t){return _r(),zr("svg",Wq,[...Uq])}},qq={class:"inline-block",viewBox:"0 0 1024 1024",width:"1em",height:"1em"},Kq=[Ir("path",{fill:"currentColor",d:"M668.6 320c0-4.4-3.6-8-8-8h-54.5c-3 0-5.8 1.7-7.1 4.4l-84.7 168.8H511l-84.7-168.8a8 8 0 0 0-7.1-4.4h-55.7c-1.3 0-2.6.3-3.8 1c-3.9 2.1-5.3 7-3.2 10.8l103.9 191.6h-57c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76v39h-76c-4.4 0-8 3.6-8 8v27.1c0 4.4 3.6 8 8 8h76V704c0 4.4 3.6 8 8 8h49.9c4.4 0 8-3.6 8-8v-63.5h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8h-76.3v-39h76.3c4.4 0 8-3.6 8-8v-27.1c0-4.4-3.6-8-8-8H564l103.7-191.6c.5-1.1.9-2.4.9-3.7M157.9 504.2a352.7 352.7 0 0 1 103.5-242.4c32.5-32.5 70.3-58.1 112.4-75.9c43.6-18.4 89.9-27.8 137.6-27.8c47.8 0 94.1 9.3 137.6 27.8c42.1 17.8 79.9 43.4 112.4 75.9c10 10 19.3 20.5 27.9 31.4l-50 39.1a8 8 0 0 0 3 14.1l156.8 38.3c5 1.2 9.9-2.6 9.9-7.7l.8-161.5c0-6.7-7.7-10.5-12.9-6.3l-47.8 37.4C770.7 146.3 648.6 82 511.5 82C277 82 86.3 270.1 82 503.8a8 8 0 0 0 8 8.2h60c4.3 0 7.8-3.5 7.9-7.8M934 512h-60c-4.3 0-7.9 3.5-8 7.8a352.7 352.7 0 0 1-103.5 242.4a352.6 352.6 0 0 1-112.4 75.9c-43.6 18.4-89.9 27.8-137.6 27.8s-94.1-9.3-137.6-27.8a352.6 352.6 0 0 1-112.4-75.9c-10-10-19.3-20.5-27.9-31.4l49.9-39.1a8 8 0 0 0-3-14.1l-156.8-38.3c-5-1.2-9.9 2.6-9.9 7.7l-.8 161.7c0 6.7 7.7 10.5 12.9 6.3l47.8-37.4C253.3 877.7 375.4 942 512.5 942C747 942 937.7 753.9 942 520.2a8 8 0 0 0-8-8.2"},null,-1)],Gq={name:"ant-design-transaction-outlined",render:function(e,t){return _r(),zr("svg",qq,[...Kq])}},Xq={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},Yq=[Ir("path",{fill:"currentColor",d:"M19 17v2H7v-2s0-4 6-4s6 4 6 4m-3-9a3 3 0 1 0-3 3a3 3 0 0 0 3-3m3.2 5.06A5.6 5.6 0 0 1 21 17v2h3v-2s0-3.45-4.8-3.94M18 5a2.9 2.9 0 0 0-.89.14a5 5 0 0 1 0 5.72A2.9 2.9 0 0 0 18 11a3 3 0 0 0 0-6M8 10H5V7H3v3H0v2h3v3h2v-3h3Z"},null,-1)],Qq={name:"mdi-invite",render:function(e,t){return _r(),zr("svg",Xq,[...Yq])}},Zq={class:"text-5xl font-normal"},Jq={class:"ml-2.5 text-xl text-gray-500 md:ml-5"},eK={class:"text-gray-500"},tK={class:"flex justify-between pb-1 pt-1"},nK={class:"flex justify-between pb-1 pt-1"},oK={key:0},rK={key:1},iK={class:"flex justify-between pb-1 pt-1"},aK={class:"flex justify-between pb-1 pt-1"},lK={class:"mt-2.5"},sK={class:"mb-1"},cK={class:"mt-2.5"},uK={class:"mb-1"},dK={class:"flex justify-end"},pK={class:"mt-2.5"},hK={class:"mb-1"},fK={class:"mt-2.5"},vK={class:"mb-1"},mK={class:"flex justify-end"},gK=zn({__name:"index",setup(e){const t=_N(),n=e=>jf.global.t(e),o=[{title:n("邀请码"),key:"code",render(e){const t=`${window.location.protocol}//${window.location.host}/#/register?code=${e.code}`;return li("div",[li("span",e.code),li(oO,{size:"small",onClick:()=>Xf(t),quaternary:!0,type:"info"},{default:()=>n("复制链接")})])}},{title:n("创建时间"),key:"created_at",fixed:"right",align:"right",render:e=>Vf(e.created_at)}],r=[{title:n("发放时间"),key:"created_at",render:e=>Vf(e.created_at)},{title:n("佣金"),key:"get_amount",fixed:"right",align:"right",render:e=>Gf(e.get_amount)}],i=Et(),a=Et([]);function l(){return v(this,null,(function*(){const e=yield EN.get("/user/invite/fetch"),{data:t}=e;i.value=t.codes,a.value=t.stat}))}const s=Et([]),c=vt({page:1,pageSize:10,showSizePicker:!0,pageSizes:[10,50,100,150],onChange:e=>{c.page=e,u()},onUpdatePageSize:e=>{c.pageSize=e,c.page=1,u()}});function u(){return v(this,null,(function*(){const e=yield function(e=1,t=10){return EN.get(`/user/invite/details?current=${e}&page_size=${t}`)}(c.page,c.pageSize),{data:t}=e;s.value=t}))}const d=Et(!1);function p(){return v(this,null,(function*(){d.value=!0;const{data:e}=yield EN.get("/user/invite/save");!0===e&&(window.$message.success(n("已生成")),w()),d.value=!1}))}const h=Et(!1),f=Et(),m=Et(!1);function g(){return v(this,null,(function*(){m.value=!0;const e=f.value;if("number"!=typeof e)return window.$message.error(n("请输入正确的划转金额")),void(m.value=!1);const{data:t}=yield function(e){return EN.post("/user/transfer",{transfer_amount:e})}(100*e);!0===t&&(window.$message.success(n("划转成功")),h.value=!1,l()),m.value=!1}))}const b=Et(!1),y=vt({method:null,account:null}),x=Et(!1);function C(){return v(this,null,(function*(){if(x.value=!0,!y.method)return window.$message.error(n("提现方式不能为空")),void(x.value=!1);if(!y.account)return window.$message.error(n("提现账号不能为空")),void(x.value=!1);const e=y.method,t=y.account,{data:o}=yield(r={withdraw_method:e,withdraw_account:t},EN.post("/user/ticket/withdraw",r));var r;!0===o&&qN.push("/ticket"),x.value=!1}))}function w(){l(),u()}return Dn((()=>{w()})),(e,n)=>{const l=Qq,u=eM,v=Gq,w=Vq,k=lL,S=vO,_=rI,P=uE,T=AE,A=EB,z=RI,R=ZN,E=hM,O=Zj;return _r(),Rr(O,null,{default:hn((()=>[Lr(S,{title:e.$t("我的邀请"),class:"rounded-md"},{"header-extra":hn((()=>[Lr(l,{class:"text-4xl text-gray-500"})])),default:hn((()=>{var o;return[Ir("div",null,[Ir("span",Zq,[Lr(u,{from:0,to:parseFloat(It(Gf)(a.value[4])),active:!0,precision:2,duration:500},null,8,["to"])]),Ir("span",Jq,oe(null==(o=It(t).appConfig)?void 0:o.currency),1)]),Ir("div",eK,oe(e.$t("当前剩余佣金")),1),Lr(k,{class:"mt-2.5"},{default:hn((()=>{var o;return[Lr(It(oO),{size:"small",type:"primary",onClick:n[0]||(n[0]=e=>h.value=!0)},{icon:hn((()=>[Lr(v)])),default:hn((()=>[Dr(" "+oe(e.$t("划转")),1)])),_:1}),(null==(o=It(t).appConfig)?void 0:o.withdraw_close)?$r("",!0):(_r(),Rr(It(oO),{key:0,size:"small",type:"primary",onClick:n[1]||(n[1]=e=>b.value=!0)},{icon:hn((()=>[Lr(w)])),default:hn((()=>[Dr(" "+oe(e.$t("推广佣金提现")),1)])),_:1}))]})),_:1})]})),_:1},8,["title"]),Lr(S,{class:"mt-4 rounded-md"},{default:hn((()=>{var n,o,r,i,l,s;return[Ir("div",tK,[Ir("div",null,oe(e.$t("已注册用户数")),1),Ir("div",null,oe(e.$tc("{number} 人",{number:a.value[0]})),1)]),Ir("div",nK,[Ir("div",null,oe(e.$t("佣金比例")),1),(null==(n=It(t).appConfig)?void 0:n.commission_distribution_enable)?(_r(),zr("div",oK,oe(`${Math.floor(((null==(o=It(t).appConfig)?void 0:o.commission_distribution_l1)||0)*a.value[3]/100)}%,${Math.floor(((null==(r=It(t).appConfig)?void 0:r.commission_distribution_l2)||0)*a.value[3]/100)}%,${Math.floor(((null==(i=It(t).appConfig)?void 0:i.commission_distribution_l3)||0)*a.value[3]/100)}%`),1)):(_r(),zr("div",rK,oe(a.value[3])+"%",1))]),Ir("div",iK,[Ir("div",null,oe(e.$t("确认中的佣金")),1),Ir("div",null,oe(null==(l=It(t).appConfig)?void 0:l.currency_symbol)+" "+oe(It(Gf)(a.value[2])),1)]),Ir("div",aK,[Ir("div",null,oe(e.$t("累计获得佣金")),1),Ir("div",null,oe(null==(s=It(t).appConfig)?void 0:s.currency_symbol)+" "+oe(It(Gf)(a.value[1])),1)])]})),_:1}),Lr(S,{title:e.$t("邀请码管理"),class:"mt-4 rounded-md"},{"header-extra":hn((()=>[Lr(It(oO),{size:"small",type:"primary",round:"",loading:d.value,onClick:p},{default:hn((()=>[Dr(oe(e.$t("生成邀请码")),1)])),_:1},8,["loading"])])),default:hn((()=>[Lr(_,{columns:o,data:i.value,bordered:!0},null,8,["data"])])),_:1},8,["title"]),Lr(S,{title:e.$t("佣金发放记录"),class:"mt-4 rounded-md"},{default:hn((()=>[Lr(_,{columns:r,data:s.value,pagination:c},null,8,["data","pagination"])])),_:1},8,["title"]),Lr(z,{show:h.value,"onUpdate:show":n[6]||(n[6]=e=>h.value=e)},{default:hn((()=>[Lr(S,{title:e.$t("划转"),segmented:{content:!0,footer:!0},"footer-style":"padding-top: 10px; padding-bottom:10px",class:"mx-2.5 max-w-full w-150 md:mx-auto",closable:"",onClose:n[5]||(n[5]=e=>h.value=!1)},{footer:hn((()=>[Ir("div",dK,[Ir("div",null,[Lr(It(oO),{onClick:n[3]||(n[3]=e=>h.value=!1)},{default:hn((()=>[Dr(oe(e.$t("取消")),1)])),_:1}),Lr(It(oO),{type:"primary",class:"ml-2.5",onClick:n[4]||(n[4]=e=>g()),loading:m.value,disabled:m.value},{default:hn((()=>[Dr(oe(e.$t("确定")),1)])),_:1},8,["loading","disabled"])])])])),default:hn((()=>[Lr(P,{type:"warning"},{default:hn((()=>[Dr(oe(e.$tc("划转后的余额仅用于{title}消费使用",{title:It(t).title})),1)])),_:1}),Ir("div",lK,[Ir("div",sK,oe(e.$t("当前推广佣金余额")),1),Lr(T,{placeholder:It(Gf)(a.value[4]),type:"number",disabled:""},null,8,["placeholder"])]),Ir("div",cK,[Ir("div",uK,oe(e.$t("划转金额")),1),Lr(A,{value:f.value,"onUpdate:value":n[2]||(n[2]=e=>f.value=e),min:0,placeholder:e.$t("请输入需要划转到余额的金额"),clearable:""},null,8,["value","placeholder"])])])),_:1},8,["title"])])),_:1},8,["show"]),Lr(z,{show:b.value,"onUpdate:show":n[12]||(n[12]=e=>b.value=e)},{default:hn((()=>[Lr(S,{title:e.$t("推广佣金划转至余额"),segmented:{content:!0,footer:!0},"footer-style":"padding-top: 10px; padding-bottom:10px",class:"mx-2.5 max-w-full w-150 md:mx-auto"},{"header-extra":hn((()=>[Lr(It(oO),{class:"h-auto p-0.5",tertiary:"",size:"large",onClick:n[7]||(n[7]=e=>b.value=!1)},{icon:hn((()=>[Lr(R,{class:"cursor-pointer opacity-85"})])),_:1})])),footer:hn((()=>[Ir("div",mK,[Ir("div",null,[Lr(It(oO),{onClick:n[10]||(n[10]=e=>b.value=!1)},{default:hn((()=>[Dr(oe(e.$t("取消")),1)])),_:1}),Lr(It(oO),{type:"primary",class:"ml-2.5",onClick:n[11]||(n[11]=e=>C()),loading:x.value,disabled:x.value},{default:hn((()=>[Dr(oe(e.$t("确定")),1)])),_:1},8,["loading","disabled"])])])])),default:hn((()=>{var o;return[Ir("div",pK,[Ir("div",hK,oe(e.$t("提现方式")),1),Lr(E,{value:y.method,"onUpdate:value":n[8]||(n[8]=e=>y.method=e),options:null==(o=It(t).appConfig)?void 0:o.withdraw_methods.map((e=>({label:e,value:e}))),placeholder:e.$t("请选择提现方式")},null,8,["value","options","placeholder"])]),Ir("div",fK,[Ir("div",vK,oe(e.$t("提现账号")),1),Lr(T,{value:y.account,"onUpdate:value":n[9]||(n[9]=e=>y.account=e),placeholder:e.$t("请输入提现账号"),type:"string"},null,8,["value","placeholder"])])]})),_:1},8,["title"])])),_:1},8,["show"])])),_:1})}}}),bK=Object.freeze(Object.defineProperty({__proto__:null,default:gK},Symbol.toStringTag,{value:"Module"})),yK={class:""},xK={class:"mb-1 text-base font-semibold"},CK={class:"text-xs text-gray-500"},wK=["innerHTML"],kK=zn({__name:"index",setup(e){const t=_N(),n=new qV({html:!0});window.copy=e=>Xf(e),window.jump=e=>i(e);const o=Et(!1),r=Et();function i(e){return v(this,null,(function*(){const{data:n}=yield function(e,t="zh-CN"){return EN.get(`/user/knowledge/fetch?id=${e}&language=${t}`)}(e,t.lang);n&&(r.value=n),o.value=!0}))}const a=Et(""),l=Et(!0),s=Et();function c(){return v(this,null,(function*(){l.value=!0;const e=a.value,{data:n}=yield function(e="",t="zh-CN"){return EN.get(`/user/knowledge/fetch?keyword=${e}&language=${t}`)}(e,t.lang);s.value=n,l.value=!1}))}function u(){c()}return Dn((()=>{u()})),(e,t)=>{const c=AE,d=oO,p=RE,h=P$,f=lL,v=YB,m=XB,g=vO,b=JI,y=ZI,x=Zj;return _r(),Rr(x,{"show-footer":!1},{default:hn((()=>[Lr(p,null,{default:hn((()=>[Lr(c,{placeholder:e.$t("使用文档"),value:a.value,"onUpdate:value":t[0]||(t[0]=e=>a.value=e),onKeyup:t[1]||(t[1]=fa((e=>u()),["enter"]))},null,8,["placeholder","value"]),Lr(d,{type:"primary",ghost:"",onClick:t[2]||(t[2]=e=>u())},{default:hn((()=>[Dr(oe(e.$t("搜索")),1)])),_:1})])),_:1}),l.value?(_r(),Rr(f,{key:0,vertical:"",class:"mt-5"},{default:hn((()=>[Lr(h,{height:"20px",width:"33%"}),Lr(h,{height:"20px",width:"66%"}),Lr(h,{height:"20px"})])),_:1})):$r("",!0),(_r(!0),zr(yr,null,eo(s.value,((t,n)=>(_r(),Rr(g,{key:n,title:n,class:"mt-5 rounded-md",contentStyle:"padding:0"},{default:hn((()=>[Lr(m,{clickable:"",hoverable:""},{default:hn((()=>[(_r(!0),zr(yr,null,eo(t,(t=>(_r(),Rr(v,{key:t.id,onClick:e=>i(t.id)},{default:hn((()=>[Ir("div",yK,[Ir("div",xK,oe(t.title),1),Ir("div",CK,oe(e.$t("最后更新"))+" "+oe(It(qf)(t.updated_at)),1)])])),_:2},1032,["onClick"])))),128))])),_:2},1024)])),_:2},1032,["title"])))),128)),Lr(y,{show:o.value,"onUpdate:show":t[3]||(t[3]=e=>o.value=e),width:"80%",placement:"right"},{default:hn((()=>{var e;return[Lr(b,{title:null==(e=r.value)?void 0:e.title,closable:""},{default:hn((()=>{var e,t;return[Ir("div",{innerHTML:(t=(null==(e=r.value)?void 0:e.body)||"",n.render(t)),class:"custom-html-style markdown-body"},null,8,wK)]})),_:1},8,["title"])]})),_:1},8,["show"])])),_:1})}}}),SK=Object.freeze(Object.defineProperty({__proto__:null,default:kK},Symbol.toStringTag,{value:"Module"})),_K={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},PK=[Ir("path",{fill:"currentColor",d:"M11 18h2v-2h-2zm1-16A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-14a4 4 0 0 0-4 4h2a2 2 0 0 1 2-2a2 2 0 0 1 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5a4 4 0 0 0-4-4"},null,-1)],TK={name:"mdi-help-circle-outline",render:function(e,t){return _r(),zr("svg",_K,[...PK])}},AK={class:"flex"},zK={class:"flex-[1]"},RK={class:"flex flex-[2] flex-shrink-0 text-center"},EK={class:"flex flex-1 items-center justify-center"},OK={class:"flex flex-1 items-center justify-center"},MK={class:"flex-1"},FK={class:"flex"},IK={class:"flex-[1] break-anywhere"},LK={class:"flex flex-[2] flex-shrink-0 items-center text-center"},BK={class:"flex flex-[1] items-center justify-center"},DK={class:"flex-[1]"},$K={class:"flex-[1]"},NK={key:0},jK={key:1},HK=zn({__name:"index",setup(e){const t=Et([]),n=Et(!0);return Dn((()=>{!function(){v(this,null,(function*(){n.value=!0;const e=yield ON(),{data:o}=e;t.value=o,n.value=!1}))}()})),(e,o)=>{const r=P$,i=lL,a=TK,l=NM,s=qR,c=YB,u=XB,d=Xn("router-link"),p=uE,h=Zj;return _r(),Rr(h,null,{default:hn((()=>[n.value?(_r(),Rr(i,{key:0,vertical:"",class:"mt-5"},{default:hn((()=>[Lr(r,{height:"20px",width:"33%"}),Lr(r,{height:"20px",width:"66%"}),Lr(r,{height:"20px"})])),_:1})):t.value.length>0?(_r(),Rr(u,{key:1,clickable:"",hoverable:""},{header:hn((()=>[Ir("div",AK,[Ir("div",zK,oe(e.$t("名称")),1),Ir("div",RK,[Ir("div",EK,[Dr(oe(e.$t("状态"))+" ",1),Lr(l,{placement:"bottom",trigger:"hover"},{trigger:hn((()=>[Lr(a,{class:"ml-1 text-base"})])),default:hn((()=>[Ir("span",null,oe(e.$t("五分钟内节点在线情况")),1)])),_:1})]),Ir("div",OK,[Dr(oe(e.$t("倍率"))+" ",1),Lr(l,{placement:"bottom",trigger:"hover"},{trigger:hn((()=>[Lr(a,{class:"ml-1 text-base"})])),default:hn((()=>[Ir("span",null,oe(e.$t("使用的流量将乘以倍率进行扣除")),1)])),_:1})]),Ir("div",MK,oe(e.$t("标签")),1)])])])),default:hn((()=>[(_r(!0),zr(yr,null,eo(t.value,(e=>(_r(),Rr(c,{key:e.id},{default:hn((()=>[Ir("div",FK,[Ir("div",IK,oe(e.name),1),Ir("div",LK,[Ir("div",BK,[Ir("div",{class:J(["h-1.5 w-1.5 rounded-full",e.is_online?"bg-blue-500":"bg-red-500"])},null,2)]),Ir("div",DK,[Lr(s,{size:"small",round:"",class:""},{default:hn((()=>[Dr(oe(e.rate)+" x ",1)])),_:2},1024)]),Ir("div",$K,[e.tags&&e.tags.length>0?(_r(),zr("div",NK,[(_r(!0),zr(yr,null,eo(e.tags,(e=>(_r(),Rr(s,{size:"small",round:"",key:e},{default:hn((()=>[Dr(oe(e),1)])),_:2},1024)))),128))])):(_r(),zr("span",jK,"-"))])])])])),_:2},1024)))),128))])),_:1})):(_r(),Rr(p,{key:2,type:"info"},{default:hn((()=>[Ir("div",null,[Dr(oe(e.$t("没有可用节点,如果您未订阅或已过期请"))+" ",1),Lr(d,{class:"font-semibold",to:"/plan"},{default:hn((()=>[Dr(oe(e.$t("订阅")),1)])),_:1}),Dr("。 ")])])),_:1}))])),_:1})}}}),WK=Object.freeze(Object.defineProperty({__proto__:null,default:HK},Symbol.toStringTag,{value:"Module"})),UK=zn({__name:"index",setup(e){const t=e=>jf.global.t(e),n=[{title:t("# 订单号"),key:"trade_no",render:e=>li(oO,{text:!0,class:"color-primary",onClick:()=>qN.push(`/order/${e.trade_no}`)},{default:()=>e.trade_no})},{title:t("周期"),key:"period",render:e=>li(qR,{round:!0,size:"small"},{default:()=>t(eq[e.period])})},{title:t("订单金额"),key:"total_amount",render:e=>Gf(e.total_amount)},{title:t("订单状态"),key:"status",render(e){const n=t(JV[e.status]),o=li("div",{class:["h-1.5 w-1.5 rounded-full mr-1.2",3===e.status?"bg-green-500":"bg-red-500"]});return li("div",{class:"flex items-center"},[o,n])}},{title:t("创建时间"),key:"created_at",render:e=>Vf(e.created_at)},{title:t("操作"),key:"actions",fixed:"right",render(e){const n=li(oO,{text:!0,type:"primary",onClick:()=>qN.push(`/order/${e.trade_no}`)},{default:()=>t("查看详情")}),o=li(oO,{text:!0,type:"primary",disabled:0!==e.status,onClick:()=>function(e){return v(this,null,(function*(){window.$dialog.confirm({title:t("注意"),type:"info",content:t("如果您已经付款,取消订单可能会导致支付失败,确定要取消订单吗?"),confirm(){return v(this,null,(function*(){const{data:n}=yield FN(e);!0===n&&(window.$message.success(t("取消成功")),r())}))}})}))}(e.trade_no)},{default:()=>t("取消")}),i=li(DI,{vertical:!0});return li("div",[n,i,o])}}],o=Et([]);function r(){return v(this,null,(function*(){!function(){v(this,null,(function*(){const e=yield MN(),{data:t}=e;o.value=t}))}()}))}return Dn((()=>{r()})),(e,t)=>{const r=rI,i=Zj;return _r(),Rr(i,null,{default:hn((()=>[Lr(r,{columns:n,data:o.value,bordered:!1,"scroll-x":800},null,8,["data"])])),_:1})}}}),VK=Object.freeze(Object.defineProperty({__proto__:null,default:UK},Symbol.toStringTag,{value:"Module"})),qK={class:"inline-block",viewBox:"0 0 48 48",width:"1em",height:"1em"},KK=[Ir("g",{fill:"currentColor","fill-rule":"evenodd","clip-rule":"evenodd"},[Ir("path",{d:"M24 42c9.941 0 18-8.059 18-18S33.941 6 24 6S6 14.059 6 24s8.059 18 18 18m0 2c11.046 0 20-8.954 20-20S35.046 4 24 4S4 12.954 4 24s8.954 20 20 20"}),Ir("path",{d:"M34.67 16.259a1 1 0 0 1 .072 1.412L21.386 32.432l-8.076-7.709a1 1 0 0 1 1.38-1.446l6.59 6.29L33.259 16.33a1 1 0 0 1 1.413-.07"})],-1)],GK={name:"healthicons-yes-outline",render:function(e,t){return _r(),zr("svg",qK,[...KK])}},XK={class:"inline-block",viewBox:"0 0 1024 1024",width:"1em",height:"1em"},YK=[Ir("path",{fill:"currentColor",d:"M952.08 1.552L529.039 116.144c-10.752 2.88-34.096 2.848-44.815-.16L72.08 1.776C35.295-8.352-.336 18.176-.336 56.048V834.16c0 32.096 24.335 62.785 55.311 71.409l412.16 114.224c11.025 3.055 25.217 4.751 39.937 4.751c10.095 0 25.007-.784 38.72-4.528l423.023-114.592c31.056-8.4 55.504-39.024 55.504-71.248V56.048c.016-37.84-35.616-64.464-72.24-54.496zM479.999 956.943L71.071 843.887c-3.088-.847-7.408-6.496-7.408-9.712V66.143L467.135 177.68c3.904 1.088 8.288 1.936 12.864 2.656zm480.336-122.767c0 3.152-5.184 8.655-8.256 9.503L544 954.207v-775.92c.592-.144 1.2-.224 1.792-.384L960.32 65.775v768.4h.016zM641.999 366.303c2.88 0 5.81-.367 8.69-1.184l223.935-63.024c17.025-4.816 26.945-22.465 22.16-39.473s-22.56-26.88-39.472-22.16l-223.936 63.025c-17.024 4.816-26.944 22.464-22.16 39.472c3.968 14.128 16.815 23.344 30.783 23.344m.002 192.001c2.88 0 5.81-.368 8.69-1.185l223.935-63.024c17.025-4.816 26.945-22.465 22.16-39.473c-4.783-17.008-22.56-26.88-39.472-22.16l-223.936 63.025c-17.024 4.816-26.944 22.464-22.16 39.457c3.968 14.127 16.815 23.36 30.783 23.36m.002 192c2.88 0 5.81-.368 8.69-1.185l223.935-63.024c17.025-4.816 26.945-22.465 22.16-39.473s-22.56-26.88-39.472-22.16L633.38 687.487c-17.024 4.816-26.944 22.464-22.16 39.472c3.968 14.113 16.815 23.345 30.783 23.345M394.629 303.487l-223.934-63.025c-16.912-4.72-34.688 5.152-39.473 22.16s5.12 34.656 22.16 39.473l223.937 63.024a31.8 31.8 0 0 0 8.687 1.184c13.968 0 26.815-9.215 30.783-23.343c4.784-16.993-5.12-34.657-22.16-39.473m.002 191.999l-223.934-63.025c-16.912-4.72-34.689 5.152-39.473 22.16s5.12 34.656 22.16 39.473l223.936 63.024a31.8 31.8 0 0 0 8.688 1.184c13.968 0 26.815-9.215 30.783-23.343c4.784-16.993-5.12-34.657-22.16-39.473m.002 191.999L170.699 624.46c-16.912-4.72-34.689 5.152-39.473 22.16s5.12 34.656 22.16 39.473l223.936 63.024a31.8 31.8 0 0 0 8.688 1.184c13.968 0 26.815-9.215 30.783-23.343c4.784-17.008-5.12-34.657-22.16-39.473"},null,-1)],QK={name:"simple-line-icons-book-open",render:function(e,t){return _r(),zr("svg",XK,[...YK])}},ZK={class:"inline-block",viewBox:"0 0 20 20",width:"1em",height:"1em"},JK=[Ir("path",{fill:"currentColor",d:"M10 2c-4.42 0-8 3.58-8 8s3.58 8 8 8s8-3.58 8-8s-3.58-8-8-8m-.615 12.66h-1.34l-3.24-4.54l1.341-1.25l2.569 2.4l5.141-5.931l1.34.94z"},null,-1)],eG={name:"dashicons-yes-alt",render:function(e,t){return _r(),zr("svg",ZK,[...JK])}},tG={class:"inline-block",viewBox:"0 0 20 20",width:"1em",height:"1em"},nG=[Ir("path",{fill:"currentColor",d:"M10 2c4.42 0 8 3.58 8 8s-3.58 8-8 8s-8-3.58-8-8s3.58-8 8-8m1.13 9.38l.35-6.46H8.52l.35 6.46zm-.09 3.36c.24-.23.37-.55.37-.96c0-.42-.12-.74-.36-.97s-.59-.35-1.06-.35s-.82.12-1.07.35s-.37.55-.37.97c0 .41.13.73.38.96c.26.23.61.34 1.06.34s.8-.11 1.05-.34"},null,-1)],oG={name:"dashicons-warning",render:function(e,t){return _r(),zr("svg",tG,[...nG])}},rG={class:"relative max-w-full w-75",style:{"padding-bottom":"100%"}},iG={class:"p-2.5 text-center"},aG={key:1,class:"flex flex-wrap"},lG={class:"w-full md:flex-[2]"},sG={key:2,class:"mt-2.5 text-xl"},cG={key:3,class:"text-sm text-[rgba(0,0,0,0.45)]"},uG={class:"flex"},dG={class:"flex-[1] text-gray-400"},pG={class:"flex-[2]"},hG={class:"flex"},fG={class:"mt-1 flex-[1] text-gray-400"},vG={class:"flex-[2]"},mG={class:"flex"},gG={class:"mb-1 mt-1 flex-[1] text-gray-400"},bG={class:"flex-[2]"},yG={class:"flex"},xG={class:"flex-[1] text-gray-400"},CG={class:"flex-[2]"},wG={key:0,class:"flex"},kG={class:"flex-[1] text-gray-400"},SG={class:"flex-[2]"},_G={key:1,class:"flex"},PG={class:"flex-[1] text-gray-400"},TG={class:"flex-[2]"},AG={key:2,class:"flex"},zG={class:"flex-[1] text-gray-400"},RG={class:"flex-[2]"},EG={key:3,class:"flex"},OG={class:"flex-[1] text-gray-400"},MG={class:"flex-[2]"},FG={key:4,class:"flex"},IG={class:"flex-[1] text-gray-400"},LG={class:"flex-[2]"},BG={class:"flex"},DG={class:"mt-1 flex-[1] text-gray-400"},$G={class:"flex-[2]"},NG=["onClick"],jG={class:"flex-[1] whitespace-nowrap"},HG={class:"flex-[1]"},WG=["src"],UG={key:0,class:"w-full md:flex-[1] md:pl-5"},VG={class:"mt-5 rounded-md bg-gray-800 p-5 text-white"},qG={class:"text-lg font-semibold"},KG={class:"flex border-gray-600 border-b pb-4 pt-4"},GG={class:"flex-[2]"},XG={class:"flex-[1] text-right color-#f8f9fa"},YG={key:0,class:"border-[#646669] border-b pb-4 pt-4"},QG={class:"color-#f8f9fa41"},ZG={class:"pt-4 text-right"},JG={key:1,class:"border-[#646669] border-b pb-4 pt-4"},eX={class:"color-#f8f9fa41"},tX={class:"pt-4 text-right"},nX={key:2,class:"border-[#646669] border-b pb-4 pt-4"},oX={class:"color-#f8f9fa41"},rX={class:"pt-4 text-right"},iX={key:3,class:"border-[#646669] border-b pb-4 pt-4"},aX={class:"color-#f8f9fa41"},lX={class:"pt-4 text-right"},sX={key:4,class:"border-[#646669] border-b pb-4 pt-4"},cX={class:"color-#f8f9fa41"},uX={class:"pt-4 text-right"},dX={class:"pb-4 pt-4"},pX={class:"color-#f8f9fa41"},hX={class:"text-4xl font-semibold"},fX=zn({__name:"detail",setup(e){const t=_N(),n=BN(),o=Yl(),r=e=>jf.global.t(e);function i(e){switch(e){case 1:return{icon:"info",title:r("开通中"),subTitle:r("订单系统正在进行处理,请稍等1-3分钟。")};case 2:return{icon:"info",title:r("已取消"),subTitle:r("订单由于超时支付已被取消。")};case 3:case 4:return{icon:"info",title:r("已完成"),subTitle:r("订单已支付并开通。")}}return{icon:"error",title:r("意料之外"),subTitle:r("意料之外的状态")}}const a=Et(""),l=Et(),s=Et(),c=Et(!0);function u(){return v(this,null,(function*(){c.value=!0;const{data:e}=yield(t=a.value,EN.get("/user/order/detail?trade_no="+t));var t;l.value=e,clearInterval(s.value),e.status===ZV.PENDING&&function(){v(this,null,(function*(){const{data:e}=yield EN.get("/user/order/getPaymentMethod");d.value=e}))}(),[ZV.PENDING,ZV.PROCESSING].includes(e.status)&&(s.value=setInterval(y,1500)),c.value=!1}))}const d=Et([]),p=Et(0);function h(){var e,t,n,o,r;return((null==(e=l.value)?void 0:e.plan[l.value.period])||0)-((null==(t=l.value)?void 0:t.balance_amount)||0)-((null==(n=l.value)?void 0:n.surplus_amount)||0)+((null==(o=l.value)?void 0:o.refund_amount)||0)-((null==(r=l.value)?void 0:r.discount_amount)||0)}function f(){const e=d.value[p.value];return((null==e?void 0:e.handling_fee_percent)||(null==e?void 0:e.handling_fee_fixed))&&h()?h()*parseFloat(e.handling_fee_percent||"0")/100+((null==e?void 0:e.handling_fee_fixed)||0):0}function m(){return v(this,null,(function*(){const e=d.value[p.value],{data:t,type:n}=yield(o=a.value,i=null==e?void 0:e.id,EN.post("/user/order/checkout",{trade_no:o,method:i}));var o,i;t&&(!0===t?(window.$message.info(r("支付成功")),setTimeout((()=>{x()}),500)):0===n?(g.value=!0,b.value=t):1===n&&(window.$message.info(r("正在前往收银台")),setTimeout((()=>{window.location.href=t}),500)))}))}const g=Et(!1),b=Et("");function y(){return v(this,null,(function*(){var e;const{data:t}=yield(n=a.value,EN.get("/user/order/check?trade_no="+n));var n;t!==(null==(e=l.value)?void 0:e.status)&&x()}))}function x(){return v(this,null,(function*(){C(),n.getUserInfo()}))}function C(){return v(this,null,(function*(){u(),g.value=!1}))}return Dn((()=>{"string"==typeof o.params.trade_no&&(a.value=o.params.trade_no),C()})),Wn((()=>{clearInterval(s.value)})),(e,n)=>{const o=h$,s=DI,u=vO,y=RI,x=P$,w=lL,k=oG,S=eG,_=QK,P=oO,T=GK,A=Zj;return _r(),Rr(A,null,{default:hn((()=>{var A,z,R,E,O,M,F,I,L,B,D,$,N,j,H,W,U,V,q,K,G,X,Y,Q,Z,ee;return[Lr(y,{show:g.value,"onUpdate:show":n[0]||(n[0]=e=>g.value=e),onOnAfterLeave:n[1]||(n[1]=e=>b.value="")},{default:hn((()=>[Lr(u,{"content-style":"padding:10px",class:"w-auto",bordered:!1,size:"huge",role:"dialog","aria-modal":"true"},{default:hn((()=>[Ir("div",rG,[b.value?(_r(),Rr(o,{key:0,value:b.value,class:"pay-qrcode absolute h-full! w-full!",size:"400"},null,8,["value"])):$r("",!0)]),Lr(s,{class:"m-0!"}),Ir("div",iG,oe(e.$t("等待支付中")),1)])),_:1})])),_:1},8,["show"]),c.value?(_r(),Rr(w,{key:0,vertical:"",class:"mt-5"},{default:hn((()=>[Lr(x,{height:"20px",width:"33%"}),Lr(x,{height:"20px",width:"66%"}),Lr(x,{height:"20px"})])),_:1})):(_r(),zr("div",aG,[Ir("div",lG,[0!==(null==(A=l.value)?void 0:A.status)?(_r(),Rr(u,{key:0,class:"flex text-center","items-center":""},{default:hn((()=>{var t,o,r,a,s,c;return[2===(null==(t=l.value)?void 0:t.status)?(_r(),Rr(k,{key:0,class:"text-9xl color-#f9a314"})):$r("",!0),3===(null==(o=l.value)?void 0:o.status)||4==(null==(r=l.value)?void 0:r.status)?(_r(),Rr(S,{key:1,class:"text-9xl color-#48bc19"})):$r("",!0),(null==(a=l.value)?void 0:a.status)?(_r(),zr("div",sG,oe(i(l.value.status).title),1)):$r("",!0),(null==(s=l.value)?void 0:s.status)?(_r(),zr("div",cG,oe(i(l.value.status).subTitle),1)):$r("",!0),3===(null==(c=l.value)?void 0:c.status)?(_r(),Rr(P,{key:4,"icon-placement":"left",strong:"",color:"#db4619",size:"small",round:"",class:"mt-8",onClick:n[2]||(n[2]=t=>e.$router.push("/knowledge"))},{icon:hn((()=>[Lr(_)])),default:hn((()=>[Dr(" "+oe(e.$t("查看使用教程")),1)])),_:1})):$r("",!0)]})),_:1})):$r("",!0),Lr(u,{class:"mt-5 rounded-md",title:e.$t("商品信息")},{default:hn((()=>{var t,n,o;return[Ir("div",uG,[Ir("div",dG,oe(e.$t("产品名称"))+":",1),Ir("div",pG,oe(null==(t=l.value)?void 0:t.plan.name),1)]),Ir("div",hG,[Ir("div",fG,oe(e.$t("类型/周期"))+":",1),Ir("div",vG,oe((null==(n=l.value)?void 0:n.period)?e.$t(It(eq)[l.value.period]):""),1)]),Ir("div",mG,[Ir("div",gG,oe(e.$t("产品流量"))+":",1),Ir("div",bG,oe(null==(o=l.value)?void 0:o.plan.transfer_enable)+" GB",1)])]})),_:1},8,["title"]),Lr(u,{class:"mt-5 rounded-md",title:e.$t("订单信息")},{"header-extra":hn((()=>{var t;return[0===(null==(t=l.value)?void 0:t.status)?(_r(),Rr(P,{key:0,color:"#db4619",size:"small",round:"",strong:"",onClick:n[3]||(n[3]=e=>function(){return v(this,null,(function*(){window.$dialog.confirm({title:r("注意"),type:"info",content:r("如果您已经付款,取消订单可能会导致支付失败,确定要取消订单吗?"),confirm(){return v(this,null,(function*(){const{data:e}=yield FN(a.value);!0===e&&(window.$message.success(r("取消成功")),C())}))}})}))}())},{default:hn((()=>[Dr(oe(e.$t("关闭订单")),1)])),_:1})):$r("",!0)]})),default:hn((()=>{var t,n,o,r,i,a,s,c,u,d,p;return[Ir("div",yG,[Ir("div",xG,oe(e.$t("订单号"))+":",1),Ir("div",CG,oe(null==(t=l.value)?void 0:t.trade_no),1)]),(null==(n=l.value)?void 0:n.discount_amount)&&(null==(o=l.value)?void 0:o.discount_amount)>0?(_r(),zr("div",wG,[Ir("div",kG,oe(e.$t("优惠金额")),1),Ir("div",SG,oe(It(Gf)(l.value.discount_amount)),1)])):$r("",!0),(null==(r=l.value)?void 0:r.surplus_amount)&&(null==(i=l.value)?void 0:i.surplus_amount)>0?(_r(),zr("div",_G,[Ir("div",PG,oe(e.$t("旧订阅折抵金额")),1),Ir("div",TG,oe(It(Gf)(l.value.surplus_amount)),1)])):$r("",!0),(null==(a=l.value)?void 0:a.refund_amount)&&(null==(s=l.value)?void 0:s.refund_amount)>0?(_r(),zr("div",AG,[Ir("div",zG,oe(e.$t("退款金额")),1),Ir("div",RG,oe(It(Gf)(l.value.refund_amount)),1)])):$r("",!0),(null==(c=l.value)?void 0:c.balance_amount)&&(null==(u=l.value)?void 0:u.balance_amount)>0?(_r(),zr("div",EG,[Ir("div",OG,oe(e.$t("余额支付 ")),1),Ir("div",MG,oe(It(Gf)(l.value.balance_amount)),1)])):$r("",!0),0===(null==(d=l.value)?void 0:d.status)&&f()>0?(_r(),zr("div",FG,[Ir("div",IG,oe(e.$t("支付手续费"))+":",1),Ir("div",LG,oe(It(Gf)(f())),1)])):$r("",!0),Ir("div",BG,[Ir("div",DG,oe(e.$t("创建时间"))+":",1),Ir("div",$G,oe(It(Vf)(null==(p=l.value)?void 0:p.created_at)),1)])]})),_:1},8,["title"]),0===(null==(z=l.value)?void 0:z.status)?(_r(),Rr(u,{key:1,title:e.$t("支付方式"),class:"mt-5","content-style":"padding:0"},{default:hn((()=>[(_r(!0),zr(yr,null,eo(d.value,((e,t)=>(_r(),zr("div",{key:e.id,class:J(["border-2 rounded-md p-5 border-solid flex",p.value===t?"border-primary":"border-transparent"]),onClick:e=>p.value=t},[Ir("div",jG,oe(e.name),1),Ir("div",HG,[Ir("img",{class:"max-h-8",src:e.icon},null,8,WG)])],10,NG)))),128))])),_:1},8,["title"])):$r("",!0)]),0===(null==(R=l.value)?void 0:R.status)?(_r(),zr("div",UG,[Ir("div",VG,[Ir("div",qG,oe(e.$t("订单总额")),1),Ir("div",KG,[Ir("div",GG,oe(null==(E=l.value)?void 0:E.plan.name),1),Ir("div",XG,oe(null==(O=It(t).appConfig)?void 0:O.currency_symbol)+oe((null==(M=l.value)?void 0:M.period)&&It(Gf)(null==(F=l.value)?void 0:F.plan[l.value.period])),1)]),(null==(I=l.value)?void 0:I.surplus_amount)&&(null==(L=l.value)?void 0:L.surplus_amount)>0?(_r(),zr("div",YG,[Ir("div",QG,oe(e.$t("折抵")),1),Ir("div",ZG," - "+oe(null==(B=It(t).appConfig)?void 0:B.currency_symbol)+oe(It(Gf)(null==(D=l.value)?void 0:D.surplus_amount)),1)])):$r("",!0),(null==($=l.value)?void 0:$.discount_amount)&&(null==(N=l.value)?void 0:N.discount_amount)>0?(_r(),zr("div",JG,[Ir("div",eX,oe(e.$t("折扣")),1),Ir("div",tX," - "+oe(null==(j=It(t).appConfig)?void 0:j.currency_symbol)+oe(It(Gf)(null==(H=l.value)?void 0:H.discount_amount)),1)])):$r("",!0),(null==(W=l.value)?void 0:W.refund_amount)&&(null==(U=l.value)?void 0:U.refund_amount)>0?(_r(),zr("div",nX,[Ir("div",oX,oe(e.$t("退款")),1),Ir("div",rX," - "+oe(null==(V=It(t).appConfig)?void 0:V.currency_symbol)+oe(It(Gf)(null==(q=l.value)?void 0:q.refund_amount)),1)])):$r("",!0),(null==(K=l.value)?void 0:K.balance_amount)&&(null==(G=l.value)?void 0:G.balance_amount)>0?(_r(),zr("div",iX,[Ir("div",aX,oe(e.$t("余额支付")),1),Ir("div",lX," - "+oe(null==(X=It(t).appConfig)?void 0:X.currency_symbol)+oe(It(Gf)(null==(Y=l.value)?void 0:Y.balance_amount)),1)])):$r("",!0),f()>0?(_r(),zr("div",sX,[Ir("div",cX,oe(e.$t("支付手续费")),1),Ir("div",uX," + "+oe(null==(Q=It(t).appConfig)?void 0:Q.currency_symbol)+oe(It(Gf)(f())),1)])):$r("",!0),Ir("div",dX,[Ir("div",pX,oe(e.$t("总计")),1),Ir("div",hX,oe(null==(Z=It(t).appConfig)?void 0:Z.currency_symbol)+" "+oe(It(Gf)(h()+f()))+" "+oe(null==(ee=It(t).appConfig)?void 0:ee.currency),1)]),Lr(P,{type:"primary",class:"w-full text-white","icon-placement":"left",strong:"",onClick:n[4]||(n[4]=e=>m())},{icon:hn((()=>[Lr(T)])),default:hn((()=>[Dr(" "+oe(e.$t("结账")),1)])),_:1})])])):$r("",!0)]))]})),_:1})}}}),vX=Object.freeze(Object.defineProperty({__proto__:null,default:fX},Symbol.toStringTag,{value:"Module"})),mX={class:"inline-block",viewBox:"0 0 50 50",width:"1em",height:"1em"},gX=[Ir("path",{fill:"currentColor",d:"M25 42c-9.4 0-17-7.6-17-17S15.6 8 25 8s17 7.6 17 17s-7.6 17-17 17m0-32c-8.3 0-15 6.7-15 15s6.7 15 15 15s15-6.7 15-15s-6.7-15-15-15"},null,-1),Ir("path",{fill:"currentColor",d:"m32.283 16.302l1.414 1.415l-15.98 15.98l-1.414-1.414z"},null,-1),Ir("path",{fill:"currentColor",d:"m17.717 16.302l15.98 15.98l-1.414 1.415l-15.98-15.98z"},null,-1)],bX={name:"ei-close-o",render:function(e,t){return _r(),zr("svg",mX,[...gX])}},yX={class:"inline-block",viewBox:"0 0 50 50",width:"1em",height:"1em"},xX=[Ir("path",{fill:"currentColor",d:"M25 42c-9.4 0-17-7.6-17-17S15.6 8 25 8s17 7.6 17 17s-7.6 17-17 17m0-32c-8.3 0-15 6.7-15 15s6.7 15 15 15s15-6.7 15-15s-6.7-15-15-15"},null,-1),Ir("path",{fill:"currentColor",d:"m23 32.4l-8.7-8.7l1.4-1.4l7.3 7.3l11.3-11.3l1.4 1.4z"},null,-1)],CX={name:"ei-check",render:function(e,t){return _r(),zr("svg",yX,[...xX])}},wX={class:"ml-auto mr-auto max-w-1200 w-full"},kX={class:"m-3 mb-1 mt-1 text-3xl font-normal"},SX={class:"card-container mt-2.5 md:mt-10"},_X=["onClick"],PX={class:"vertical-bottom"},TX={class:"text-3xl font-semibold"},AX={class:"pl-1 text-base text-gray-500"},zX={key:0},RX=["innerHTML"],EX=zn({__name:"index",setup(e){const t=_N(),n=e=>jf.global.t(e),o=new qV({html:!0}),r=Et(0),i=[{value:0,label:n("全部")},{value:1,label:n("按周期")},{value:2,label:n("按流量")}],a=Et([]),l=Et([]);function s(){return v(this,null,(function*(){const{data:e}=yield EN.get("/user/plan/fetch");e.forEach((e=>{const t=function(e){return null!==e.onetime_price?{price:e.onetime_price/100,cycle:n("一次性")}:null!==e.month_price?{price:e.month_price/100,cycle:n("月付")}:null!==e.quarter_price?{price:e.quarter_price/100,cycle:n("季付")}:null!==e.half_year_price?{price:e.half_year_price/100,cycle:n("半年付")}:null!==e.year_price?{price:e.year_price/100,cycle:n("年付")}:null!==e.two_year_price?{price:e.two_year_price/100,cycle:n("两年付")}:null!==e.three_year_price?{price:e.three_year_price/100,cycle:n("三年付")}:{price:0,cycle:n("错误")}}(e);e.price=t.price,e.cycle=t.cycle})),l.value=e}))}return lr([l,r],(e=>{a.value=e[0].filter((t=>0===e[1]?1:1===e[1]?!((t.onetime_price||0)>0):2===e[1]?(t.onetime_price||0)>0:void 0))})),Dn((()=>{s()})),(e,n)=>{const l=rF,s=oF,c=CX,u=bX,d=SF,p=oO,h=vO,f=Zj;return _r(),Rr(f,null,{default:hn((()=>[Ir("div",wX,[Ir("h2",kX,oe(e.$t("选择最适合你的计划")),1),Lr(s,{value:r.value,"onUpdate:value":n[0]||(n[0]=e=>r.value=e),name:"plan_select",class:""},{default:hn((()=>[(_r(),zr(yr,null,eo(i,(e=>Lr(l,{key:e.value,value:e.value,label:e.label,style:{background:"--n-color"}},null,8,["value","label"]))),64))])),_:1},8,["value"]),Ir("section",SX,[(_r(!0),zr(yr,null,eo(a.value,(n=>(_r(),zr("div",{class:"card-item min-w-75 cursor-pointer",key:n.id,onClick:t=>e.$router.push("/plan/"+n.id)},[Lr(h,{title:n.name,hoverable:"",class:"max-w-full w-375"},{"header-extra":hn((()=>{var e;return[Ir("div",PX,[Ir("span",TX,oe(null==(e=It(t).appConfig)?void 0:e.currency_symbol)+" "+oe(n.price),1),Ir("span",AX," /"+oe(n.cycle),1)])]})),action:hn((()=>[Lr(p,{strong:"",secondary:"",type:"primary"},{default:hn((()=>[Dr(oe(e.$t("立即订阅")),1)])),_:1})])),default:hn((()=>{return[It(rd)(n.content)?(_r(),zr("div",zX,[(_r(!0),zr(yr,null,eo(JSON.parse(n.content),((e,t)=>(_r(),zr("div",{key:t,class:J(["vertical-center flex items-center",e.support?"":"opacity-30"])},[Lr(d,{size:"30",class:"flex items-center text-[--primary-color]"},{default:hn((()=>[e.support?(_r(),Rr(c,{key:0})):(_r(),Rr(u,{key:1}))])),_:2},1024),Ir("div",null,oe(e.feature),1)],2)))),128))])):(_r(),zr("div",{key:1,innerHTML:(e=n.content||"",o.render(e)),class:"markdown-body"},null,8,RX))];var e})),_:2},1032,["title"])],8,_X)))),128))])])])),_:1})}}}),OX=tj(EX,[["__scopeId","data-v-16d7c058"]]),MX=Object.freeze(Object.defineProperty({__proto__:null,default:OX},Symbol.toStringTag,{value:"Module"})),FX={class:"inline-block",viewBox:"0 0 576 512",width:"1em",height:"1em"},IX=[Ir("path",{fill:"currentColor",d:"M64 64C28.7 64 0 92.7 0 128v64c0 8.8 7.4 15.7 15.7 18.6C34.5 217.1 48 235 48 256s-13.5 38.9-32.3 45.4C7.4 304.3 0 311.2 0 320v64c0 35.3 28.7 64 64 64h448c35.3 0 64-28.7 64-64v-64c0-8.8-7.4-15.7-15.7-18.6C541.5 294.9 528 277 528 256s13.5-38.9 32.3-45.4c8.3-2.9 15.7-9.8 15.7-18.6v-64c0-35.3-28.7-64-64-64zm64 112v160c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V176c0-8.8-7.2-16-16-16H144c-8.8 0-16 7.2-16 16m-32-16c0-17.7 14.3-32 32-32h320c17.7 0 32 14.3 32 32v192c0 17.7-14.3 32-32 32H128c-17.7 0-32-14.3-32-32z"},null,-1)],LX={name:"fa6-solid-ticket",render:function(e,t){return _r(),zr("svg",FX,[...IX])}},BX={key:1,class:"grid grid-cols-1 lg:grid-cols-2 gap-5 mt-5"},DX={class:"space-y-5"},$X={key:0},NX=["innerHTML"],jX=["onClick"],HX={class:"space-y-5"},WX={class:"bg-gray-800 rounded-lg p-5 text-white"},UX={class:"flex items-center gap-3"},VX=["placeholder"],qX={class:"bg-gray-800 rounded-lg p-5 text-white space-y-4"},KX={class:"text-lg font-semibold"},GX={class:"flex justify-between items-center py-3 border-b border-gray-600"},XX={class:"font-semibold"},YX={key:0,class:"flex justify-between items-center py-3 border-b border-gray-600"},QX={class:"text-gray-300"},ZX={class:"text-sm text-gray-400"},JX={class:"font-semibold text-green-400"},eY={class:"py-3"},tY={class:"text-gray-300 mb-2"},nY={class:"text-3xl font-bold"},oY=zn({__name:"detail",setup(e){const t=_N(),n=BN(),o=Yl(),r=e=>jf.global.t(e),i=Et(Number(o.params.plan_id)),a=Et(),l=Et(!0),s=Et(),c=Et(0),u={month_price:r("月付"),quarter_price:r("季付"),half_year_price:r("半年付"),year_price:r("年付"),two_year_price:r("两年付"),three_year_price:r("三年付"),onetime_price:r("一次性"),reset_price:r("流量重置包")},d=Et(""),p=Et(!1),h=Et(),f=Et(!1),m=ai((()=>a.value?Object.entries(u).filter((([e])=>null!==a.value[e]&&void 0!==a.value[e])).map((([e,t])=>({name:t,key:e}))):[])),g=ai((()=>{var e;return(null==(e=t.appConfig)?void 0:e.currency_symbol)||"¥"})),b=ai((()=>{var e;return null==(e=m.value[c.value])?void 0:e.key})),y=ai((()=>a.value&&b.value&&a.value[b.value]||0)),x=ai((()=>{if(!h.value||!y.value)return 0;const{type:e,value:t}=h.value;return 1===e?t:Math.floor(t*y.value/100)})),C=ai((()=>Math.max(0,y.value-x.value))),w=ai((()=>{var e;const t=null==(e=a.value)?void 0:e.content;if(!t)return!1;try{return JSON.parse(t),!0}catch(WQ){return!1}})),k=ai((()=>{var e;if(!w.value)return[];try{return JSON.parse((null==(e=a.value)?void 0:e.content)||"[]")}catch(WQ){return[]}})),S=ai((()=>{var e;return w.value||!(null==(e=a.value)?void 0:e.content)?"":new qV({html:!0}).render(a.value.content)})),_=e=>{var t;return Gf((null==(t=a.value)?void 0:t[e])||0)},P=()=>v(this,null,(function*(){if(d.value.trim()){p.value=!0;try{const{data:n}=yield(e=d.value,t=i.value,EN.post("/user/coupon/check",{code:e,plan_id:t}));n&&(h.value=n,window.$message.success(r("优惠券验证成功")))}catch(n){h.value=void 0}finally{p.value=!1}var e,t}})),T=()=>v(this,null,(function*(){var e;const t=null==(e=s.value)?void 0:e.find((e=>0===e.status));return t?A(t.trade_no):z()?R():void(yield E())})),A=e=>{window.$dialog.confirm({title:r("注意"),type:"info",content:r("你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?"),positiveText:r("确认取消"),negativeText:r("返回我的订单"),confirm(){return v(this,null,(function*(){const{data:t}=yield FN(e);t&&(yield E())}))},cancel:()=>qN.push("/order")})},z=()=>n.plan_id&&n.plan_id!=i.value&&(null===n.expired_at||n.expired_at>=Math.floor(Date.now()/1e3)),R=()=>{window.$dialog.confirm({title:r("注意"),type:"info",content:r("请注意,变更订阅会导致当前订阅被覆盖。"),confirm:()=>E()})},E=()=>v(this,null,(function*(){var e;if(b.value){f.value=!0;try{const{data:t}=yield LN(i.value,b.value,null==(e=h.value)?void 0:e.code);t&&(window.$message.success(r("订单提交成功,正在跳转支付")),setTimeout((()=>qN.push("/order/"+t)),500))}finally{f.value=!1}}})),O=()=>v(this,null,(function*(){l.value=!0;try{const{data:t}=yield(e=i.value,EN.get("/user/plan/fetch?id="+e));t?a.value=t:qN.push("/plan")}finally{l.value=!1}var e})),M=()=>v(this,null,(function*(){const{data:e}=yield MN();s.value=e}));return Dn((()=>v(this,null,(function*(){yield Promise.all([O(),M()])})))),(e,n)=>{const o=P$,i=lL,s=CX,u=bX,v=SF,y=vO,A=DI,z=LX,R=oO,E=GK,O=Zj;return _r(),Rr(O,null,{default:hn((()=>{var O,M,F;return[l.value?(_r(),Rr(i,{key:0,vertical:"",class:"mt-5"},{default:hn((()=>[Lr(o,{height:"20px",width:"33%"}),Lr(o,{height:"20px",width:"66%"}),Lr(o,{height:"20px"})])),_:1})):(_r(),zr("div",BX,[Ir("div",DX,[Lr(y,{title:null==(O=a.value)?void 0:O.name,class:"rounded-lg"},{default:hn((()=>[w.value?(_r(),zr("div",$X,[(_r(!0),zr(yr,null,eo(k.value,((e,t)=>(_r(),zr("div",{key:t,class:J(["flex items-center gap-3 py-2",e.support?"":"opacity-50"])},[Lr(v,{size:"20",class:J(e.support?"text-green-500":"text-red-500")},{default:hn((()=>[e.support?(_r(),Rr(s,{key:0})):(_r(),Rr(u,{key:1}))])),_:2},1032,["class"]),Ir("span",null,oe(e.feature),1)],2)))),128))])):(_r(),zr("div",{key:1,innerHTML:S.value,class:"markdown-body"},null,8,NX))])),_:1},8,["title"]),Lr(y,{title:e.$t("付款周期"),class:"rounded-lg","content-style":"padding:0"},{default:hn((()=>[(_r(!0),zr(yr,null,eo(m.value,((e,t)=>(_r(),zr("div",{key:e.key},[Ir("div",{class:J(["flex justify-between items-center p-5 text-base cursor-pointer border-2 transition-all duration-200 border-solid rounded-lg"," dark:hover:bg-primary/20",t===c.value?"border-primary dark:bg-primary/20":"border-transparent"]),onClick:e=>c.value=t},[Ir("div",{class:J(["font-medium transition-colors",t===c.value?" dark:text-primary-400":"text-gray-900 dark:text-gray-100"])},oe(e.name),3),Ir("div",{class:J(["text-lg font-semibold transition-colors",t===c.value?"text-primary-600 dark:text-primary-400":"text-gray-700 dark:text-gray-300"])},oe(g.value)+oe(_(e.key)),3)],10,jX),td.value=e),placeholder:r("有优惠券?"),class:"flex-1 bg-transparent border-none outline-none text-white placeholder-gray-400"},null,8,VX),[[ca,d.value]]),Lr(R,{type:"primary",loading:p.value,disabled:p.value||!d.value.trim(),onClick:P},{icon:hn((()=>[Lr(z)])),default:hn((()=>[Dr(" "+oe(e.$t("验证")),1)])),_:1},8,["loading","disabled"])])]),Ir("div",qX,[Ir("h3",KX,oe(e.$t("订单总额")),1),Ir("div",GX,[Ir("span",null,oe(null==(M=a.value)?void 0:M.name),1),Ir("span",XX,oe(g.value)+oe(_(b.value)),1)]),h.value&&x.value>0?(_r(),zr("div",YX,[Ir("div",null,[Ir("div",QX,oe(e.$t("折扣")),1),Ir("div",ZX,oe(h.value.name),1)]),Ir("span",JX,"-"+oe(g.value)+oe(It(Gf)(x.value)),1)])):$r("",!0),Ir("div",eY,[Ir("div",tY,oe(e.$t("总计")),1),Ir("div",nY,oe(g.value)+oe(It(Gf)(C.value))+" "+oe(null==(F=It(t).appConfig)?void 0:F.currency),1)]),Lr(R,{type:"primary",size:"large",class:"w-full",loading:f.value,disabled:f.value,onClick:T},{icon:hn((()=>[Lr(E)])),default:hn((()=>[Dr(" "+oe(e.$t("下单")),1)])),_:1},8,["loading","disabled"])])])]))]})),_:1})}}}),rY=Object.freeze(Object.defineProperty({__proto__:null,default:oY},Symbol.toStringTag,{value:"Module"})),iY={class:"inline-block",viewBox:"0 0 256 256",width:"1em",height:"1em"},aY=[Ir("path",{fill:"currentColor",d:"M216 64H56a8 8 0 0 1 0-16h136a8 8 0 0 0 0-16H56a24 24 0 0 0-24 24v128a24 24 0 0 0 24 24h160a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16m-36 80a12 12 0 1 1 12-12a12 12 0 0 1-12 12"},null,-1)],lY={name:"ph-wallet-fill",render:function(e,t){return _r(),zr("svg",iY,[...aY])}},sY={class:"text-5xl font-normal"},cY={class:"ml-5 text-xl text-gray-500"},uY={class:"text-gray-500"},dY={class:"mt-2.5 max-w-125"},pY={class:"mt-2.5 max-w-125"},hY={class:"mt-2.5 max-w-125"},fY={class:"mt-2.5 max-w-125"},vY={class:"mb-1"},mY={class:"mt-2.5 max-w-125"},gY={class:"mb-1"},bY={class:"m-0 pb-2.5 pt-2.5 text-xl"},yY={class:"mt-5"},xY=["href"],CY={class:"mt-5"},wY={class:"m-0 pb-2.5 pt-2.5 text-xl"},kY={class:"mt-5"},SY={class:"flex justify-end"},_Y=zn({__name:"index",setup(e){const t=BN(),n=_N(),o=e=>jf.global.t(e),r=Et(""),i=Et(""),a=Et(""),l=Et(!1);function s(){return v(this,null,(function*(){if(l.value=!0,i.value!==a.value)return void window.$message.error(o("两次新密码输入不同"));const{data:e}=yield(t=r.value,n=i.value,EN.post("/user/changePassword",{old_password:t,new_password:n}));var t,n;!0===e&&window.$message.success(o("密码修改成功")),l.value=!1}))}const c=Et(!1),u=Et(!1);function d(e){return v(this,null,(function*(){if("expire"===e){const{data:e}=yield IN({remind_expire:c.value?1:0});!0===e?window.$message.success(o("更新成功")):(window.$message.error(o("更新失败")),c.value=!c.value)}else if("traffic"===e){const{data:e}=yield IN({remind_traffic:u.value?1:0});!0===e?window.$message.success(o("更新成功")):(window.$message.error(o("更新失败")),u.value=!u.value)}}))}const p=Et(),h=Et(!1);function f(){return v(this,null,(function*(){const{data:e}=yield EN.get("user/telegram/getBotInfo");e&&(p.value=e)}))}const m=Et(!1);function g(){return v(this,null,(function*(){const{data:e}=yield EN.get("/user/resetSecurity");e&&window.$message.success(o("重置成功"))}))}return Dn((()=>{!function(){v(this,null,(function*(){t.getUserInfo(),c.value=!!t.remind_expire,u.value=!!t.remind_traffic}))}()})),(e,o)=>{const v=lY,b=vO,y=AE,x=oO,C=M$,w=uE,k=DI,S=z$,_=RI,P=Zj;return _r(),Rr(P,null,{default:hn((()=>{var P,T,A,z;return[Lr(b,{title:e.$t("我的钱包"),class:"rounded-md"},{"header-extra":hn((()=>[Lr(v,{class:"text-4xl text-gray-500"})])),default:hn((()=>{var o;return[Ir("div",null,[Ir("span",sY,oe(It(Gf)(It(t).balance)),1),Ir("span",cY,oe(null==(o=It(n).appConfig)?void 0:o.currency),1)]),Ir("div",uY,oe(e.$t("账户余额(仅消费)")),1)]})),_:1},8,["title"]),Lr(b,{title:e.$t("修改密码"),class:"mt-5 rounded-md"},{default:hn((()=>[Ir("div",dY,[Ir("label",null,oe(e.$t("旧密码")),1),Lr(y,{type:"password",value:r.value,"onUpdate:value":o[0]||(o[0]=e=>r.value=e),placeholder:e.$t("请输入旧密码"),maxlength:32},null,8,["value","placeholder"])]),Ir("div",pY,[Ir("label",null,oe(e.$t("新密码")),1),Lr(y,{type:"password",value:i.value,"onUpdate:value":o[1]||(o[1]=e=>i.value=e),placeholder:e.$t("请输入新密码"),maxlength:32},null,8,["value","placeholder"])]),Ir("div",hY,[Ir("label",null,oe(e.$t("新密码")),1),Lr(y,{type:"password",value:a.value,"onUpdate:value":o[2]||(o[2]=e=>a.value=e),placeholder:e.$t("请输入新密码"),maxlength:32},null,8,["value","placeholder"])]),Lr(x,{class:"mt-5",type:"primary",onClick:s,loading:l.value,disabled:l.value},{default:hn((()=>[Dr(oe(e.$t("保存")),1)])),_:1},8,["loading","disabled"])])),_:1},8,["title"]),Lr(b,{title:e.$t("通知"),class:"mt-5 rounded-md"},{default:hn((()=>[Ir("div",fY,[Ir("div",vY,oe(e.$t("到期邮件提醒")),1),Lr(C,{value:c.value,"onUpdate:value":[o[3]||(o[3]=e=>c.value=e),o[4]||(o[4]=e=>d("expire"))]},null,8,["value"])]),Ir("div",mY,[Ir("div",gY,oe(e.$t("流量邮件提醒")),1),Lr(C,{value:u.value,"onUpdate:value":[o[5]||(o[5]=e=>u.value=e),o[6]||(o[6]=e=>d("traffic"))]},null,8,["value"])])])),_:1},8,["title"]),(null==(T=null==(P=It(n))?void 0:P.appConfig)?void 0:T.is_telegram)?(_r(),Rr(b,{key:0,title:e.$t("绑定Telegram"),class:"mt-5 rounded-md"},{"header-extra":hn((()=>[Lr(x,{type:"primary",round:"",disabled:It(t).userInfo.telegram_id,onClick:o[7]||(o[7]=e=>(h.value=!0,f(),It(t).getUserSubscribe()))},{default:hn((()=>[Dr(oe(It(t).userInfo.telegram_id?e.$t("已绑定"):e.$t("立即开始")),1)])),_:1},8,["disabled"])])),_:1},8,["title"])):$r("",!0),(null==(z=null==(A=It(n))?void 0:A.appConfig)?void 0:z.telegram_discuss_link)?(_r(),Rr(b,{key:1,title:e.$t("Telegram 讨论组"),class:"mt-5 rounded-md"},{"header-extra":hn((()=>[Lr(x,{type:"primary",round:"",onClick:o[8]||(o[8]=e=>{var t,o,r;return r=null==(o=null==(t=It(n))?void 0:t.appConfig)?void 0:o.telegram_discuss_link,void(window.location.href=r)})},{default:hn((()=>[Dr(oe(e.$t("立即加入")),1)])),_:1})])),_:1},8,["title"])):$r("",!0),Lr(b,{title:e.$t("重置订阅信息"),class:"mt-5 rounded-md"},{default:hn((()=>[Lr(w,{type:"warning"},{default:hn((()=>[Dr(oe(e.$t("当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。")),1)])),_:1}),Lr(x,{type:"error",size:"small",class:"mt-2.5",onClick:o[9]||(o[9]=e=>m.value=!0)},{default:hn((()=>[Dr(oe(e.$t("重置")),1)])),_:1})])),_:1},8,["title"]),Lr(_,{title:e.$t("绑定Telegram"),preset:"card",show:h.value,"onUpdate:show":o[12]||(o[12]=e=>h.value=e),class:"mx-2.5 max-w-full w-150 md:mx-auto",footerStyle:"padding: 10px 16px",segmented:{content:!0,footer:!0}},{footer:hn((()=>[Ir("div",SY,[Lr(x,{type:"primary",onClick:o[11]||(o[11]=e=>h.value=!1)},{default:hn((()=>[Dr(oe(e.$t("我知道了")),1)])),_:1})])])),default:hn((()=>{var n,r,i;return[p.value&&It(t).subscribe?(_r(),zr(yr,{key:0},[Ir("div",null,[Ir("h2",bY,oe(e.$t("第一步")),1),Lr(k,{class:"m-0!"}),Ir("div",yY,[Dr(oe(e.$t("打开Telegram搜索"))+" ",1),Ir("a",{href:"https://t.me/"+(null==(n=p.value)?void 0:n.username)},"@"+oe(null==(r=p.value)?void 0:r.username),9,xY)])]),Ir("div",CY,[Ir("h2",wY,oe(e.$t("第二步")),1),Lr(k,{class:"m-0!"}),Ir("div",kY,oe(e.$t("向机器人发送你的")),1),Ir("code",{class:"cursor-pointer",onClick:o[10]||(o[10]=e=>{var n;return It(Xf)("/bind "+(null==(n=It(t).subscribe)?void 0:n.subscribe_url))})},"/bind "+oe(null==(i=It(t).subscribe)?void 0:i.subscribe_url),1)])],64)):(_r(),Rr(S,{key:1,size:"large"}))]})),_:1},8,["title","show"]),Lr(_,{show:m.value,"onUpdate:show":o[13]||(o[13]=e=>m.value=e),preset:"dialog",title:e.$t("确定要重置订阅信息?"),content:e.$t("如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。"),"positive-text":e.$t("确认"),"negative-text":e.$t("取消"),onPositiveClick:g},null,8,["show","title","content","positive-text","negative-text"])]})),_:1})}}}),PY=Object.freeze(Object.defineProperty({__proto__:null,default:_Y},Symbol.toStringTag,{value:"Module"})),TY={class:"flex justify-end"},AY=zn({__name:"index",setup(e){const t=e=>jf.global.t(e),n=[{label:t("低"),value:0},{label:t("中"),value:1},{label:t("高"),value:2}],o=[{title:t("主题"),key:"subject"},{title:t("工单级别"),key:"u",render:e=>n[e.level].label},{title:t("工单状态"),key:"status",render(e){const n=li("div",{class:["h-1.5 w-1.5 rounded-full mr-1.3",1===e.status?"bg-green-500":0===e.reply_status?"bg-blue-500":"bg-red-500"]});return li("div",{class:"flex items-center"},[n,1===e.status?t("已关闭"):0===e.reply_status?t("已回复"):t("待回复")])}},{title:t("创建时间"),key:"created_at",render:e=>Vf(e.created_at)},{title:t("最后回复时间"),key:"updated_at",render:e=>Vf(e.updated_at)},{title:t("操作"),key:"actions",fixed:"right",render(e){const n=li(oO,{text:!0,type:"primary",onClick:()=>qN.push(`/ticket/${e.id}`)},{default:()=>t("查看")}),o=li(oO,{text:!0,type:"primary",disabled:1===e.status,onClick:()=>function(e){return v(this,null,(function*(){const{data:n}=yield function(e){return EN.post("/user/ticket/close",{id:e})}(e);n&&(window.$message.success(t("关闭成功")),d())}))}(e.id)},{default:()=>t("关闭")}),r=li(DI,{vertical:!0});return li("div",[n,r,o])}}],r=Et(!1),i=Et(""),a=Et(),l=Et("");function s(){return v(this,null,(function*(){const{data:e}=yield(n=i.value,o=a.value,s=l.value,EN.post("/user/ticket/save",{subject:n,level:o,message:s}));var n,o,s;!0===e&&(window.$message.success(t("创建成功")),d(),r.value=!1)}))}const c=Et([]);function u(){return v(this,null,(function*(){const{data:e}=yield EN.get("/user/ticket/fetch");c.value=e}))}function d(){u()}return Dn((()=>{d()})),(e,t)=>{const u=AE,d=hM,p=lL,h=vO,f=RI,v=rI,m=Zj;return _r(),Rr(m,null,{default:hn((()=>[Lr(f,{show:r.value,"onUpdate:show":t[6]||(t[6]=e=>r.value=e)},{default:hn((()=>[Lr(h,{title:e.$t("新的工单"),class:"mx-2.5 max-w-full w-150 md:mx-auto",segmented:{content:!0,footer:!0},closable:"",onClose:t[5]||(t[5]=e=>r.value=!1)},{footer:hn((()=>[Ir("div",TY,[Lr(p,null,{default:hn((()=>[Lr(It(oO),{onClick:t[3]||(t[3]=e=>r.value=!1)},{default:hn((()=>[Dr(oe(e.$t("取消")),1)])),_:1}),Lr(It(oO),{type:"primary",onClick:t[4]||(t[4]=e=>s())},{default:hn((()=>[Dr(oe(e.$t("确认")),1)])),_:1})])),_:1})])])),default:hn((()=>[Ir("div",null,[Ir("label",null,oe(e.$t("主题")),1),Lr(u,{value:i.value,"onUpdate:value":t[0]||(t[0]=e=>i.value=e),class:"mt-1",placeholder:e.$t("请输入工单主题")},null,8,["value","placeholder"])]),Ir("div",null,[Ir("label",null,oe(e.$t("工单级别")),1),Lr(d,{value:a.value,"onUpdate:value":t[1]||(t[1]=e=>a.value=e),options:n,placeholder:e.$t("请选项工单等级"),class:"mt-1"},null,8,["value","placeholder"])]),Ir("div",null,[Ir("label",null,oe(e.$t("消息")),1),Lr(u,{value:l.value,"onUpdate:value":t[2]||(t[2]=e=>l.value=e),type:"textarea",placeholder:e.$t("请描述你遇到的问题"),round:"",class:"mt-1"},null,8,["value","placeholder"])])])),_:1},8,["title"])])),_:1},8,["show"]),Lr(h,{class:"rounded-md",title:e.$t("工单历史")},{"header-extra":hn((()=>[Lr(It(oO),{type:"primary",round:"",onClick:t[7]||(t[7]=e=>r.value=!0)},{default:hn((()=>[Dr(oe(e.$t("新的工单")),1)])),_:1})])),default:hn((()=>[Lr(v,{columns:o,data:c.value,"scroll-x":800},null,8,["data"])])),_:1},8,["title"])])),_:1})}}}),zY=Object.freeze(Object.defineProperty({__proto__:null,default:AY},Symbol.toStringTag,{value:"Module"})),RY={class:"relative",style:{height:"calc(100% - 70px)"}},EY={class:"mb-2 mt-2 text-sm text-gray-500"},OY={class:"mb-2 inline-block rounded-md bg-gray-50 pb-8 pl-4 pr-4 pt-2"},MY=zn({__name:"detail",setup(e){const t=Yl(),n=Et("");function o(){return v(this,null,(function*(){const{data:e}=yield(t=r.value,o=n.value,EN.post("/user/ticket/reply",{id:t,message:o}));var t,o,i;!0===e&&(window.$message.success((i="回复成功",jf.global.t(i))),n.value="",d())}))}const r=Et(),i=Et();function a(){return v(this,null,(function*(){const{data:e}=yield(t=r.value,EN.get("/user/ticket/fetch?id="+t));var t;e&&(i.value=e)}))}const l=Et(null),s=Et(null),c=()=>v(this,null,(function*(){const e=l.value,t=s.value;e&&t&&e.scrollBy({top:t.scrollHeight,behavior:"auto"})})),u=Et();function d(){return v(this,null,(function*(){yield a(),yield tn(),c(),u.value=setInterval(a,2e3)}))}return Dn((()=>{r.value=t.params.ticket_id,d()})),(e,t)=>{const r=w$,a=AE,c=oO,u=RE,d=vO,p=Zj;return _r(),Rr(p,null,{default:hn((()=>{var p;return[Lr(d,{title:null==(p=i.value)?void 0:p.subject,class:"h-full overflow-hidden"},{default:hn((()=>[Ir("div",RY,[Lr(r,{class:"absolute right-0 h-full",ref_key:"scrollbarRef",ref:l},{default:hn((()=>{var e;return[Ir("div",{ref_key:"scrollContainerRef",ref:s},[(_r(!0),zr(yr,null,eo(null==(e=i.value)?void 0:e.message,(e=>(_r(),zr("div",{key:e.id,class:J([e.is_me?"text-right":"text-left"])},[Ir("div",EY,oe(It(Vf)(e.created_at)),1),Ir("div",OY,oe(e.message),1)],2)))),128))],512)]})),_:1},512)]),Lr(u,{size:"large",class:"mt-8"},{default:hn((()=>[Lr(a,{type:"text",size:"large",placeholder:e.$t("输入内容回复工单"),autofocus:!0,value:n.value,"onUpdate:value":t[0]||(t[0]=e=>n.value=e),onKeyup:t[1]||(t[1]=fa((e=>o()),["enter"]))},null,8,["placeholder","value"]),Lr(c,{type:"primary",size:"large",onClick:t[2]||(t[2]=e=>o())},{default:hn((()=>[Dr(oe(e.$t("回复")),1)])),_:1})])),_:1})])),_:1},8,["title"])]})),_:1})}}}),FY=Object.freeze(Object.defineProperty({__proto__:null,default:MY},Symbol.toStringTag,{value:"Module"})),IY=zn({__name:"index",setup(e){const t=e=>jf.global.t(e),n=[{title:t("记录时间"),key:"record_at",render:e=>qf(e.record_at)},{title:t("实际上行"),key:"u",render:e=>Qf(e.u/parseFloat(e.server_rate))},{title:t("实际下行"),key:"d",render:e=>Qf(e.d/parseFloat(e.server_rate))},{title:t("扣费倍率"),key:"server_rate",render:e=>li(qR,{size:"small",round:!0},{default:()=>e.server_rate+" x"})},{title(){const e=li(NM,{placement:"bottom",trigger:"hover"},{trigger:()=>li(j$("mdi-help-circle-outline",{size:16})),default:()=>t("公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量")});return li("div",{class:"flex items-center"},[t("总计"),e])},key:"total",fixed:"right",render:e=>Qf(e.d+e.u)}],o=Et([]);function r(){return v(this,null,(function*(){const{data:e}=yield EN.get("/user/stat/getTrafficLog");o.value=e}))}return Dn((()=>{r()})),(e,t)=>{const r=uE,i=rI,a=vO,l=Zj;return _r(),Rr(l,null,{default:hn((()=>[Lr(a,{class:"rounded-md"},{default:hn((()=>[Lr(r,{type:"info",bordered:!1,class:"mb-5"},{default:hn((()=>[Dr(oe(e.$t("流量明细仅保留近月数据以供查询。")),1)])),_:1}),Lr(i,{columns:n,data:o.value,"scroll-x":600},null,8,["data"])])),_:1})])),_:1})}}}),LY=Object.freeze(Object.defineProperty({__proto__:null,default:IY},Symbol.toStringTag,{value:"Module"})),BY={"h-full":"",flex:""},DY=tj({name:"NOTFOUND"},[["render",function(e,t,n,o,r,i){const a=oO,l=x$;return _r(),zr("div",BY,[Lr(l,{"m-auto":"",status:"404",title:"404 Not Found",description:""},{footer:hn((()=>[Lr(a,null,{default:hn((()=>[Dr("Find some fun")])),_:1})])),_:1})])}]]),$Y=Object.freeze(Object.defineProperty({__proto__:null,default:DY},Symbol.toStringTag,{value:"Module"})),NY={class:"inline-block",viewBox:"0 0 24 24",width:"1em",height:"1em"},jY=[Ir("g",{fill:"none",stroke:"currentColor","stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"1.5"},[Ir("path",{d:"M2 12c0 5.523 4.477 10 10 10s10-4.477 10-10S17.523 2 12 2S2 6.477 2 12"}),Ir("path",{d:"M13 2.05S16 6 16 12s-3 9.95-3 9.95m-2 0S8 18 8 12s3-9.95 3-9.95M2.63 15.5h18.74m-18.74-7h18.74"})],-1)],HY={name:"iconoir-language",render:function(e,t){return _r(),zr("svg",NY,[...jY])}},WY={class:"inline-block",viewBox:"0 0 32 32",width:"1em",height:"1em"},UY=[Ir("path",{fill:"currentColor",d:"M26 30H14a2 2 0 0 1-2-2v-3h2v3h12V4H14v3h-2V4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v24a2 2 0 0 1-2 2"},null,-1),Ir("path",{fill:"currentColor",d:"M14.59 20.59L18.17 17H4v-2h14.17l-3.58-3.59L16 10l6 6l-6 6z"},null,-1)],VY={name:"carbon-login",render:function(e,t){return _r(),zr("svg",WY,[...UY])}},qY=zn({__name:"vueRecaptcha",props:{sitekey:{type:String,required:!0},size:{type:String,required:!1,default:"normal"},theme:{type:String,required:!1,default:"light"},hl:{type:String,required:!1},loadingTimeout:{type:Number,required:!1,default:0}},emits:{verify:e=>null!=e&&""!=e,error:e=>e,expire:null,fail:null},setup(e,{expose:t,emit:n}){const o=e,r=Et(null);let i=null;function a(){i=window.grecaptcha.render(r.value,{sitekey:o.sitekey,theme:o.theme,size:o.size,callback:e=>n("verify",e),"expired-callback":()=>n("expire"),"error-callback":()=>n("fail")})}return t({execute:function(){window.grecaptcha.execute(i)},reset:function(){window.grecaptcha.reset(i)}}),$n((()=>{null==window.grecaptcha?new Promise(((e,t)=>{let n,r=!1;window.recaptchaReady=function(){r||(r=!0,clearTimeout(n),e())};const i="recaptcha-script",a=e=>()=>{var o;r||(r=!0,clearTimeout(n),null==(o=document.getElementById(i))||o.remove(),t(e))};o.loadingTimeout>0&&(n=setTimeout(a("timeout"),o.loadingTimeout));const l=window.document,s=l.createElement("script");s.id=i,s.onerror=a("error"),s.onabort=a("aborted"),s.setAttribute("src",`https://www.recaptcha.net/recaptcha/api.js?onload=recaptchaReady&render=explicit&hl=${o.hl}&_=${+new Date}`),l.head.appendChild(s)})).then((()=>{a()})).catch((e=>{n("error",e)})):a()})),(e,t)=>(_r(),zr("div",{ref_key:"recaptchaDiv",ref:r},null,512))}}),KY="cfTurnstileOnLoad";let GY,XY=typeof window<"u"&&void 0!==window.turnstile?"ready":"unloaded";const YY=zn({name:"VueTurnstile",emits:["update:modelValue","error","unsupported","expired","before-interactive","after-interactive"],props:{siteKey:{type:String,required:!0},modelValue:{type:String,required:!0},resetInterval:{type:Number,required:!1,default:295e3},size:{type:String,required:!1,default:"normal"},theme:{type:String,required:!1,default:"auto"},language:{type:String,required:!1,default:"auto"},action:{type:String,required:!1,default:""},appearance:{type:String,required:!1,default:"always"},renderOnMount:{type:Boolean,required:!1,default:!0}},data:()=>({resetTimeout:void 0,widgetId:void 0}),computed:{turnstileOptions(){return{sitekey:this.siteKey,theme:this.theme,language:this.language,size:this.size,callback:this.callback,action:this.action,appearance:this.appearance,"error-callback":this.errorCallback,"expired-callback":this.expiredCallback,"unsupported-callback":this.unsupportedCallback,"before-interactive-callback":this.beforeInteractiveCallback,"after-interactive-callback":this.afterInteractivecallback}}},methods:{afterInteractivecallback(){this.$emit("after-interactive")},beforeInteractiveCallback(){this.$emit("before-interactive")},expiredCallback(){this.$emit("expired")},unsupportedCallback(){this.$emit("unsupported")},errorCallback(e){this.$emit("error",e)},callback(e){this.$emit("update:modelValue",e),this.startResetTimeout()},reset(){window.turnstile&&(this.$emit("update:modelValue",""),window.turnstile.reset())},remove(){this.widgetId&&(window.turnstile.remove(this.widgetId),this.widgetId=void 0)},render(){this.widgetId=window.turnstile.render(this.$refs.turnstile,this.turnstileOptions)},startResetTimeout(){this.resetTimeout=setTimeout((()=>{this.reset()}),this.resetInterval)}},mounted(){return v(this,null,(function*(){const e=new Promise(((e,t)=>{GY={resolve:e,reject:t},"ready"===XY&&e(void 0)}));window[KY]=()=>{GY.resolve(),XY="ready"},yield(()=>{if("unloaded"===XY){XY="loading";const e=`https://challenges.cloudflare.com/turnstile/v0/api.js?onload=${KY}&render=explicit`,t=document.createElement("script");t.src=e,t.async=!0,t.addEventListener("error",(()=>{GY.reject("Failed to load Turnstile.")})),document.head.appendChild(t)}return e})(),this.renderOnMount&&this.render()}))},beforeUnmount(){this.remove(),clearTimeout(this.resetTimeout)}}),QY={ref:"turnstile"},ZY=((e,t)=>{const n=e.__vccOpts||e;for(const[o,r]of t)n[o]=r;return n})(YY,[["render",function(e,t,n,o,r,i){return _r(),zr("div",QY,null,512)}]]);var JY={},eQ={},tQ={},nQ=fd&&fd.__awaiter||function(e,t,n,o){return new(n||(n=Promise))((function(r,i){function a(e){try{s(o.next(e))}catch(WQ){i(WQ)}}function l(e){try{s(o.throw(e))}catch(WQ){i(WQ)}}function s(e){var t;e.done?r(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,l)}s((o=o.apply(e,t||[])).next())}))},oQ=fd&&fd.__generator||function(e,t){var n,o,r,i,a={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return i={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function l(l){return function(s){return function(l){if(n)throw new TypeError("Generator is already executing.");for(;i&&(i=0,l[0]&&(a=0)),a;)try{if(n=1,o&&(r=2&l[0]?o.return:l[0]?o.throw||((r=o.return)&&r.call(o),0):o.next)&&!(r=r.call(o,l[1])).done)return r;switch(o=0,r&&(l=[2&l[0],r.value]),l[0]){case 0:case 1:r=l;break;case 4:return a.label++,{value:l[1],done:!1};case 5:a.label++,o=l[1],l=[0];continue;case 7:l=a.ops.pop(),a.trys.pop();continue;default:if(!((r=(r=a.trys).length>0&&r[r.length-1])||6!==l[0]&&2!==l[0])){a=0;continue}if(3===l[0]&&(!r||l[1]>r[0]&&l[1]jf.global.t(e),i=Et(null),a=Et(!1),l=Et(!1),s=ai((()=>{var e,t;return(null==(e=x.value)?void 0:e.telegram_login_enable)&&(null==(t=x.value)?void 0:t.telegram_bot_username)})),c=vt({email:"",email_code:"",password:"",confirm_password:"",confirm:"",invite_code:"",lock_invite_code:!1,suffix:""}),u=Et(!0),p=ai((()=>{var e;const t=null==(e=x.value)?void 0:e.tos_url;return"
"+jf.global.tc('我已阅读并同意 服务条款',{url:t})+"
"})),h=Et(!1),f=Et(""),m=Et(""),g=Et(),b=Et(),y=Et(""),x=Et(),C=ai((()=>{var e,t;return(null==(e=x.value)?void 0:e.is_captcha)?(null==(t=x.value)?void 0:t.captcha_type)||"recaptcha":null})),w=ai((()=>{var e,t,n,o;return(null==(e=x.value)?void 0:e.is_captcha)&&("recaptcha"===C.value&&(null==(t=x.value)?void 0:t.recaptcha_site_key)||"recaptcha-v3"===C.value&&(null==(n=x.value)?void 0:n.recaptcha_v3_site_key)||"turnstile"===C.value&&(null==(o=x.value)?void 0:o.turnstile_site_key))}));function k(e){if(e.startsWith("skip_recaptcha"))return{};const t={};switch(C.value){case"recaptcha":t.recaptcha_data=e;break;case"recaptcha-v3":t.recaptcha_v3_token=e;break;case"turnstile":t.turnstile_token=e}return t}function S(e){f.value=e,h.value=!1;const t=y.value;y.value="","register"===t?D():"sendEmailVerify"===t&&E()}function _(){f.value="",T()}function P(){return v(this,null,(function*(){var e;if((null==(e=x.value)?void 0:e.recaptcha_v3_site_key)&&!a.value)try{const e=yield JY.load(x.value.recaptcha_v3_site_key,{autoHideBadge:!0});i.value=e,a.value=!0}catch(t){}}))}function T(){var e,t;"recaptcha"===C.value&&(null==(e=g.value)?void 0:e.reset)?g.value.reset():"turnstile"===C.value&&(null==(t=b.value)?void 0:t.reset)&&b.value.reset(),m.value="",f.value=""}function A(e){return v(this,null,(function*(){return!!w.value&&(f.value="",y.value=e,"recaptcha-v3"===C.value?yield function(e){return v(this,null,(function*(){try{if(a.value||(yield P()),!i.value)return f.value="skip_recaptcha_v3",S("skip_recaptcha_v3"),!0;const t=yield i.value.execute(e);return!!t&&(S(t),!0)}catch(t){return f.value="skip_recaptcha_v3_error",S("skip_recaptcha_v3_error"),!0}}))}(e):(T(),h.value=!0,!0))}))}lr(m,(e=>{e&&y.value&&S(e)}));const z=Et(!1),R=Et(0);function E(){return v(this,null,(function*(){z.value=!0;const e=c.suffix?`${c.email}${c.suffix}`:c.email;try{const t=f.value?k(f.value):void 0,{data:n}=yield function(e,t){return EN.post("/passport/comm/sendEmailVerify",d({email:e},t))}(e,t);if(!0===n){window.$message.success(r("发送成功")),f.value="",R.value=60;const e=setInterval((()=>{R.value--,0===R.value&&clearInterval(e)}),1e3)}}catch(t){throw f.value="",t}finally{z.value=!1}}))}function O(e){!function(e){v(this,null,(function*(){var t,i,a;L.value=!0;try{const l=yield(e=>EN({url:"/passport/auth/telegramLogin",method:"post",data:e}))(e);(null==(t=l.data)?void 0:t.auth_data)?(window.$message.success(r("登录成功")),PN(l.data.auth_data),n.push(null!=(a=null==(i=o.query.redirect)?void 0:i.toString())?a:"/dashboard")):window.$message.error("登录响应格式错误")}catch(l){window.$message.error(r("Telegram 登录失败")+": "+(null==l?void 0:l.message)||"未知错误")}finally{L.value=!1}}))}(e)}function M(){window.onTelegramAuth=function(e){O(e)},window.handleTelegramAuth=O}function F(){return v(this,null,(function*(){if(s.value)try{M(),yield function(){return v(this,null,(function*(){if(!l.value)return new Promise(((e,t)=>{if(document.getElementById("telegram-widget-script"))return l.value=!0,void e();const n=document.createElement("script");n.id="telegram-widget-script",n.src="https://telegram.org/js/telegram-widget.js?22",n.async=!0,n.onload=()=>{l.value=!0,e()},n.onerror=()=>{t(new Error("Failed to load Telegram script"))},document.head.appendChild(n)}))}))}(),setTimeout((()=>{!function(){var e;if(!(null==(e=x.value)?void 0:e.telegram_bot_username))return;const t=document.getElementById("telegram-login-container");if(!t)return;"function"!=typeof window.handleTelegramAuth&&(window.handleTelegramAuth=O),t.innerHTML="";const n=document.createElement("script");n.async=!0,n.src="https://telegram.org/js/telegram-widget.js?22",n.setAttribute("data-telegram-login",x.value.telegram_bot_username),n.setAttribute("data-size","large"),n.setAttribute("data-onauth","onTelegramAuth(user)"),n.setAttribute("data-request-access","write"),n.onerror=()=>{},t.appendChild(n)}()}),200)}catch(e){}}))}function I(){return v(this,null,(function*(){var e,t;const{data:n}=yield EN.get("/guest/comm/config");n&&(x.value=n,Zf(n.email_whitelist_suffix)&&(c.suffix=(null==(e=n.email_whitelist_suffix)?void 0:e[0])?"@"+(null==(t=n.email_whitelist_suffix)?void 0:t[0]):""),n.tos_url&&(u.value=!1),"recaptcha-v3"===n.captcha_type&&n.recaptcha_v3_site_key&&(yield P()),n.telegram_login_enable&&n.telegram_bot_username&&(yield F()))}))}const L=Et(!1);function B(){return v(this,null,(function*(){const{email:e,password:t,confirm_password:i}=c;switch($.value){case"login":yield function(){return v(this,null,(function*(){var e,t;const{email:i,password:a}=c;if(i&&a){L.value=!0;try{const{data:l}=yield(e=>EN({url:"/passport/auth/login",method:"post",data:e}))({email:i,password:a.toString()});(null==l?void 0:l.auth_data)&&(window.$message.success(r("登录成功")),PN(null==l?void 0:l.auth_data),n.push(null!=(t=null==(e=o.query.redirect)?void 0:e.toString())?t:"/dashboard"))}finally{L.value=!1}}else window.$message.warning(r("请输入用户名和密码"))}))}();break;case"register":if(""===c.email)return void window.$message.error(r("请输入邮箱地址"));if(!e||!t)return void window.$message.warning(r("请输入账号密码"));if(t!==i)return void window.$message.warning(r("请确保两次密码输入一致"));if(yield A("register"))return;D();break;case"forgetpassword":yield function(){return v(this,null,(function*(){const{email:e,password:t,confirm_password:o,email_code:i}=c;if(""!==e)if(e&&t)if(t===o){L.value=!0;try{const e=c.suffix?`${c.email}${c.suffix}`:c.email,{data:o}=yield function(e,t,n){return EN.post("/passport/auth/forget",{email:e,password:t,email_code:n})}(e,t,i);o&&(window.$message.success(r("重置密码成功,正在返回登录")),setTimeout((()=>{n.push("/login")}),500))}finally{L.value=!1}}else window.$message.warning(r("请确保两次密码输入一致"));else window.$message.warning(r("请输入账号密码"));else window.$message.error(r("请输入邮箱地址"))}))}()}}))}function D(){return v(this,null,(function*(){const{password:e,invite_code:t,email_code:o}=c,i=c.suffix?`${c.email}${c.suffix}`:c.email;L.value=!0;try{const a=f.value?k(f.value):{},{data:l}=yield(e=>EN({url:"/passport/auth/register",method:"post",data:e}))(d({email:i,password:e,invite_code:t,email_code:o},a));(null==l?void 0:l.auth_data)&&(window.$message.success(r("注册成功")),PN(l.auth_data),f.value="",n.push("/"))}catch(a){throw f.value="",a}finally{L.value=!1}}))}const $=ai((()=>{const e=o.path;return e.includes("login")?"login":e.includes("register")?"register":e.includes("forgetpassword")?"forgetpassword":""})),N=()=>v(this,null,(function*(){"login"===$.value&&(M(),function(){const e=new URLSearchParams(window.location.search);if(["id","first_name","username","auth_date","hash"].some((t=>e.has(t)))){const t={id:parseInt(e.get("id")||"0"),first_name:e.get("first_name")||"",last_name:e.get("last_name")||void 0,username:e.get("username")||void 0,photo_url:e.get("photo_url")||void 0,auth_date:parseInt(e.get("auth_date")||"0"),hash:e.get("hash")||""},n=window.location.pathname+window.location.hash;window.history.replaceState({},document.title,n),O(t)}}()),["register","forgetpassword","login"].includes($.value)&&I(),o.query.code&&(c.lock_invite_code=!0,c.invite_code=o.query.code);const{verify:e,redirect:t}=o.query;if(e&&t){const{data:o}=yield(e=>EN.get("/passport/auth/token2Login?verify="+encodeURIComponent(e.verify)+"&redirect="+encodeURIComponent(e.redirect)))({verify:e,redirect:t});(null==o?void 0:o.auth_data)&&(window.$message.success(r("登录成功")),PN(null==o?void 0:o.auth_data),n.push(t.toString()))}}));return ir((()=>{N()})),(e,n)=>{const o=RI,i=AE,a=hM,d=RE,f=oO,y=GO,w=VY,k=Xn("router-link"),P=DI,T=HY,O=sM,M=vO;return _r(),zr(yr,null,[Lr(o,{show:h.value,"onUpdate:show":n[1]||(n[1]=e=>h.value=e),"mask-closable":!1},{default:hn((()=>{var e,t;return["recaptcha"===C.value&&(null==(e=x.value)?void 0:e.recaptcha_site_key)?(_r(),Rr(It(qY),{key:0,sitekey:x.value.recaptcha_site_key,size:"normal",theme:"light","loading-timeout":3e4,onVerify:S,onExpire:_,onFail:_,onError:_,ref_key:"vueRecaptchaRef",ref:g},null,8,["sitekey"])):"turnstile"===C.value&&(null==(t=x.value)?void 0:t.turnstile_site_key)?(_r(),Rr(It(ZY),{key:1,siteKey:x.value.turnstile_site_key,theme:"auto",modelValue:m.value,"onUpdate:modelValue":n[0]||(n[0]=e=>m.value=e),onError:_,onExpired:_,ref_key:"vueTurnstileRef",ref:b},null,8,["siteKey","modelValue"])):$r("",!0)]})),_:1},8,["show"]),Ir("div",{class:"wh-full flex items-center justify-center",style:G(It(t).background_url&&`background:url(${It(t).background_url}) no-repeat center center / cover;`)},[Lr(M,{class:"mx-auto max-w-md rounded-md bg-[--n-color] shadow-black","content-style":"padding: 0;"},{default:hn((()=>{var o,h,m;return[Ir("div",uQ,[It(t).logo?(_r(),zr("div",dQ,[Ir("img",{src:It(t).logo,class:"mb-1em max-w-full"},null,8,pQ)])):(_r(),zr("h1",hQ,oe(It(t).title),1)),Ir("h5",fQ,oe(It(t).description||" "),1),Ir("div",vQ,[Lr(d,null,{default:hn((()=>{var t,o,r;return[Lr(i,{type:"email",value:c.email,"onUpdate:value":n[2]||(n[2]=e=>c.email=e),autofocus:"",placeholder:e.$t("邮箱"),maxlength:40,"input-props":{autocomplete:"email"}},null,8,["value","placeholder"]),["register","forgetpassword"].includes($.value)&&It(Zf)(null==(t=x.value)?void 0:t.email_whitelist_suffix)?(_r(),Rr(a,{key:0,value:c.suffix,"onUpdate:value":n[3]||(n[3]=e=>c.suffix=e),options:(null==(r=null==(o=x.value)?void 0:o.email_whitelist_suffix)?void 0:r.map((e=>({value:`@${e}`,label:`@${e}`}))))||[],class:"flex-[1]","consistent-menu-width":!1},null,8,["value","options"])):$r("",!0)]})),_:1})]),fn(Ir("div",mQ,[Lr(d,{class:"flex"},{default:hn((()=>[Lr(i,{value:c.email_code,"onUpdate:value":n[4]||(n[4]=e=>c.email_code=e),placeholder:e.$t("邮箱验证码")},null,8,["value","placeholder"]),Lr(f,{type:"primary",onClick:n[5]||(n[5]=e=>function(){return v(this,null,(function*(){""!==c.email?R.value>0?window.$message.warning(jf.global.tc("{second}秒后可重新发送",{second:R.value})):(yield A("sendEmailVerify"))||E():window.$message.error(r("请输入邮箱地址"))}))}()),loading:z.value,disabled:z.value||R.value>0},{default:hn((()=>[Dr(oe(R.value||e.$t("发送")),1)])),_:1},8,["loading","disabled"])])),_:1})],512),[[Mi,["register"].includes($.value)&&(null==(o=x.value)?void 0:o.is_email_verify)||["forgetpassword"].includes($.value)]]),Ir("div",gQ,[Lr(i,{value:c.password,"onUpdate:value":n[6]||(n[6]=e=>c.password=e),class:"",type:"password","show-password-on":"click",placeholder:e.$t("密码"),maxlength:40,"input-props":{autocomplete:"current-password"},onKeydown:n[7]||(n[7]=fa((e=>["login"].includes($.value)&&B()),["enter"]))},null,8,["value","placeholder"])]),fn(Ir("div",bQ,[Lr(i,{value:c.confirm_password,"onUpdate:value":n[8]||(n[8]=e=>c.confirm_password=e),type:"password","show-password-on":"click",placeholder:e.$t("再次输入密码"),maxlength:40,onKeydown:n[9]||(n[9]=fa((e=>["forgetpassword"].includes($.value)&&B()),["enter"]))},null,8,["value","placeholder"])],512),[[Mi,["register","forgetpassword"].includes($.value)]]),fn(Ir("div",yQ,[Lr(i,{value:c.invite_code,"onUpdate:value":n[10]||(n[10]=e=>c.invite_code=e),placeholder:[e.$t("邀请码"),(null==(h=x.value)?void 0:h.is_invite_force)?`(${e.$t("必填")})`:`(${e.$t("选填")})`],maxlength:20,disabled:c.lock_invite_code,onKeydown:n[11]||(n[11]=fa((e=>B()),["enter"]))},null,8,["value","placeholder","disabled"])],512),[[Mi,["register"].includes($.value)]]),fn(Ir("div",xQ,[Lr(y,{checked:u.value,"onUpdate:checked":n[12]||(n[12]=e=>u.value=e),class:"text-bold text-base"},{default:hn((()=>[Ir("div",{innerHTML:p.value},null,8,CQ)])),_:1},8,["checked"])],512),[[Mi,["register"].includes($.value)&&(null==(m=x.value)?void 0:m.tos_url)]]),Ir("div",wQ,[Lr(f,{class:"h-9 w-full rounded-md text-base",type:"primary","icon-placement":"left",onClick:n[13]||(n[13]=e=>B()),loading:L.value,disabled:L.value||!u.value&&["register"].includes($.value)},{icon:hn((()=>[Lr(w)])),default:hn((()=>[Dr(" "+oe(["login"].includes($.value)?e.$t("登入"):["register"].includes($.value)?e.$t("注册"):e.$t("重置密码")),1)])),_:1},8,["loading","disabled"])]),["login"].includes($.value)&&s.value?(_r(),zr("div",kQ,[Ir("div",SQ,[_Q,Ir("span",PQ,oe(e.$t("或使用第三方登录")),1),TQ]),Ir("div",AQ,[l.value?(_r(),zr("div",zQ)):(_r(),zr("div",RQ,[Lr(f,{class:"h-10 w-full rounded-md text-base",type:"info",loading:!0,disabled:""},{icon:hn((()=>[EQ])),default:hn((()=>[Dr(" "+oe(e.$t("正在加载 Telegram 登录...")),1)])),_:1})]))])])):$r("",!0)]),Ir("div",OQ,[Ir("div",null,[["login"].includes($.value)?(_r(),zr(yr,{key:0},[Lr(k,{to:"/register",class:"text-gray-500"},{default:hn((()=>[Dr(oe(e.$t("注册")),1)])),_:1}),Lr(P,{vertical:""}),Lr(k,{to:"/forgetpassword",class:"text-gray-500"},{default:hn((()=>[Dr(oe(e.$t("忘记密码")),1)])),_:1})],64)):(_r(),Rr(k,{key:1,to:"/login",class:"text-gray-500"},{default:hn((()=>[Dr(oe(e.$t("返回登入")),1)])),_:1}))]),Ir("div",null,[Lr(O,{value:It(t).lang,"onUpdate:value":n[14]||(n[14]=e=>It(t).lang=e),options:Object.entries(It(Wf)).map((([e,t])=>({label:t,value:e}))),trigger:"click","on-update:value":It(t).switchLang},{default:hn((()=>[Lr(f,{text:"","icon-placement":"left"},{icon:hn((()=>[Lr(T)])),default:hn((()=>[Dr(" "+oe(It(Wf)[It(t).lang]),1)])),_:1})])),_:1},8,["value","options","on-update:value"])])])]})),_:1})],4)],64)}}}),FQ=Object.freeze(Object.defineProperty({__proto__:null,default:MQ},Symbol.toStringTag,{value:"Module"})),IQ=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"Request failed","月付":"Monthly","季付":"Quarterly","半年付":"Semi-Annually","年付":"Annually","两年付":"Biennially","三年付":"Triennially","一次性":"One Time","重置流量包":"Data Reset Package","待支付":"Pending Payment","开通中":"Pending Active","已取消":"Canceled","已完成":"Completed","已折抵":"Converted","待确认":"Pending","发放中":"Confirming","已发放":"Completed","无效":"Invalid","个人中心":"User Center","登出":"Logout","搜索":"Search","仪表盘":"Dashboard","订阅":"Subscription","我的订阅":"My Subscription","购买订阅":"Purchase Subscription","财务":"Billing","我的订单":"My Orders","我的邀请":"My Invitation","用户":"Account","我的工单":"My Tickets","流量明细":"Transfer Data Details","使用文档":"Knowledge Base","绑定Telegram获取更多服务":"Not link to Telegram yet","点击这里进行绑定":"Please click here to link to Telegram","公告":"Announcements","总览":"Overview","该订阅长期有效":"The subscription is valid for an unlimited time","已过期":"Expired","已用 {used} / 总计 {total}":"{used} Used / Total {total}","查看订阅":"View Subscription","邮箱":"Email","邮箱验证码":"Email verification code","发送":"Send","重置密码":"Reset Password","返回登入":"Back to Login","邀请码":"Invitation Code","复制链接":"Copy Link","完成时间":"Complete Time","佣金":"Commission","已注册用户数":"Registered users","佣金比例":"Commission rate","确认中的佣金":"Pending commission","佣金将会在确认后会到达你的佣金账户。":"The commission will reach your commission account after review.","邀请码管理":"Invitation Code Management","生成邀请码":"Generate invitation code","佣金发放记录":"Commission Income Record","复制成功":"Copied successfully","密码":"Password","登入":"Login","注册":"Register","忘记密码":"Forgot password","# 订单号":"Order Number #","周期":"Type / Cycle","订单金额":"Order Amount","订单状态":"Order Status","创建时间":"Creation Time","操作":"Action","查看详情":"View Details","请选择支付方式":"Please select a payment method","请检查信用卡支付信息":"Please check credit card payment information","订单详情":"Order Details","折扣":"Discount","折抵":"Converted","退款":"Refund","支付方式":"Payment Method","填写信用卡支付信息":"Please fill in credit card payment information","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"We will not collect your credit card information, credit card number and other details only use to verify the current transaction.","订单总额":"Order Total","总计":"Total","结账":"Checkout","等待支付中":"Waiting for payment","订单系统正在进行处理,请稍等1-3分钟。":"Order system is being processed, please wait 1 to 3 minutes.","订单由于超时支付已被取消。":"The order has been canceled due to overtime payment.","订单已支付并开通。":"The order has been paid and the service is activated.","选择订阅":"Select a Subscription","立即订阅":"Subscribe now","配置订阅":"Configure Subscription","付款周期":"Payment Cycle","有优惠券?":"Have coupons?","验证":"Verify","下单":"Order","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"Attention please, change subscription will overwrite your current subscription.","该订阅无法续费":"This subscription cannot be renewed","选择其他订阅":"Choose another subscription","我的钱包":"My Wallet","账户余额(仅消费)":"Account Balance (For billing only)","推广佣金(可提现)":"Invitation Commission (Can be used to withdraw)","钱包组成部分":"Wallet Details","划转":"Transfer","推广佣金提现":"Invitation Commission Withdrawal","修改密码":"Change Password","保存":"Save","旧密码":"Old Password","新密码":"New Password","请输入旧密码":"Please enter the old password","请输入新密码":"Please enter the new password","通知":"Notification","到期邮件提醒":"Subscription expiration email reminder","流量邮件提醒":"Insufficient transfer data email alert","绑定Telegram":"Link to Telegram","立即开始":"Start Now","重置订阅信息":"Reset Subscription","重置":"Reset","确定要重置订阅信息?":"Do you want to reset subscription?","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"In case of your account information or subscription leak, this option is for reset. After resetting your UUID and subscription will change, you need to re-subscribe.","重置成功":"Reset successfully","两次新密码输入不同":"Two new passwords entered do not match","两次密码输入不同":"The passwords entered do not match","邀请码(选填)":"Invitation code (Optional)",'我已阅读并同意 服务条款':'I have read and agree to the terms of service',"请同意服务条款":"Please agree to the terms of service","名称":"Name","标签":"Tags","状态":"Status","节点五分钟内节点在线情况":"Access Point online status in the last 5 minutes","倍率":"Rate","使用的流量将乘以倍率进行扣除":"The transfer data usage will be multiplied by the transfer data rate deducted.","更多操作":"Action","没有可用节点,如果您未订阅或已过期请":"No access points are available. If you have not subscribed or the subscription has expired, please","确定重置当前已用流量?":"Are you sure to reset your current data usage?","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":'Click "Confirm" and you will be redirected to the payment page. The system will empty your current month"s usage after your purchase.',"确定":"Confirm","低":"Low","中":"Medium","高":"High","主题":"Subject","工单级别":"Ticket Priority","工单状态":"Ticket Status","最后回复":"Last Reply","已关闭":"Closed","待回复":"Pending Reply","已回复":"Replied","查看":"View","关闭":"Cancel","新的工单":"My Tickets","确认":"Confirm","请输入工单主题":"Please enter a subject","工单等级":"Ticket Priority","请选择工单等级":"Please select the ticket priority","消息":"Message","请描述你遇到的问题":"Please describe the problem you encountered","记录时间":"Record Time","实际上行":"Actual Upload","实际下行":"Actual Download","合计":"Total","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"Formula: (Actual Upload + Actual Download) x Deduction Rate = Deduct Transfer Data","复制订阅地址":"Copy Subscription URL","导入到":"Export to","一键订阅":"Quick Subscription","复制订阅":"Copy Subscription URL","推广佣金划转至余额":"Transfer Invitation Commission to Account Balance","划转后的余额仅用于{title}消费使用":"The transferred balance will be used for {title} payments only","当前推广佣金余额":"Current invitation balance","划转金额":"Transfer amount","请输入需要划转到余额的金额":"Please enter the amount to be transferred to the balance","输入内容回复工单...":"Please enter to reply to the ticket...","申请提现":"Apply For Withdrawal","取消":"Cancel","提现方式":"Withdrawal Method","请选择提现方式":"Please select a withdrawal method","提现账号":"Withdrawal Account","请输入提现账号":"Please enter the withdrawal account","我知道了":"I got it","第一步":"First Step","第二步":"Second Step","打开Telegram搜索":"Open Telegram and Search ","向机器人发送你的":"Send the following command to bot","最后更新: {date}":"Last Updated: {date}","还有没支付的订单":"There are still unpaid orders","立即支付":"Pay Now","条工单正在处理中":"tickets are in process","立即查看":"View Now","节点状态":"Access Point Status","商品信息":"Product Information","产品名称":"Product Name","类型/周期":"Type / Cycle","产品流量":"Product Transfer Data","订单信息":"Order Details","关闭订单":"Close order","订单号":"Order Number","优惠金额":"Discount amount","旧订阅折抵金额":"Old subscription converted amount","退款金额":"Refunded amount","余额支付":"Balance payment","工单历史":"Ticket History","已用流量将在 {time} 重置":"Used data will reset at {time}","已用流量已在今日重置":"Data usage has been reset today","重置已用流量":"Reset used data","查看节点状态":"View Access Point status","当前已使用流量达{rate}%":"Currently used data up to {rate}%","节点名称":"Access Point Name","于 {date} 到期,距离到期还有 {day} 天。":"Will expire on {date}, {day} days before expiration.","Telegram 讨论组":"Telegram Discussion Group","立即加入":"Join Now","该订阅无法续费,仅允许新用户购买":"This subscription cannot be renewed and is only available to new users.","重置当月流量":"Reset current month usage","流量明细仅保留近月数据以供查询。":'Only keep the most recent month"s usage for checking the transfer data details.',"扣费倍率":"Fee deduction rate","支付手续费":"Payment fee","续费订阅":"Renewal Subscription","学习如何使用":"Learn how to use","快速将节点导入对应客户端进行使用":"Quickly export subscription into the client app","对您当前的订阅进行续费":"Renew your current subscription","对您当前的订阅进行购买":"Purchase your current subscription","捷径":"Shortcut","不会使用,查看使用教程":"I am a newbie, view the tutorial","使用支持扫码的客户端进行订阅":"Use a client app that supports scanning QR code to subscribe","扫描二维码订阅":"Scan QR code to subscribe","续费":"Renewal","购买":"Purchase","查看教程":"View Tutorial","注意":"Attention","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"You still have an unpaid order. You need to cancel it before purchasing. Are you sure you want to cancel the previous order?","确定取消":"Confirm Cancel","返回我的订单":"Back to My Order","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"If you have already paid, canceling the order may cause the payment to fail. Are you sure you want to cancel the order?","选择最适合你的计划":"Choose the right plan for you","全部":"All","按周期":"By Cycle","遇到问题":"I have a problem","遇到问题可以通过工单与我们沟通":"If you have any problems, you can contact us via ticket","按流量":"Pay As You Go","搜索文档":"Search Documents","技术支持":"Technical Support","当前剩余佣金":"Current commission remaining","三级分销比例":"Three-level Distribution Ratio","累计获得佣金":"Cumulative commission earned","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"The users you invite to re-invite users will be divided according to the order amount multiplied by the distribution level.","发放时间":"Commission Time","{number} 人":"{number} people","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"If your subscription address or account is leaked and misused by others, you can reset your subscription information here to prevent unnecessary losses.","再次输入密码":"Enter password again","返回登陆":"Return to Login","选填":"Optional","必填":"Required","最后回复时间":"Last Reply Time","请选项工单等级":"Please Select Ticket Priority","回复":"Reply","输入内容回复工单":"Enter Content to Reply to Ticket","已生成":"Generated","选择协议":"Select Protocol","自动":"Automatic","流量重置包":"Data Reset Package","复制失败":"Copy failed","提示":"Notification","确认退出?":"Confirm Logout?","已退出登录":"Logged out successfully","请输入邮箱地址":"Enter email address","{second}秒后可重新发送":"Resend available in {second} seconds","发送成功":"Sent successfully","请输入账号密码":"Enter account and password","请确保两次密码输入一致":"Ensure password entries match","注册成功":"Registration successful","重置密码成功,正在返回登录":"Password reset successful, returning to login","确认取消":"Confirm Cancel","请注意,变更订阅会导致当前订阅被覆盖。":"Please note that changing the subscription will overwrite the current subscription.","订单提交成功,正在跳转支付":"Order submitted successfully, redirecting to payment.","回复成功":"Reply Successful","工单详情":"Ticket Details","登录成功":"Login Successful","确定退出?":"Are you sure you want to exit?","支付成功":"Payment Successful","正在前往收银台":"Proceeding to Checkout","请输入正确的划转金额":"Please enter the correct transfer amount","划转成功":"Transfer Successful","提现方式不能为空":"Withdrawal method cannot be empty","提现账号不能为空":"Withdrawal account cannot be empty","已绑定":"Already Bound","创建成功":"Creation successful","关闭成功":"Shutdown successful","或使用第三方登录":"Or sign in with","正在加载 Telegram 登录...":"Loading Telegram login...","Telegram 登录失败":"Telegram login failed","下次流量重置时间":"Next Reset Time: "}},Symbol.toStringTag,{value:"Module"})),LQ=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"درخواست انجام نشد","月付":"ماهانه","季付":"سه ماهه","半年付":"نیم سال","年付":"سالانه","两年付":"دو سال","三年付":"سه سال","一次性":"یک‌باره","重置流量包":"بازنشانی بسته های داده","待支付":"در انتظار پرداخت","开通中":"ایجاید","已取消":"صرف نظر شد","已完成":"به پایان رسید","已折抵":"تخفیف داده شده است","待确认":"در حال بررسی","发放中":"صدور","已发放":"صادر شده","无效":"نامعتبر","个人中心":"پروفایل","登出":"خروج","搜索":"جستجو","仪表盘":"داشبرد","订阅":"اشتراک","我的订阅":"اشتراک من","购买订阅":"خرید اشتراک","财务":"امور مالی","我的订单":"درخواست های من","我的邀请":"دعوتنامه های من","用户":"کاربر","我的工单":"درخواست های من","流量明细":"جزئیات\\nعبورو مرور در\\nمحیط آموزشی","使用文档":"کار با مستندات","绑定Telegram获取更多服务":"برای خدمات بیشتر تلگرام را ببندید","点击这里进行绑定":"برای اتصال اینجا را کلیک کنید","公告":"هشدارها","总览":"بررسی کلی","该订阅长期有效":"این اشتراک برای مدت طولانی معتبر است","已过期":"منقضی شده","已用 {used} / 总计 {total}":"استفاده شده {used} / مجموع {total}","查看订阅":"مشاهده عضویت ها","邮箱":"ایمیل","邮箱验证码":"کد تایید ایمیل شما","发送":"ارسال","重置密码":"بازنشانی رمز عبور","返回登入":"بازگشت به صفحه ورود","邀请码":"کد دعوت شما","复制链接":"کپی‌کردن لینک","完成时间":"زمان پایان","佣金":"کمیسیون","已注册用户数":"تعداد کاربران ثبت نام شده","佣金比例":"نرخ کمیسیون","确认中的佣金":"کمیسیون تایید شده","佣金将会在确认后会到达你的佣金账户。":"کمیسیون پس از تایید به حساب کمیسیون شما واریز خواهد شد","邀请码管理":"مدیریت کد دعوت","生成邀请码":"یک کد دعوت ایجاد کنید","佣金发放记录":"سابقه پرداخت کمیسیون","复制成功":"آدرس URL با موفقیت کپی شد","密码":"رمز عبور","登入":"ورود","注册":"ثبت‌نام","忘记密码":"رمز عبور فراموش شده","# 订单号":"# شماره سفارش","周期":"چرخه","订单金额":"مقدار سفارش","订单状态":"وضعیت سفارش","创建时间":"ساختن","操作":"عملیات","查看详情":"مشاهده جزئیات","请选择支付方式":"لطفا نوع پرداخت را انتخاب کنید","请检查信用卡支付信息":"لطفا اطلاعات پرداخت کارت اعتباری خود را بررسی کنید","订单详情":"اطلاعات سفارش","折扣":"ذخیره","折抵":"折抵","退款":"بازگشت هزینه","支付方式":"روش پرداخت","填写信用卡支付信息":"لطفا اطلاعات پرداخت کارت اعتباری خود را بررسی کنید","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"اطلاعات کارت اعتباری شما فقط برای بدهی فعلی استفاده می شود، سیستم آن را ذخیره نمی کند، که ما فکر می کنیم امن ترین است.","订单总额":"مجموع سفارش","总计":"مجموع","结账":"پرداخت","等待支付中":"در انتظار پرداخت","订单系统正在进行处理,请稍等1-3分钟。":"سیستم سفارش در حال پردازش است، لطفا 1-3 دقیقه صبر کنید.","订单由于超时支付已被取消。":"سفارش به دلیل پرداخت اضافه کاری لغو شده است","订单已支付并开通。":"سفارش پرداخت و باز شد.","选择订阅":"انتخاب اشتراک","立即订阅":"همین حالا مشترک شوید","配置订阅":"پیکربندی اشتراک","付款周期":"چرخه پرداخت","有优惠券?":"یک کوپن دارید؟","验证":"تأیید","下单":"ایجاد سفارش","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"لطفاً توجه داشته باشید، تغییر یک اشتراک باعث می‌شود که اشتراک فعلی توسط اشتراک جدید بازنویسی شود.","该订阅无法续费":"این اشتراک قابل تمدید نیست","选择其他订阅":"اشتراک دیگری را انتخاب کنید","我的钱包":"کیف پول من","账户余额(仅消费)":"موجودی حساب (فقط خرج کردن)","推广佣金(可提现)":"کمیسیون ارتقاء (قابل برداشت)","钱包组成部分":"اجزای کیف پول","划转":"منتقل کردن","推广佣金提现":"انصراف کمیسیون ارتقاء","修改密码":"تغییر کلمه عبور","保存":"ذخیره کردن","旧密码":"گذرواژه قدیمی","新密码":"رمز عبور جدید","请输入旧密码":", رمز عبور مورد نیاز است","请输入新密码":"گذاشتن گذرواژه","通知":"اعلانات","到期邮件提醒":"یادآوری ایمیل انقضا","流量邮件提醒":"یادآوری ایمیل ترافیک","绑定Telegram":"تلگرام را ببندید","立即开始":"امروز شروع کنید","重置订阅信息":"بازنشانی اطلاعات اشتراک","重置":"تغییر","确定要重置订阅信息?":"آیا مطمئن هستید که می خواهید اطلاعات اشتراک خود را بازنشانی کنید؟","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"اگر آدرس یا اطلاعات اشتراک شما لو رفته باشد، این کار را می توان انجام داد. پس از تنظیم مجدد، Uuid و اشتراک شما تغییر خواهد کرد و باید دوباره مشترک شوید.","重置成功":"بازنشانی با موفقیت انجام شد","两次新密码输入不同":"رمز جدید را دو بار وارد کنید","两次密码输入不同":"رمز جدید را دو بار وارد کنید","邀请码(选填)":"کد دعوت (اختیاری)",'我已阅读并同意 服务条款':"من شرایط خدمات را خوانده‌ام و با آن موافقم","请同意服务条款":"لطفاً با شرایط خدمات موافقت کنید","名称":"نام ویژگی محصول","标签":"برچسب‌ها","状态":"وضعیت","节点五分钟内节点在线情况":"وضعیت آنلاین گره را در عرض پنج دقیقه ثبت کنید","倍率":"بزرگنمایی","使用的流量将乘以倍率进行扣除":"جریان استفاده شده در ضریب برای کسر ضرب خواهد شد","更多操作":"اکشن های بیشتر","没有可用节点,如果您未订阅或已过期请":"هیچ گره ای در دسترس نیست، اگر مشترک نیستید یا منقضی شده اید، لطفاً","确定重置当前已用流量?":"آیا مطمئن هستید که می خواهید داده های استفاده شده فعلی را بازنشانی کنید؟","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":"برای رفتن به صندوقدار روی 'OK' کلیک کنید. پس از پرداخت سفارش، سیستم اطلاعاتی را که برای ماه استفاده کرده اید پاک می کند.","确定":"تأیید","低":"پایین","中":"متوسط","高":"بالا","主题":"موضوع","工单级别":"سطح بلیط","工单状态":"وضعیت درخواست","最后回复":"آخرین پاسخ","已关闭":"پایان‌یافته","待回复":"در انتظار پاسخ","已回复":"پاسخ داده","查看":"بازدیدها","关闭":"بستن","新的工单":"سفارش کار جدید","确认":"تاييدات","请输入工单主题":"لطفا موضوع بلیط را وارد کنید","工单等级":"سطح سفارش کار","请选择工单等级":"لطفا سطح بلیط را انتخاب کنید","消息":"پیام ها","请描述你遇到的问题":"لطفا مشکلی که با آن مواجه شدید را شرح دهید","记录时间":"زمان ضبط","实际上行":"نقطه ضعف واقعی","实际下行":"نقطه ضعف واقعی","合计":"تعداد ارزش‌ها","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"فرمول: (خط واقعی + پایین دست واقعی) x نرخ کسر = ترافیک کسر شده","复制订阅地址":"آدرس اشتراک را کپی کنید","导入到":"واردات در:","一键订阅":"اشتراک با یک کلیک","复制订阅":"اشتراک را کپی کنید","推广佣金划转至余额":"کمیسیون ارتقاء به موجودی منتقل می شود","划转后的余额仅用于{title}消费使用":"موجودی منتقل شده فقط برای مصرف {title} استفاده می شود","当前推广佣金余额":"موجودی کمیسیون ترفیع فعلی","划转金额":"مقدار انتقال","请输入需要划转到余额的金额":"لطفا مبلغی را که باید به موجودی منتقل شود وارد کنید","输入内容回复工单...":"برای پاسخ به تیکت محتوا را وارد کنید...","申请提现":"برای انصراف اقدام کنید","取消":"انصراف","提现方式":"روش برداشت","请选择提现方式":"لطفاً یک روش برداشت را انتخاب کنید","提现账号":"حساب برداشت","请输入提现账号":"لطفا حساب برداشت را وارد کنید","我知道了":"می فهمم","第一步":"گام ۱","第二步":"گام ۲","打开Telegram搜索":"جستجوی تلگرام را باز کنید","向机器人发送你的":"ربات های خود را بفرستید","最后更新: {date}":"آخرین به روز رسانی: {date}","还有没支付的订单":"هنوز سفارشات پرداخت نشده وجود دارد","立即支付":"اکنون پرداخت کنید","条工单正在处理中":"بلیط در حال پردازش است","立即查看":"آن را در عمل ببینید","节点状态":"وضعیت گره","商品信息":"مشتریان ثبت نام شده","产品名称":"عنوان کالا","类型/周期":"نوع/چرخه","产品流量":"جریان محصول","订单信息":"اطلاعات سفارش","关闭订单":"سفارش بستن","订单号":"شماره سفارش","优惠金额":"قیمت با تخفیف","旧订阅折抵金额":"مبلغ تخفیف اشتراک قدیمی","退款金额":"کل مبلغ مسترد شده","余额支付":"پرداخت مانده","工单历史":"تاریخچه بلیط","已用流量将在 {time} 重置":"حجم مصرفی در {time} بازنشانی خواهد شد","已用流量已在今日重置":"امروز بازنشانی داده استفاده شده است","重置已用流量":"بازنشانی داده های استفاده شده","查看节点状态":"مشاهده وضعیت گره","当前已使用流量达{rate}%":"ترافیک استفاده شده در حال حاضر در {rate}%","节点名称":"نام گره","于 {date} 到期,距离到期还有 {day} 天。":"در {date} منقضی می‌شود که {day} روز دیگر است.","Telegram 讨论组":"گروه گفتگوی تلگرام","立即加入":"حالا پیوستن","该订阅无法续费,仅允许新用户购买":"این اشتراک قابل تمدید نیست، فقط کاربران جدید مجاز به خرید آن هستند","重置当月流量":"بازنشانی ترافیک ماه جاری","流量明细仅保留近月数据以供查询。":"جزئیات ترافیک فقط داده های ماه های اخیر را برای پرس و جو حفظ می کند.","扣费倍率":"نرخ کسر","支付手续费":"پرداخت هزینه های پردازش","续费订阅":"تمدید اشتراک","学习如何使用":"نحوه استفاده را یاد بگیرید","快速将节点导入对应客户端进行使用":"به سرعت گره ها را برای استفاده به مشتری مربوطه وارد کنید","对您当前的订阅进行续费":"با اشتراک فعلی خود خرید کنید","对您当前的订阅进行购买":"با اشتراک فعلی خود خرید کنید","捷径":"میانبر","不会使用,查看使用教程":"استفاده نمی شود، به آموزش مراجعه کنید","使用支持扫码的客户端进行订阅":"برای اشتراک از کلاینتی استفاده کنید که از کد اسکن پشتیبانی می کند","扫描二维码订阅":"برای اشتراک، کد QR را اسکن کنید","续费":"تمدید","购买":"خرید","查看教程":"مشاهده آموزش","注意":"یادداشت!","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"هنوز سفارشات ناتمام دارید. قبل از خرید باید آن را لغو کنید. آیا مطمئن هستید که می‌خواهید سفارش قبلی را لغو کنید؟","确定取消":"تایید لغو","返回我的订单":"بازگشت به سفارش من","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"اگر قبلاً پرداخت کرده‌اید، لغو سفارش ممکن است باعث عدم موفقیت در پرداخت شود. آیا مطمئن هستید که می‌خواهید سفارش را لغو کنید؟","选择最适合你的计划":"طرحی را انتخاب کنید که مناسب شما باشد","全部":"تمام","按周期":"توسط چرخه","遇到问题":"ما یک مشکل داریم","遇到问题可以通过工单与我们沟通":"در صورت بروز مشکل می توانید از طریق تیکت با ما در ارتباط باشید","按流量":"با جریان","搜索文档":"جستجوی اسناد","技术支持":"دریافت پشتیبانی","当前剩余佣金":"کمیسیون فعلی باقی مانده","三级分销比例":"نسبت توزیع سه لایه","累计获得佣金":"کمیسیون انباشته شده","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"کاربرانی که برای دعوت مجدد از کاربران دعوت می کنید بر اساس نسبت مقدار سفارش ضرب در سطح توزیع تقسیم می شوند.","发放时间":"زمان پرداخت","{number} 人":"{number} نفر","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"در صورت انتشار آدرس یا حساب اشتراک شما و سوء استفاده از آن توسط دیگران، می‌توانید اطلاعات اشتراک خود را در اینجا بازنشانی کنید تا از زیان‌های غیرضروری جلوگیری شود.","再次输入密码":"ورود مجدد رمز عبور","返回登陆":"بازگشت به ورود","选填":"اختیاری","必填":"الزامی","最后回复时间":"زمان آخرین پاسخ","请选项工单等级":"لطفاً اولویت تیکت را انتخاب کنید","回复":"پاسخ","输入内容回复工单":"محتوا را برای پاسخ به تیکت وارد کنید","已生成":"تولید شده","选择协议":"انتخاب پروتکل","自动":"خودکار","流量重置包":"بسته بازنشانی داده","复制失败":"کپی ناموفق بود","提示":"اطلاع","确认退出?":"تأیید خروج?","已退出登录":"با موفقیت خارج شده","请输入邮箱地址":"آدرس ایمیل را وارد کنید","{second}秒后可重新发送":"{second} ثانیه دیگر می‌توانید مجدداً ارسال کنید","发送成功":"با موفقیت ارسال شد","请输入账号密码":"نام کاربری و رمز عبور را وارد کنید","请确保两次密码输入一致":"اطمینان حاصل کنید که ورودهای رمز عبور مطابقت دارند","注册成功":"ثبت نام با موفقیت انجام شد","重置密码成功,正在返回登录":"با موفقیت رمز عبور بازنشانی شد، در حال بازگشت به صفحه ورود","确认取消":"تایید لغو","请注意,变更订阅会导致当前订阅被覆盖。":"لطفاً توجه داشته باشید که تغییر اشتراک موجب ایجاد اشتراک فعلی می‌شود.","订单提交成功,正在跳转支付":"سفارش با موفقیت ثبت شد، به پرداخت هدایت می‌شود.","回复成功":"پاسخ با موفقیت ارسال شد","工单详情":"جزئیات تیکت","登录成功":"ورود موفقیت‌آمیز","确定退出?":"آیا مطمئن هستید که می‌خواهید خارج شوید؟","支付成功":"پرداخت موفق","正在前往收银台":"در حال رفتن به صندوق پرداخت","请输入正确的划转金额":"لطفا مبلغ انتقال صحیح را وارد کنید","划转成功":"انتقال موفق","提现方式不能为空":"روش برداشت نمی‌تواند خالی باشد","提现账号不能为空":"حساب برداشت نمی‌تواند خالی باشد","已绑定":"قبلاً متصل شده","创建成功":"ایجاد موفقیت‌آمیز","关闭成功":"خاموش کردن موفق","或使用第三方登录":"یا با ورود از طریق شخص ثالث","正在加载 Telegram 登录...":"در حال بارگذاری ورود تلگرام...","Telegram 登录失败":"ورود تلگرام ناموفق بود","下次流量重置时间":"زمان بازنشانی بعدی ترافیک:"}},Symbol.toStringTag,{value:"Module"})),BQ=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"リクエストエラー","月付":"月間プラン","季付":"3か月プラン","半年付":"半年プラン","年付":"年間プラン","两年付":"2年プラン","三年付":"3年プラン","一次性":"一括払い","重置流量包":"使用済みデータをリセット","待支付":"お支払い待ち","开通中":"開通中","已取消":"キャンセル済み","已完成":"済み","已折抵":"控除済み","待确认":"承認待ち","发放中":"処理中","已发放":"処理済み","无效":"無効","个人中心":"会員メニュー","登出":"ログアウト","搜索":"検索","仪表盘":"ダッシュボード","订阅":"サブスクリプションプラン","我的订阅":"マイプラン","购买订阅":"プランの購入","财务":"ファイナンス","我的订单":"注文履歴","我的邀请":"招待リスト","用户":"ユーザー","我的工单":"お問い合わせ","流量明细":"データ通信明細","使用文档":"ナレッジベース","绑定Telegram获取更多服务":"Telegramと連携し各種便利な通知を受け取ろう","点击这里进行绑定":"こちらをクリックして連携開始","公告":"お知らせ","总览":"概要","该订阅长期有效":"時間制限なし","已过期":"期限切れ","已用 {used} / 总计 {total}":"使用済み {used} / 合計 {total}","查看订阅":"プランを表示","邮箱":"E-mail アドレス","邮箱验证码":"確認コード","发送":"送信","重置密码":"パスワードを変更","返回登入":"ログインページへ戻る","邀请码":"招待コード","复制链接":"URLをコピー","完成时间":"完了日時","佣金":"コミッション金額","已注册用户数":"登録済みユーザー数","佣金比例":"コミッションレート","确认中的佣金":"承認待ちのコミッション","佣金将会在确认后会到达你的佣金账户。":"コミッションは承認処理完了後にカウントされます","邀请码管理":"招待コードの管理","生成邀请码":"招待コードを生成","佣金发放记录":"コミッション履歴","复制成功":"クリップボードにコピーされました","密码":"パスワード","登入":"ログイン","注册":"新規登録","忘记密码":"パスワードをお忘れの方","# 订单号":"受注番号","周期":"サイクル","订单金额":"ご注文金額","订单状态":"ご注文状況","创建时间":"作成日時","操作":"アクション","查看详情":"詳細を表示","请选择支付方式":"支払い方法をお選びください","请检查信用卡支付信息":"クレジットカード決済情報をご確認ください","订单详情":"ご注文詳細","折扣":"割引","折抵":"控除","退款":"払い戻し","支付方式":"お支払い方法","填写信用卡支付信息":"クレジットカード決済情報をご入力ください。","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"お客様のカード情報は今回限りリクエストされ、記録に残ることはございません","订单总额":"ご注文の合計金額","总计":"合計金額","结账":"チェックアウト","等待支付中":"お支払い待ち","订单系统正在进行处理,请稍等1-3分钟。":"システム処理中です、しばらくお待ちください","订单由于超时支付已被取消。":"ご注文はキャンセルされました","订单已支付并开通。":"お支払いが完了しました、プランはご利用可能です","选择订阅":"プランをお選びください","立即订阅":"今すぐ購入","配置订阅":"プランの内訳","付款周期":"お支払いサイクル","有优惠券?":"キャンペーンコード","验证":"確定","下单":"チェックアウト","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"プランを変更なされます場合は、既存のプランが新規プランによって上書きされます、ご注意下さい","该订阅无法续费":"該当プランは継続利用できません","选择其他订阅":"その他のプランを選択","我的钱包":"マイウォレット","账户余额(仅消费)":"残高(サービスの購入のみ)","推广佣金(可提现)":"招待によるコミッション(出金可)","钱包组成部分":"ウォレットの内訳","划转":"お振替","推广佣金提现":"コミッションのお引き出し","修改密码":"パスワードの変更","保存":"変更を保存","旧密码":"現在のパスワード","新密码":"新しいパスワード","请输入旧密码":"現在のパスワードをご入力ください","请输入新密码":"新しいパスワードをご入力ください","通知":"お知らせ","到期邮件提醒":"期限切れ前にメールで通知","流量邮件提醒":"データ量不足時にメールで通知","绑定Telegram":"Telegramと連携","立即开始":"今すぐ連携開始","重置订阅信息":"サブスクリプションURLの変更","重置":"変更","确定要重置订阅信息?":"サブスクリプションURLをご変更なされますか?","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"サブスクリプションのURL及び情報が外部に漏れた場合にご操作ください。操作後はUUIDやURLが変更され、再度サブスクリプションのインポートが必要になります。","重置成功":"変更完了","两次新密码输入不同":"ご入力されました新しいパスワードが一致しません","两次密码输入不同":"ご入力されましたパスワードが一致しません","邀请码(选填)":"招待コード (オプション)",'我已阅读并同意 服务条款':"ご利用規約に同意します","请同意服务条款":"ご利用規約に同意してください","名称":"名称","标签":"ラベル","状态":"ステータス","节点五分钟内节点在线情况":"5分間のオンラインステータス","倍率":"適応レート","使用的流量将乘以倍率进行扣除":"通信量は該当レートに基き計算されます","更多操作":"アクション","没有可用节点,如果您未订阅或已过期请":"ご利用可能なサーバーがありません,プランの期限切れまたは購入なされていない場合は","确定重置当前已用流量?":"利用済みデータ量をリセットしますか?","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":"「確定」をクリックし次のページへ移動,お支払い後に当月分のデータ通信量は即時リセットされます","确定":"確定","低":"低","中":"中","高":"高","主题":"タイトル","工单级别":"プライオリティ","工单状态":"進捗状況","最后回复":"最終回答日時","已关闭":"終了","待回复":"対応待ち","已回复":"回答済み","查看":"閲覧","关闭":"終了","新的工单":"新規お問い合わせ","确认":"確定","请输入工单主题":"お問い合わせタイトルをご入力ください","工单等级":"ご希望のプライオリティ","请选择工单等级":"ご希望のプライオリティをお選びください","消息":"メッセージ","请描述你遇到的问题":"お問い合わせ内容をご入力ください","记录时间":"記録日時","实际上行":"アップロード","实际下行":"ダウンロード","合计":"合計","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"計算式:(アップロード + ダウンロード) x 適応レート = 使用済みデータ通信量","复制订阅地址":"サブスクリプションのURLをコピー","导入到":"インポート先:","一键订阅":"ワンクリックインポート","复制订阅":"サブスクリプションのURLをコピー","推广佣金划转至余额":"コミッションを残高へ振替","划转后的余额仅用于{title}消费使用":"振替済みの残高は{title}でのみご利用可能です","当前推广佣金余额":"現在のコミッション金額","划转金额":"振替金額","请输入需要划转到余额的金额":"振替金額をご入力ください","输入内容回复工单...":"お問い合わせ内容をご入力ください...","申请提现":"出金申請","取消":"キャンセル","提现方式":"お振込み先","请选择提现方式":"お振込み先をお選びください","提现账号":"お振り込み先口座","请输入提现账号":"お振込み先口座をご入力ください","我知道了":"了解","第一步":"ステップその1","第二步":"ステップその2","打开Telegram搜索":"Telegramを起動後に右記内容を入力し検索","向机器人发送你的":"テレグラムボットへ下記内容を送信","最后更新: {date}":"最終更新日: {date}","还有没支付的订单":"未払いのご注文があります","立即支付":"チェックアウト","条工单正在处理中":"件のお問い合わせ","立即查看":"閲覧","节点状态":"サーバーステータス","商品信息":"プラン詳細","产品名称":"プラン名","类型/周期":"サイクル","产品流量":"ご利用可能データ量","订单信息":"オーダー情報","关闭订单":"注文をキャンセル","订单号":"受注番号","优惠金额":"'割引額","旧订阅折抵金额":"既存プラン控除額","退款金额":"返金額","余额支付":"残高ご利用分","工单历史":"お問い合わせ履歴","已用流量将在 {reset_day} 日后重置":"利用済みデータ量は {reset_day} 日後にリセットします","已用流量已在今日重置":"利用済みデータ量は本日リセットされました","重置已用流量":"利用済みデータ量をリセット","查看节点状态":"接続先サーバのステータス","当前已使用流量达{rate}%":"データ使用量が{rate}%になりました","节点名称":"サーバー名","于 {date} 到期,距离到期还有 {day} 天。":"ご利用期限は {date} まで,期限まであと {day} 日","Telegram 讨论组":"Telegramグループ","立即加入":"今すぐ参加","该订阅无法续费,仅允许新用户购买":"該当プランは継続利用できません、新規ユーザーのみが購入可能です","重置当月流量":"使用済みデータ量のカウントリセット","流量明细仅保留近月数据以供查询。":"データ通信明細は当月分のみ表示されます","扣费倍率":"適応レート","支付手续费":"お支払い手数料","续费订阅":"購読更新","学习如何使用":"ご利用ガイド","快速将节点导入对应客户端进行使用":"最短ルートでサーバー情報をアプリにインポートして使用する","对您当前的订阅进行续费":"ご利用中のサブスクの継続料金を支払う","对您当前的订阅进行购买":"ご利用中のサブスクを再度購入する","捷径":"ショートカット","不会使用,查看使用教程":"ご利用方法がわからない方はナレッジベースをご閲覧ください","使用支持扫码的客户端进行订阅":"使用支持扫码的客户端进行订阅","扫描二维码订阅":"QRコードをスキャンしてサブスクを設定","续费":"更新","购买":"購入","查看教程":"チュートリアルを表示","注意":"注意","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"まだ購入が完了していないオーダーがあります。購入前にそちらをキャンセルする必要がありますが、キャンセルしてよろしいですか?","确定取消":"キャンセル","返回我的订单":"注文履歴に戻る","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"もし既にお支払いが完了していると、注文をキャンセルすると支払いが失敗となる可能性があります。キャンセルしてもよろしいですか?","选择最适合你的计划":"あなたにピッタリのプランをお選びください","全部":"全て","按周期":"期間順","遇到问题":"何かお困りですか?","遇到问题可以通过工单与我们沟通":"何かお困りでしたら、お問い合わせからご連絡ください。","按流量":"データ通信量順","搜索文档":"ドキュメント内を検索","技术支持":"テクニカルサポート","当前剩余佣金":"コミッション残高","三级分销比例":"3ティア比率","累计获得佣金":"累計獲得コミッション金額","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"お客様に招待された方が更に別の方を招待された場合、お客様は支払われるオーダーからティア分配分の比率分を受け取ることができます。","发放时间":"手数料支払時間","{number} 人":"{number} 人","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"購読アドレスまたはアカウントが漏れて他者に悪用された場合、不必要な損失を防ぐためにここで購読情報をリセットできます。","再次输入密码":"パスワードを再入力してください","返回登陆":"ログインに戻る","选填":"任意","必填":"必須","最后回复时间":"最終返信時刻","请选项工单等级":"チケットの優先度を選択してください","回复":"返信","输入内容回复工单":"チケットへの返信内容を入力","已生成":"生成済み","选择协议":"プロトコルの選択","自动":"自動","流量重置包":"データリセットパッケージ","复制失败":"コピーに失敗しました","提示":"通知","确认退出?":"ログアウトを確認?","已退出登录":"正常にログアウトしました","请输入邮箱地址":"メールアドレスを入力してください","{second}秒后可重新发送":"{second} 秒後に再送信可能","发送成功":"送信成功","请输入账号密码":"アカウントとパスワードを入力してください","请确保两次密码输入一致":"パスワードの入力が一致していることを確認してください","注册成功":"登録が成功しました","重置密码成功,正在返回登录":"パスワードのリセットが成功しました。ログインに戻っています","确认取消":"キャンセルの確認","请注意,变更订阅会导致当前订阅被覆盖。":"購読の変更は現在の購読を上書きします。","订单提交成功,正在跳转支付":"注文が成功裏に送信されました。支払いにリダイレクトしています。","回复成功":"返信が成功しました","工单详情":"チケットの詳細","登录成功":"ログイン成功","确定退出?":"本当に退出しますか?","支付成功":"支払い成功","正在前往收银台":"チェックアウトに進行中","请输入正确的划转金额":"正しい振替金額を入力してください","划转成功":"振替成功","提现方式不能为空":"出金方法は空にできません","提现账号不能为空":"出金口座を空にすることはできません","已绑定":"既にバインドされています","创建成功":"作成成功","关闭成功":"閉鎖成功","或使用第三方登录":"または第三者ログインを使用","正在加载 Telegram 登录...":"Telegram ログインを読み込み中...","Telegram 登录失败":"Telegram ログインに失敗しました","下次流量重置时间":"次回のリセット時刻:","已用流量将在 {time} 重置":"ご利用済みデータ量は {time} にリセットされます"}},Symbol.toStringTag,{value:"Module"})),DQ=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"요청실패","月付":"월간","季付":"3개월간","半年付":"반년간","年付":"1년간","两年付":"2년마다","三年付":"3년마다","一次性":"한 번","重置流量包":"데이터 재설정 패키지","待支付":"지불 보류중","开通中":"보류 활성화","已取消":"취소 됨","已完成":"완료","已折抵":"변환","待确认":"보류중","发放中":"확인중","已发放":"완료","无效":"유효하지 않음","个人中心":"사용자 센터","登出":"로그아웃","搜索":"검색","仪表盘":"대시보드","订阅":"구독","我的订阅":"나의 구독","购买订阅":"구독 구매 내역","财务":"청구","我的订单":"나의 주문","我的邀请":"나의 초청","用户":"사용자 센터","我的工单":"나의 티켓","流量明细":"데이터 세부 정보 전송","使用文档":"사용 설명서","绑定Telegram获取更多服务":"텔레그램에 아직 연결되지 않았습니다","点击这里进行绑定":"텔레그램에 연결되도록 여기를 눌러주세요","公告":"발표","总览":"개요","该订阅长期有效":"구독은 무제한으로 유효합니다","已过期":"만료","已用 {used} / 总计 {total}":"{date}에 만료됩니다, 만료 {day}이 전, {reset_day}후 데이터 전송 재설정","查看订阅":"구독 보기","邮箱":"이메일","邮箱验证码":"이메일 확인 코드","发送":"보내기","重置密码":"비밀번호 재설정","返回登入":"로그인 다시하기","邀请码":"초청 코드","复制链接":"링크 복사","完成时间":"완료 시간","佣金":"수수료","已注册用户数":"등록 된 사용자들","佣金比例":"수수료율","确认中的佣金":"수수료 상태","佣金将会在确认后会到达你的佣金账户。":"수수료는 검토 후 수수료 계정에서 확인할 수 있습니다","邀请码管理":"초청 코드 관리","生成邀请码":"초청 코드 생성하기","佣金发放记录":"수수료 지불 기록","复制成功":"복사 성공","密码":"비밀번호","登入":"로그인","注册":"등록하기","忘记密码":"비밀번호를 잊으셨나요","# 订单号":"주문 번호 #","周期":"유형/기간","订单金额":"주문량","订单状态":"주문 상태","创建时间":"생성 시간","操作":"설정","查看详情":"세부사항 보기","请选择支付方式":"지불 방식을 선택 해주세요","请检查信用卡支付信息":"신용카드 지불 정보를 확인 해주세요","订单详情":"주문 세부사항","折扣":"할인","折抵":"변환","退款":"환불","支付方式":"지불 방식","填写信用卡支付信息":"신용카드 지불 정보를 적으세요","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"현재 거래를 확인하는 데 사용하는 귀하의 신용 카드 정보, 신용 카드 번호 및 기타 세부 정보를 수집하지 않습니다.","订单总额":"전체주문","总计":"전체","结账":"결제하기","等待支付中":"결제 대기 중","订单系统正在进行处理,请稍等1-3分钟。":"주문 시스템이 처리 중입니다. 1-3분 정도 기다려 주십시오.","订单由于超时支付已被取消。":"결제 시간 초과로 인해 주문이 취소되었습니다.","订单已支付并开通。":"주문이 결제되고 개통되었습니다.","选择订阅":"구독 선택하기","立即订阅":"지금 구독하기","配置订阅":"구독 환경 설정하기","付款周期":"지불 기간","有优惠券?":"쿠폰을 가지고 있나요?","验证":"확인","下单":"주문","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"주의하십시오. 구독을 변경하면 현재 구독을 덮어씁니다","该订阅无法续费":"이 구독은 갱신할 수 없습니다.","选择其他订阅":"다른 구독 선택","我的钱包":"나의 지갑","账户余额(仅消费)":"계정 잔액(결제 전용)","推广佣金(可提现)":"초청수수료(인출하는 데 사용할 수 있습니다)","钱包组成部分":"지갑 세부사항","划转":"이체하기","推广佣金提现":"초청 수수료 인출","修改密码":"비밀번호 변경","保存":"저장하기","旧密码":"이전 비밀번호","新密码":"새로운 비밀번호","请输入旧密码":"이전 비밀번호를 입력해주세요","请输入新密码":"새로운 비밀번호를 입력해주세요","通知":"공고","到期邮件提醒":"구독 만료 이메일 알림","流量邮件提醒":"불충분한 데이터 이메일 전송 알림","绑定Telegram":"탤레그램으로 연결","立即开始":"지금 시작하기","重置订阅信息":"구독 재설정하기","重置":"재설정","确定要重置订阅信息?":"구독을 재설정하시겠습니까?","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"계정 정보나 구독이 누출된 경우 이 옵션은 UUID를 재설정하는 데 사용되며 재설정 후에 구독이 변경되므로 다시 구독해야 합니다.","重置成功":"재설정 성공","两次新密码输入不同":"입력한 두 개의 새 비밀번호가 일치하지 않습니다.","两次密码输入不同":"입력한 비밀번호가 일치하지 않습니다.","邀请码(选填)":"초청 코드(선택 사항)",'我已阅读并同意 服务条款':"을 읽었으며 이에 동의합니다 서비스 약관","请同意服务条款":"서비스 약관에 동의해주세요","名称":"이름","标签":"태그","状态":"설정","节点五分钟内节点在线情况":"지난 5분 동안의 액세스 포인트 온라인 상태","倍率":"요금","使用的流量将乘以倍率进行扣除":"사용된 전송 데이터에 전송 데이터 요금을 뺀 값을 곱합니다.","更多操作":"설정","没有可用节点,如果您未订阅或已过期请":"사용 가능한 액세스 포인트가 없습니다. 구독을 신청하지 않았거나 구독이 만료된 경우","确定重置当前已用流量?":"현재 사용 중인 데이터를 재설정 하시겠습니까?","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":'확인"을 클릭하면 결제 페이지로 이동됩니다. 주문이 완료되면 시스템에서 해당 월의 사용 데이터를 삭제합니다.',"确定":"확인","低":"낮음","中":"중간","高":"높음","主题":"주제","工单级别":"티켓 우선 순위","工单状态":"티켓 상태","最后回复":"생성 시간","已关闭":"마지막 답장","待回复":"설정","已回复":"닫힘","查看":"보기","关闭":"닫기","新的工单":"새로운 티켓","确认":"확인","请输入工单主题":"제목을 입력하세요","工单等级":"티켓 우선순위","请选择工单等级":"티켓 우선순위를 선택해주세요","消息":"메세지","请描述你遇到的问题":"문제를 설명하십시오 발생한","记录时间":"기록 시간","实际上行":"실제 업로드","实际下行":"실제 다운로드","合计":"전체","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"공식: (실제 업로드 + 실제 다운로드) x 공제율 = 전송 데이터 공제","复制订阅地址":"구독 URL 복사","导入到":"내보내기","一键订阅":"빠른 구독","复制订阅":"구독 URL 복사","推广佣金划转至余额":"초청 수수료를 계좌 잔액으로 이체","划转后的余额仅用于{title}消费使用":"이체된 잔액은 {title} 소비에만 사용됩니다.","当前推广佣金余额":"현재 홍보 수수료 잔액","请输入需要划转到余额的金额":"잔액으로 이체할 금액을 입력하세요.","取消":"취소","提现方式":"인출 방법","请选择提现方式":"인출 방법을 선택해주세요","提现账号":"인출 계좌","请输入提现账号":"인출 계좌를 입력해주세요","我知道了":"알겠습니다.","第一步":"첫번째 단계","第二步":"두번째 단계","打开Telegram搜索":"텔레그램 열기 및 탐색","向机器人发送你的":"봇에 다음 명령을 보냅니다","最后更新: {date}":"마지막 업데이트{date}","还有没支付的订单":"미결제 주문이 있습니다","立即支付":"즉시 지불","条工单正在处理中":"티켓이 처리 중입니다","立即查看":"제목을 입력하세요","节点状态":"노드 상태","商品信息":"제품 정보","产品名称":"제품 명칭","类型/周期":"종류/기간","产品流量":"제품 데이터 용량","订单信息":"주문 정보","关闭订单":"주문 취소","订单号":"주문 번호","优惠金额":"할인 가격","旧订阅折抵金额":"기존 패키지 공제 금액","退款金额":"환불 금액","余额支付":"잔액 지불","工单历史":"티켓 기록","已用流量将在 {reset_day} 日后重置":"{reset_day}일 후에 사용한 데이터가 재설정됩니다","已用流量已在今日重置":"오늘 이미 사용한 데이터가 재설정되었습니다","重置已用流量":"사용한 데이터 재설정","查看节点状态":"노드 상태 확인","当前已使用流量达{rate}%":"현재 사용한 데이터 비율이 {rate}%에 도달했습니다","节点名称":"환불 금액","于 {date} 到期,距离到期还有 {day} 天。":"{day}까지, 만료 {day}일 전.","Telegram 讨论组":"텔레그램으로 문의하세요","立即加入":"지금 가입하세요","该订阅无法续费,仅允许新用户购买":"이 구독은 갱신할 수 없습니다. 신규 사용자만 구매할 수 있습니다.","重置当月流量":"이번 달 트래픽 초기화","流量明细仅保留近月数据以供查询。":"귀하의 트래픽 세부 정보는 최근 몇 달 동안만 유지됩니다","扣费倍率":"수수료 공제율","支付手续费":"수수료 지불","续费订阅":"구독 갱신","学习如何使用":"사용 방법 배우기","快速将节点导入对应客户端进行使用":"빠르게 노드를 해당 클라이언트로 가져와 사용하기","对您当前的订阅进行续费":"현재 구독 갱신","对您当前的订阅进行购买":"현재 구독 구매","捷径":"단축키","不会使用,查看使用教程":"사용 방법을 모르겠다면 사용 설명서 확인","使用支持扫码的客户端进行订阅":"스캔 가능한 클라이언트로 구독하기","扫描二维码订阅":"QR 코드 스캔하여 구독","续费":"갱신","购买":"구매","查看教程":"사용 설명서 보기","注意":"주의","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"미완료된 주문이 있습니다. 구매 전에 취소해야 합니다. 이전 주문을 취소하시겠습니까?","确定取消":"취소 확인","返回我的订单":"내 주문으로 돌아가기","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"이미 결제를 했을 경우 주문 취소는 결제 실패로 이어질 수 있습니다. 주문을 취소하시겠습니까?","选择最适合你的计划":"가장 적합한 요금제 선택","全部":"전체","按周期":"주기별","遇到问题":"문제 발생","遇到问题可以通过工单与我们沟通":"문제가 발생하면 서포트 티켓을 통해 문의하세요","按流量":"트래픽별","搜索文档":"문서 검색","技术支持":"기술 지원","当前剩余佣金":"현재 잔여 수수료","三级分销比例":"삼수준 분배 비율","累计获得佣金":"누적 수수료 획득","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"초대한 사용자가 다시 초대하면 주문 금액에 분배 비율을 곱하여 분배됩니다.","发放时间":"수수료 지급 시간","{number} 人":"{number} 명","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"구독 주소 또는 계정이 유출되어 다른 사람에게 남용되는 경우 여기에서 구독 정보를 재설정하여 불필요한 손실을 방지할 수 있습니다.","再次输入密码":"비밀번호를 다시 입력하세요","返回登陆":"로그인으로 돌아가기","选填":"선택 사항","必填":"필수","最后回复时间":"최근 답장 시간","请选项工单等级":"티켓 우선 순위 선택","回复":"답장","输入内容回复工单":"티켓에 대한 내용 입력","已生成":"생성됨","选择协议":"프로토콜 선택","自动":"자동","流量重置包":"데이터 리셋 패키지","复制失败":"복사 실패","提示":"알림","确认退出?":"로그아웃 확인?","已退出登录":"로그아웃 완료","请输入邮箱地址":"이메일 주소를 입력하세요","{second}秒后可重新发送":"{second} 초 후에 다시 전송 가능","发送成功":"전송 성공","请输入账号密码":"계정과 비밀번호를 입력하세요","请确保两次密码输入一致":"비밀번호 입력이 일치하는지 확인하세요","注册成功":"등록 성공","重置密码成功,正在返回登录":"비밀번호 재설정 성공, 로그인 페이지로 돌아가는 중","确认取消":"취소 확인","请注意,变更订阅会导致当前订阅被覆盖。":"구독 변경은 현재 구독을 덮어씁니다.","订单提交成功,正在跳转支付":"주문이 성공적으로 제출되었습니다. 지불로 이동 중입니다.","回复成功":"답장 성공","工单详情":"티켓 상세 정보","登录成功":"로그인 성공","确定退出?":"확실히 종료하시겠습니까?","支付成功":"결제 성공","正在前往收银台":"결제 진행 중","请输入正确的划转金额":"정확한 이체 금액을 입력하세요","划转成功":"이체 성공","提现方式不能为空":"출금 방식은 비워 둘 수 없습니다","提现账号不能为空":"출금 계좌는 비워 둘 수 없습니다","已绑定":"이미 연결됨","创建成功":"생성 성공","关闭成功":"종료 성공","或使用第三方登录":"또는 제3자 로그인 사용","正在加载 Telegram 登录...":"Telegram 로그인 로딩 중...","Telegram 登录失败":"Telegram 로그인 실패","下次流量重置时间":"다음 트래픽 리셋 시간: ","已用流量将在 {time} 重置":"사용한 데이터가 {time}에 재설정됩니다"}},Symbol.toStringTag,{value:"Module"})),$Q=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"Запрос не удался","月付":"Ежемесячно","季付":"Ежеквартально","半年付":"Каждые полгода","年付":"Ежегодно","两年付":"Раз в два года","三年付":"Раз в три года","一次性":"Единоразово","重置流量包":"Пакет сброса трафика","待支付":"Ожидает оплаты","开通中":"Ожидает активации","已取消":"Отменено","已完成":"Завершено","已折抵":"Конвертировано","待确认":"Ожидает подтверждения","发放中":"Подтверждается","已发放":"Завершено","无效":"Недействительно","个人中心":"Личный кабинет","登出":"Выйти","搜索":"Поиск","仪表盘":"Панель управления","订阅":"Подписка","我的订阅":"Моя подписка","购买订阅":"Купить подписку","财务":"Финансы","我的订单":"Мои заказы","我的邀请":"Мои приглашения","用户":"Аккаунт","我的工单":"Мои тикеты","流量明细":"Детали трафика","使用文档":"База знаний","绑定Telegram获取更多服务":"Привяжите Telegram для получения дополнительных услуг","点击这里进行绑定":"Нажмите здесь для привязки","公告":"Объявления","总览":"Обзор","该订阅长期有效":"Подписка действует бессрочно","已过期":"Истекло","已用 {used} / 总计 {total}":"Использовано {used} / Всего {total}","查看订阅":"Просмотр подписки","邮箱":"Email","邮箱验证码":"Код подтверждения email","发送":"Отправить","重置密码":"Сброс пароля","返回登入":"Вернуться к входу","邀请码":"Код приглашения","复制链接":"Копировать ссылку","完成时间":"Время завершения","佣金":"Комиссия","已注册用户数":"Зарегистрированные пользователи","佣金比例":"Ставка комиссии","确认中的佣金":"Ожидаемая комиссия","佣金将会在确认后会到达你的佣金账户。":"Комиссия поступит на ваш счет после проверки.","邀请码管理":"Управление кодами приглашений","生成邀请码":"Создать код приглашения","佣金发放记录":"История начисления комиссий","复制成功":"Успешно скопировано","密码":"Пароль","登入":"Войти","注册":"Регистрация","忘记密码":"Забыли пароль","# 订单号":"Номер заказа #","周期":"Тип / Период","订单金额":"Сумма заказа","订单状态":"Статус заказа","创建时间":"Время создания","操作":"Действие","查看详情":"Подробнее","请选择支付方式":"Выберите способ оплаты","请检查信用卡支付信息":"Проверьте данные кредитной карты","订单详情":"Детали заказа","折扣":"Скидка","折抵":"Конвертировано","退款":"Возврат","支付方式":"Способ оплаты","填写信用卡支付信息":"Введите данные кредитной карты","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"Мы не сохраняем данные вашей кредитной карты, они используются только для текущей транзакции.","订单总额":"Итого к оплате","总计":"Итого","结账":"Оформить заказ","等待支付中":"Ожидание оплаты","订单系统正在进行处理,请稍等1-3分钟。":"Заказ обрабатывается, подождите 1-3 минуты.","订单由于超时支付已被取消。":"Заказ отменен из-за истечения времени оплаты.","订单已支付并开通。":"Заказ оплачен и услуга активирована.","选择订阅":"Выберите подписку","立即订阅":"Подписаться сейчас","配置订阅":"Настроить подписку","付款周期":"Период оплаты","有优惠券?":"Есть купон?","验证":"Проверить","下单":"Заказать","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"Внимание, изменение подписки заменит вашу текущую подписку.","该订阅无法续费":"Эту подписку нельзя продлить","选择其他订阅":"Выбрать другую подписку","我的钱包":"Мой кошелек","账户余额(仅消费)":"Баланс аккаунта (только для оплаты)","推广佣金(可提现)":"Реферальная комиссия (можно вывести)","钱包组成部分":"Детали кошелька","划转":"Перевод","推广佣金提现":"Вывод реферальной комиссии","修改密码":"Изменить пароль","保存":"Сохранить","旧密码":"Старый пароль","新密码":"Новый пароль","请输入旧密码":"Введите старый пароль","请输入新密码":"Введите новый пароль","通知":"Уведомления","到期邮件提醒":"Напоминание об истечении подписки по email","流量邮件提醒":"Уведомление о недостатке трафика по email","绑定Telegram":"Привязать Telegram","立即开始":"Начать сейчас","重置订阅信息":"Сбросить подписку","重置":"Сбросить","确定要重置订阅信息?":"Вы хотите сбросить подписку?","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"В случае утечки данных аккаунта или подписки используйте эту опцию. После сброса ваш UUID и подписка изменятся, потребуется повторная подписка.","重置成功":"Успешно сброшено","两次新密码输入不同":"Новые пароли не совпадают","两次密码输入不同":"Пароли не совпадают","邀请码(选填)":"Код приглашения (необязательно)",'我已阅读并同意 服务条款':'Я прочитал и согласен с условиями использования',"请同意服务条款":"Пожалуйста, примите условия использования","名称":"Название","标签":"Теги","状态":"Статус","节点五分钟内节点在线情况":"Статус узлов за последние 5 минут","倍率":"Коэффициент","使用的流量将乘以倍率进行扣除":"Использованный трафик будет умножен на коэффициент списания.","更多操作":"Действия","没有可用节点,如果您未订阅或已过期请":"Нет доступных узлов. Если у вас нет подписки или она истекла, пожалуйста","确定重置当前已用流量?":"Вы уверены, что хотите сбросить текущий использованный трафик?","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":'Нажмите "Подтвердить" для перехода к оплате. После оплаты система очистит ваш трафик за текущий месяц.',"确定":"Подтвердить","低":"Низкий","中":"Средний","高":"Высокий","主题":"Тема","工单级别":"Приоритет тикета","工单状态":"Статус тикета","最后回复":"Последний ответ","已关闭":"Закрыт","待回复":"Ожидает ответа","已回复":"Отвечен","查看":"Просмотр","关闭":"Отмена","新的工单":"Мои тикеты","确认":"Подтвердить","请输入工单主题":"Введите тему тикета","工单等级":"Приоритет тикета","请选择工单等级":"Выберите приоритет тикета","消息":"Сообщение","请描述你遇到的问题":"Опишите вашу проблему","记录时间":"Время записи","实际上行":"Фактическая загрузка","实际下行":"Фактическая загрузка","合计":"Итого","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"Формула: (Загрузка + Скачивание) x Коэффициент = Списание трафика","复制订阅地址":"Копировать URL подписки","导入到":"Экспорт в","一键订阅":"Быстрая подписка","复制订阅":"Копировать URL подписки","推广佣金划转至余额":"Перевести реферальную комиссию на баланс","划转后的余额仅用于{title}消费使用":"Переведенный баланс будет использоваться только для оплаты {title}","当前推广佣金余额":"Текущий баланс реферальной комиссии","划转金额":"Сумма перевода","请输入需要划转到余额的金额":"Введите сумму для перевода на баланс","输入内容回复工单...":"Введите сообщение для ответа на тикет...","申请提现":"Запросить вывод","取消":"Отмена","提现方式":"Способ вывода","请选择提现方式":"Выберите способ вывода","提现账号":"Аккаунт для вывода","请输入提现账号":"Введите аккаунт для вывода","我知道了":"Понятно","第一步":"Первый шаг","第二步":"Второй шаг","打开Telegram搜索":"Откройте Telegram и найдите","向机器人发送你的":"Отправьте следующую команду боту","最后更新: {date}":"Последнее обновление: {date}","还有没支付的订单":"Есть неоплаченные заказы","立即支付":"Оплатить сейчас","条工单正在处理中":"тикетов в обработке","立即查看":"Посмотреть","节点状态":"Статус узлов","商品信息":"Информация о продукте","产品名称":"Название продукта","类型/周期":"Тип / Период","产品流量":"Трафик продукта","订单信息":"Детали заказа","关闭订单":"Закрыть заказ","订单号":"Номер заказа","优惠金额":"Сумма скидки","旧订阅折抵金额":"Сумма конвертации старой подписки","退款金额":"Сумма возврата","余额支付":"Оплата с баланса","工单历史":"История тикетов","已用流量将在 {time} 重置":"Использованный трафик будет сброшен в {time}","已用流量已在今日重置":"Использованный трафик был сброшен сегодня","重置已用流量":"Сбросить использованный трафик","查看节点状态":"Посмотреть статус узлов","当前已使用流量达{rate}%":"Текущий использованный трафик достиг {rate}%","节点名称":"Название узла","于 {date} 到期,距离到期还有 {day} 天。":"Истекает {date}, осталось {day} дней.","Telegram 讨论组":"Группа обсуждения Telegram","立即加入":"Присоединиться","该订阅无法续费,仅允许新用户购买":"Эту подписку нельзя продлить, она доступна только для новых пользователей.","重置当月流量":"Сбросить трафик за текущий месяц","流量明细仅保留近月数据以供查询。":"Детали трафика хранятся только за последний месяц.","扣费倍率":"Коэффициент списания","支付手续费":"Комиссия за оплату","续费订阅":"Продление подписки","学习如何使用":"Узнайте как использовать","快速将节点导入对应客户端进行使用":"Быстро экспортируйте подписку в клиентское приложение","对您当前的订阅进行续费":"Продлите вашу текущую подписку","对您当前的订阅进行购买":"Купите вашу текущую подписку","捷径":"Ярлыки","不会使用,查看使用教程":"Я новичок, посмотреть руководство","使用支持扫码的客户端进行订阅":"Используйте приложение с поддержкой сканирования QR-кода для подписки","扫描二维码订阅":"Сканируйте QR-код для подписки","续费":"Продление","购买":"Купить","查看教程":"Посмотреть руководство","注意":"Внимание","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"У вас есть неоплаченный заказ. Перед покупкой его нужно отменить. Вы уверены, что хотите отменить предыдущий заказ?","确定取消":"Подтвердить отмену","返回我的订单":"Вернуться к моим заказам","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"Если вы уже оплатили, отмена заказа может привести к сбою оплаты. Вы уверены, что хотите отменить заказ?","选择最适合你的计划":"Выберите подходящий тариф","全部":"Все","按周期":"По периоду","遇到问题":"У меня проблема","遇到问题可以通过工单与我们沟通":"Если у вас есть вопросы, вы можете связаться с нами через тикет","按流量":"Оплата по трафику","搜索文档":"Поиск в документации","技术支持":"Техническая поддержка","当前剩余佣金":"Остаток комиссии","三级分销比例":"Трехуровневое распределение","累计获得佣金":"Накопленная комиссия","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"Пользователи, приглашенные вашими рефералами, будут распределяться согласно сумме заказа, умноженной на уровень распределения.","发放时间":"Время начисления","{number} 人":"{number} чел.","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"Если ваш адрес подписки или аккаунт был скомпрометирован, вы можете сбросить информацию о подписке здесь, чтобы избежать нежелательных потерь.","再次输入密码":"Введите пароль еще раз","返回登陆":"Вернуться к входу","选填":"Необязательно","必填":"Обязательно","最后回复时间":"Время последнего ответа","请选项工单等级":"Выберите приоритет тикета","回复":"Ответить","输入内容回复工单":"Введите сообщение для ответа на тикет","已生成":"Создано","选择协议":"Выберите протокол","自动":"Автоматически","流量重置包":"Пакет сброса трафика","复制失败":"Ошибка копирования","提示":"Уведомление","确认退出?":"Подтвердить выход?","已退出登录":"Вы успешно вышли","请输入邮箱地址":"Введите адрес email","{second}秒后可重新发送":"Повторная отправка через {second} сек.","发送成功":"Успешно отправлено","请输入账号密码":"Введите логин и пароль","请确保两次密码输入一致":"Убедитесь, что пароли совпадают","注册成功":"Регистрация успешна","重置密码成功,正在返回登录":"Пароль успешно сброшен, возврат к входу","确认取消":"Подтвердить отмену","请注意,变更订阅会导致当前订阅被覆盖。":"Обратите внимание, изменение подписки заменит текущую подписку.","订单提交成功,正在跳转支付":"Заказ успешно отправлен, перенаправление на оплату.","回复成功":"Ответ отправлен","工单详情":"Детали тикета","登录成功":"Вход выполнен успешно","确定退出?":"Вы уверены, что хотите выйти?","支付成功":"Оплата успешна","正在前往收银台":"Переход к оплате","请输入正确的划转金额":"Введите правильную сумму перевода","划转成功":"Перевод выполнен успешно","提现方式不能为空":"Способ вывода не может быть пустым","提现账号不能为空":"Аккаунт для вывода не может быть пустым","已绑定":"Уже привязано","创建成功":"Успешно создано","关闭成功":"Успешно закрыто","或使用第三方登录":"Или войдите через","正在加载 Telegram 登录...":"Загрузка входа через Telegram...","Telegram 登录失败":"Ошибка входа через Telegram","下次流量重置时间":"Время следующего сброса: "}},Symbol.toStringTag,{value:"Module"})),NQ=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"Yêu Cầu Thất Bại","月付":"Tháng","季付":"Hàng Quý","半年付":"6 Tháng","年付":"Năm","两年付":"Hai Năm","三年付":"Ba Năm","一次性":"Dài Hạn","重置流量包":"Cập Nhật Dung Lượng","待支付":"Đợi Thanh Toán","开通中":"Đang xử lý","已取消":"Đã Hủy","已完成":"Thực Hiện","已折抵":"Quy Đổi","待确认":"Đợi Xác Nhận","发放中":"Đang Xác Nhận","已发放":"Hoàn Thành","无效":"Không Hợp Lệ","个人中心":"Trung Tâm Kiểm Soát","登出":"Đăng Xuất","搜索":"Tìm Kiếm","仪表盘":"Trang Chủ","订阅":"Gói Dịch Vụ","我的订阅":"Gói Dịch Vụ Của Tôi","购买订阅":"Mua Gói Dịch Vụ","财务":"Tài Chính","我的订单":"Đơn Hàng Của Tôi","我的邀请":"Lời Mời Của Tôi","用户":"Người Dùng","我的工单":"Liên Hệ Với Chúng Tôi","流量明细":"Chi Tiết Dung Lượng","使用文档":"Tài liệu sử dụng","绑定Telegram获取更多服务":"Liên kết Telegram thêm dịch vụ","点击这里进行绑定":"Ấn vào để liên kết","公告":"Thông Báo","总览":"Tổng Quat","该订阅长期有效":"Gói này có thời hạn dài","已过期":"Tài khoản hết hạn","已用 {used} / 总计 {total}":"Đã sử dụng {used} / Tổng dung lượng {total}","查看订阅":"Xem Dịch Vụ","邮箱":"E-mail","邮箱验证码":"Mã xác minh mail","发送":"Gửi","重置密码":"Đặt Lại Mật Khẩu","返回登入":"Về đăng nhập","邀请码":"Mã mời","复制链接":"Sao chép đường dẫn","完成时间":"Thời gian hoàn thành","佣金":"Tiền hoa hồng","已注册用户数":"Số người dùng đã đăng ký","佣金比例":"Tỷ lệ hoa hồng","确认中的佣金":"Hoa hồng đang xác nhận","佣金将会在确认后会到达你的佣金账户。":"Sau khi xác nhận tiền hoa hồng sẽ gửi đến tài khoản hoa hồng của bạn.","邀请码管理":"Quản lý mã mời","生成邀请码":"Tạo mã mời","佣金发放记录":"Hồ sơ hoa hồng","复制成功":"Sao chép thành công","密码":"Mật khẩu","登入":"Đăng nhập","注册":"Đăng ký","忘记密码":"Quên mật khẩu","# 订单号":"# Mã đơn hàng","周期":"Chu Kỳ","订单金额":"Tiền đơn hàng","订单状态":"Trạng thái đơn","创建时间":"Thời gian tạo","操作":"Thao tác","查看详情":"Xem chi tiết","请选择支付方式":"Chọn phương thức thanh toán","请检查信用卡支付信息":"Hãy kiểm tra thông tin thẻ thanh toán","订单详情":"Chi tiết đơn hàng","折扣":"Chiết khấu","折抵":"Giảm giá","退款":"Hoàn lại","支付方式":"Phương thức thanh toán","填写信用卡支付信息":"Điền thông tin Thẻ Tín Dụng","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"Thông tin thẻ tín dụng của bạn sẽ chỉ được sử dụng cho lần thanh toán này, hệ thống sẽ không lưu thông tin đó, chúng tôi nghĩ đây à cách an toàn nhất.","订单总额":"Tổng tiền đơn hàng","总计":"Tổng","结账":"Kết toán","等待支付中":"Đang chờ thanh toán","订单系统正在进行处理,请稍等1-3分钟。":"Hệ thống đang xử lý đơn hàng, vui lòng đợi 1-3p.","订单由于超时支付已被取消。":"Do quá giờ nên đã hủy đơn hàng.","订单已支付并开通。":"Đơn hàng đã thanh toán và mở.","选择订阅":"Chọn gói","立即订阅":"Mua gói ngay","配置订阅":"Thiết lập gói","付款周期":"Chu kỳ thanh toán","有优惠券?":"Có phiếu giảm giá?","验证":"Xác minh","下单":"Đặt hàng","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"Việc thay đổi gói dịch vụ sẽ thay thế gói hiện tại bằng gói mới, xin lưu ý.","该订阅无法续费":"Gói này không thể gia hạn","选择其他订阅":"Chọn gói dịch vụ khác","我的钱包":"Ví tiền của tôi","账户余额(仅消费)":"Số dư tài khoản (Chỉ tiêu dùng)","推广佣金(可提现)":"Tiền hoa hồng giới thiệu (Được rút)","钱包组成部分":"Thành phần ví tiền","划转":"Chuyển khoản","推广佣金提现":"Rút tiền hoa hồng giới thiệu","修改密码":"Đổi mật khẩu","保存":"Lưu","旧密码":"Mật khẩu cũ","新密码":"Mật khẩu mới","请输入旧密码":"Hãy nhập mật khẩu cũ","请输入新密码":"Hãy nhập mật khẩu mới","通知":"Thông Báo","到期邮件提醒":"Mail nhắc đến hạn","流量邮件提醒":"Mail nhắc dung lượng","绑定Telegram":"Liên kết Telegram","立即开始":"Bắt Đầu","重置订阅信息":"Reset thông tin gói","重置":"Reset","确定要重置订阅信息?":"Xác nhận reset thông tin gói?","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"Nếu địa chỉ hoặc thông tin gói dịch vụ của bạn bị tiết lộ có thể tiến hành thao tác này. Sau khi reset UUID sẽ thay đổi.","重置成功":"Reset thành công","两次新密码输入不同":"Mật khẩu mới xác nhận không khớp","两次密码输入不同":"Mật khẩu xác nhận không khớp","邀请码(选填)":"Mã mời(Điền)",'我已阅读并同意 服务条款':"Tôi đã đọc và đồng ý điều khoản dịch vụ","请同意服务条款":"Hãy đồng ý điều khoản dịch vụ","名称":"Tên","标签":"Nhãn","状态":"Trạng thái","节点五分钟内节点在线情况":"Node trạng thái online trong vòng 5 phút","倍率":"Bội số","使用的流量将乘以倍率进行扣除":"Dung lượng sử dụng nhân với bội số rồi khấu trừ","更多操作":"Thêm thao tác","没有可用节点,如果您未订阅或已过期请":"Chưa có node khả dụng, nếu bạn chưa mua gói hoặc đã hết hạn hãy","确定重置当前已用流量?":"确定重置当前已用流量?","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":"Ấn 「OK」 sẽ chuyển đến trang thanh toán, sau khi thanh toán đơn hàng hệ thống sẽ xóa dung lượng đã dùng tháng này của bạn.","确定":"OK","低":"Thấp","中":"Vừa","高":"Cao","主题":"Chủ Đề","工单级别":"Cấp độ","工单状态":"Trạng thái","最后回复":"Trả lời gần đây","已关闭":"Đã đóng","待回复":"Chờ trả lời","已回复":"Đã trả lời","查看":"Xem","关闭":"Đóng","新的工单":"Việc mới","确认":"OK","请输入工单主题":"Hãy nhập chủ đề công việc","工单等级":"Cấp độ công việc","请选择工单等级":"Hãy chọn cấp độ công việc","消息":"Thông tin","请描述你遇到的问题":"Hãy mô tả vấn đề gặp phải","记录时间":"Thời gian ghi","实际上行":"Upload thực tế","实际下行":"Download thực tế","合计":"Cộng","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"Công thức: (upload thực tế + download thực tế) x bội số trừ phí = Dung lượng khấu trừ","复制订阅地址":"Sao chép liên kết","导入到":"Nhập vào","一键订阅":"Nhấp chuột để đồng bộ máy chủ","复制订阅":"Sao chép liên kết","推广佣金划转至余额":"Chuyển khoản hoa hồng giới thiệu đến số dư","划转后的余额仅用于{title}消费使用":"Số dư sau khi chuyển khoản chỉ dùng để tiêu dùng {title}","当前推广佣金余额":"Số dư hoa hồng giới thiệu hiện tại","划转金额":"Chuyển tiền","请输入需要划转到余额的金额":"Hãy nhậo số tiền muốn chuyển đến số dư","输入内容回复工单...":"Nhập nội dung trả lời công việc...","申请提现":"Yêu cầu rút tiền","取消":"Hủy","提现方式":"Phương thức rút tiền","请选择提现方式":"Hãy chọn phương thức rút tiền","提现账号":"Rút về tào khoản","请输入提现账号":"Hãy chọn tài khoản rút tiền","我知道了":"OK","第一步":"Bước 1","第二步":"Bước 2","打开Telegram搜索":"Mở Telegram tìm kiếm","向机器人发送你的":"Gửi cho bot","最后更新: {date}":"Cập nhật gần đây: {date}","还有没支付的订单":"Có đơn hàng chưa thanh toán","立即支付":"Thanh toán ngay","条工单正在处理中":" công việc đang xử lý","立即查看":"Xem Ngay","节点状态":"Trạng thái node","商品信息":"Thông tin","产品名称":"Tên sản phẩm","类型/周期":"Loại/Chu kỳ","产品流量":"Dung Lượng","订单信息":"Thông tin đơn hàng","关闭订单":"Đóng đơn hàng","订单号":"Mã đơn hàng","优惠金额":"Tiền ưu đãi","旧订阅折抵金额":"Tiền giảm giá gói cũ","退款金额":"Số tiền hoàn lại","余额支付":"Thanh toán số dư","工单历史":"Lịch sử đơn hàng","已用流量将在 {reset_day} 日后重置":"Dữ liệu đã sử dụng sẽ được đặt lại sau {reset_day} ngày","已用流量已在今日重置":"Dữ liệu đã sử dụng đã được đặt lại trong ngày hôm nay","重置已用流量":"Đặt lại dữ liệu đã sử dụng","查看节点状态":"Xem trạng thái nút","当前已使用流量达{rate}%":"Dữ liệu đã sử dụng hiện tại đạt {rate}%","节点名称":"Tên node","于 {date} 到期,距离到期还有 {day} 天。":"Hết hạn vào {date}, còn {day} ngày.","Telegram 讨论组":"Nhóm Telegram","立即加入":"Vào ngay","该订阅无法续费,仅允许新用户购买":"Đăng ký này không thể gia hạn, chỉ người dùng mới được phép mua","重置当月流量":"Đặt lại dung lượng tháng hiện tại","流量明细仅保留近月数据以供查询。":"Chi tiết dung lượng chỉ lưu dữ liệu của những tháng gần đây để truy vấn.","扣费倍率":"Tỷ lệ khấu trừ","支付手续费":"Phí thủ tục","续费订阅":"Gia hạn đăng ký","学习如何使用":"Hướng dẫn sử dụng","快速将节点导入对应客户端进行使用":"Bạn cần phải mua gói này","对您当前的订阅进行续费":"Gia hạn gói hiện tại","对您当前的订阅进行购买":"Mua gói bạn đã chọn","捷径":"Phím tắt","不会使用,查看使用教程":"Mua gói này nếu bạn đăng ký","使用支持扫码的客户端进行订阅":"Sử dụng ứng dụng quét mã để đăng ký","扫描二维码订阅":"Quét mã QR để đăng ký","续费":"Gia hạn","购买":"Mua","查看教程":"Xem hướng dẫn","注意":"Chú Ý","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"Bạn vẫn còn đơn đặt hàng chưa hoàn thành. Bạn cần hủy trước khi mua. Bạn có chắc chắn muốn hủy đơn đặt hàng trước đó không ?","确定取消":"Đúng/không","返回我的订单":"Quay lại đơn đặt hàng của tôi","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"Nếu bạn đã thanh toán, việc hủy đơn hàng có thể khiến việc thanh toán không thành công. Bạn có chắc chắn muốn hủy đơn hàng không ?","选择最适合你的计划":"Chọn kế hoạch phù hợp với bạn nhất","全部":"Tất cả","按周期":"Chu kỳ","遇到问题":"Chúng tôi có một vấn đề","遇到问题可以通过工单与我们沟通":"Nếu bạn gặp sự cố, bạn có thể liên lạc với chúng tôi thông qua ","按流量":"Theo lưu lượng","搜索文档":"Tìm kiếm tài liệu","技术支持":"Hỗ trợ kỹ thuật","当前剩余佣金":"Số dư hoa hồng hiện tại","三级分销比例":"Tỷ lệ phân phối cấp 3","累计获得佣金":"Tổng hoa hồng đã nhận","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"Người dùng bạn mời lại mời người dùng sẽ được chia theo tỷ lệ của số tiền đơn hàng nhân với cấp độ phân phối.","发放时间":"Thời gian thanh toán hoa hồng","{number} 人":"{number} người","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"Nếu địa chỉ đăng ký hoặc tài khoản của bạn bị rò rỉ và bị người khác lạm dụng, bạn có thể đặt lại thông tin đăng ký tại đây để tránh mất mát không cần thiết.","再次输入密码":"Nhập lại mật khẩu","返回登陆":"Quay lại Đăng nhập","选填":"Tùy chọn","必填":"Bắt buộc","最后回复时间":"Thời gian Trả lời Cuối cùng","请选项工单等级":"Vui lòng Chọn Mức độ Ưu tiên Công việc","回复":"Trả lời","输入内容回复工单":"Nhập Nội dung để Trả lời Công việc","已生成":"Đã tạo","选择协议":"Chọn Giao thức","自动":"Tự động","流量重置包":"Gói Reset Dữ liệu","复制失败":"Sao chép thất bại","提示":"Thông báo","确认退出?":"Xác nhận Đăng xuất?","已退出登录":"Đã đăng xuất thành công","请输入邮箱地址":"Nhập địa chỉ email","{second}秒后可重新发送":"Gửi lại sau {second} giây","发送成功":"Gửi thành công","请输入账号密码":"Nhập tên đăng nhập và mật khẩu","请确保两次密码输入一致":"Đảm bảo hai lần nhập mật khẩu giống nhau","注册成功":"Đăng ký thành công","重置密码成功,正在返回登录":"Đặt lại mật khẩu thành công, đang quay trở lại trang đăng nhập","确认取消":"Xác nhận Hủy","请注意,变更订阅会导致当前订阅被覆盖。":"Vui lòng lưu ý rằng thay đổi đăng ký sẽ ghi đè lên đăng ký hiện tại.","订单提交成功,正在跳转支付":"Đơn hàng đã được gửi thành công, đang chuyển hướng đến thanh toán.","回复成功":"Trả lời thành công","工单详情":"Chi tiết Ticket","登录成功":"Đăng nhập thành công","确定退出?":"Xác nhận thoát?","支付成功":"Thanh toán thành công","正在前往收银台":"Đang tiến hành thanh toán","请输入正确的划转金额":"Vui lòng nhập số tiền chuyển đúng","划转成功":"Chuyển khoản thành công","提现方式不能为空":"Phương thức rút tiền không được để trống","提现账号不能为空":"Tài khoản rút tiền không được để trống","已绑定":"Đã liên kết","创建成功":"Tạo thành công","关闭成功":"Đóng thành công","或使用第三方登录":"Hoặc đăng nhập bằng bên thứ ba","正在加载 Telegram 登录...":"Đang tải đăng nhập Telegram...","Telegram 登录失败":"Đăng nhập Telegram thất bại","下次流量重置时间":"Thời gian đặt lại lưu lượng tiếp theo:","已用流量将在 {time} 重置":"Dữ liệu đã sử dụng sẽ được đặt lại vào {time}"}},Symbol.toStringTag,{value:"Module"})),jQ=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"请求失败","月付":"月付","季付":"季付","半年付":"半年付","年付":"年付","两年付":"两年付","三年付":"三年付","一次性":"一次性","重置流量包":"重置流量包","待支付":"待支付","开通中":"开通中","已取消":"已取消","已完成":"已完成","已折抵":"已折抵","待确认":"待确认","发放中":"发放中","已发放":"已发放","无效":"无效","个人中心":"个人中心","登出":"登出","搜索":"搜索","仪表盘":"仪表盘","订阅":"订阅","我的订阅":"我的订阅","购买订阅":"购买订阅","财务":"财务","我的订单":"我的订单","我的邀请":"我的邀请","用户":"用户","我的工单":"我的工单","流量明细":"流量明细","使用文档":"使用文档","绑定Telegram获取更多服务":"绑定 Telegram 获取更多服务","点击这里进行绑定":"点击这里进行绑定","公告":"公告","总览":"总览","该订阅长期有效":"该订阅长期有效","已过期":"已过期","已用 {used} / 总计 {total}":"已用 {used} / 总计 {total}","查看订阅":"查看订阅","邮箱":"邮箱","邮箱验证码":"邮箱验证码","发送":"发送","重置密码":"重置密码","返回登入":"返回登入","邀请码":"邀请码","复制链接":"复制链接","完成时间":"完成时间","佣金":"佣金","已注册用户数":"已注册用户数","佣金比例":"佣金比例","确认中的佣金":"确认中的佣金","佣金将会在确认后会到达你的佣金账户。":"佣金将会在确认后到达您的佣金账户。","邀请码管理":"邀请码管理","生成邀请码":"生成邀请码","佣金发放记录":"佣金发放记录","复制成功":"复制成功","密码":"密码","登入":"登入","注册":"注册","忘记密码":"忘记密码","# 订单号":"# 订单号","周期":"周期","订单金额":"订单金额","订单状态":"订单状态","创建时间":"创建时间","操作":"操作","查看详情":"查看详情","请选择支付方式":"请选择支付方式","请检查信用卡支付信息":"请检查信用卡支付信息","订单详情":"订单详情","折扣":"折扣","折抵":"折抵","退款":"退款","支付方式":"支付方式","填写信用卡支付信息":"填写信用卡支付信息","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"您的信用卡信息只会用于当次扣款,系统并不会保存,我们认为这是最安全的。","订单总额":"订单总额","总计":"总计","结账":"结账","等待支付中":"等待支付中","订单系统正在进行处理,请稍等1-3分钟。":"订单系统正在进行处理,请等候 1-3 分钟。","订单由于超时支付已被取消。":"订单由于超时支付已被取消。","订单已支付并开通。":"订单已支付并开通。","选择订阅":"选择订阅","立即订阅":"立即订阅","配置订阅":"配置订阅","付款周期":"付款周期","有优惠券?":"有优惠券?","验证":"验证","下单":"下单","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"请注意,变更订阅会导致当前订阅被新订阅覆盖。","该订阅无法续费":"该订阅无法续费","选择其他订阅":"选择其它订阅","我的钱包":"我的钱包","账户余额(仅消费)":"账户余额(仅消费)","推广佣金(可提现)":"推广佣金(可提现)","钱包组成部分":"钱包组成部分","划转":"划转","推广佣金提现":"推广佣金提现","修改密码":"修改密码","保存":"保存","旧密码":"旧密码","新密码":"新密码","请输入旧密码":"请输入旧密码","请输入新密码":"请输入新密码","通知":"通知","到期邮件提醒":"到期邮件提醒","流量邮件提醒":"流量邮件提醒","绑定Telegram":"绑定 Telegram","立即开始":"立即开始","重置订阅信息":"重置订阅信息","重置":"重置","确定要重置订阅信息?":"确定要重置订阅信息?","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"如果您的订阅地址或信息发生泄露可以执行此操作。重置后您的 UUID 及订阅将会变更,需要重新导入订阅。","重置成功":"重置成功","两次新密码输入不同":"两次新密码输入不同","两次密码输入不同":"两次密码输入不同","邀请码(选填)":"邀请码(选填)",'我已阅读并同意 服务条款':'我已阅读并同意 服务条款',"请同意服务条款":"请同意服务条款","名称":"名称","标签":"标签","状态":"状态","节点五分钟内节点在线情况":"五分钟内节点在线情况","倍率":"倍率","使用的流量将乘以倍率进行扣除":"使用的流量将乘以倍率进行扣除","更多操作":"更多操作","没有可用节点,如果您未订阅或已过期请":"没有可用节点,如果您未订阅或已过期请","确定重置当前已用流量?":"确定重置当前已用流量?","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":"点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。","确定":"确定","低":"低","中":"中","高":"高","主题":"主题","工单级别":"工单级别","工单状态":"工单状态","最后回复":"最后回复","已关闭":"已关闭","待回复":"待回复","已回复":"已回复","查看":"查看","关闭":"关闭","新的工单":"新的工单","确认":"确认","请输入工单主题":"请输入工单主题","工单等级":"工单等级","请选择工单等级":"请选择工单等级","消息":"消息","请描述你遇到的问题":"请描述您遇到的问题","记录时间":"记录时间","实际上行":"实际上行","实际下行":"实际下行","合计":"合计","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量","复制订阅地址":"复制订阅地址","导入到":"导入到","一键订阅":"一键订阅","复制订阅":"复制订阅","推广佣金划转至余额":"推广佣金划转至余额","划转后的余额仅用于{title}消费使用":"划转后的余额仅用于{title}消费使用","当前推广佣金余额":"当前推广佣金余额","划转金额":"划转金额","请输入需要划转到余额的金额":"请输入需要划转到余额的金额","输入内容回复工单...":"输入内容回复工单...","申请提现":"申请提现","取消":"取消","提现方式":"提现方式","请选择提现方式":"请选择提现方式","提现账号":"提现账号","请输入提现账号":"请输入提现账号","我知道了":"我知道了","第一步":"第一步","第二步":"第二步","打开Telegram搜索":"打开 Telegram 搜索","向机器人发送你的":"向机器人发送您的","最后更新":"{date}","还有没支付的订单":"还有没支付的订单","立即支付":"立即支付","条工单正在处理中":"条工单正在处理中","立即查看":"立即查看","节点状态":"节点状态","商品信息":"商品信息","产品名称":"产品名称","类型/周期":"类型/周期","产品流量":"产品流量","订单信息":"订单信息","关闭订单":"关闭订单","订单号":"订单号","优惠金额":"优惠金额","旧订阅折抵金额":"旧订阅折抵金额","退款金额":"退款金额","余额支付":"余额支付","工单历史":"工单历史","已用流量将在 {time} 重置":"已用流量将在 {time} 重置","已用流量已在今日重置":"已用流量已在今日重置","重置已用流量":"重置已用流量","查看节点状态":"查看节点状态","当前已使用流量达{rate}%":"当前已使用流量达 {rate}%","节点名称":"节点名称","于 {date} 到期,距离到期还有 {day} 天。":"于 {date} 到期,距离到期还有 {day} 天。","Telegram 讨论组":"Telegram 讨论组","立即加入":"立即加入","该订阅无法续费,仅允许新用户购买":"该订阅无法续费,仅允许新用户购买","重置当月流量":"重置当月流量","流量明细仅保留近月数据以供查询。":"流量明细仅保留近一个月数据以供查询。","扣费倍率":"扣费倍率","支付手续费":"支付手续费","续费订阅":"续费订阅","学习如何使用":"学习如何使用","快速将节点导入对应客户端进行使用":"快速将节点导入对应客户端进行使用","对您当前的订阅进行续费":"对您当前的订阅进行续费","对您当前的订阅进行购买":"对您当前的订阅进行购买","捷径":"捷径","不会使用,查看使用教程":"不会使用,查看使用教程","使用支持扫码的客户端进行订阅":"使用支持扫码的客户端进行订阅","扫描二维码订阅":"扫描二维码订阅","续费":"续费","购买":"购买","查看教程":"查看教程","注意":"注意","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"您还有未完成的订单,购买前需要先取消,确定要取消之前的订单吗?","确定取消":"确定取消","返回我的订单":"返回我的订单","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"如果您已经付款,取消订单可能会导致支付失败,确定要取消订单吗?","选择最适合你的计划":"选择最适合您的计划","全部":"全部","按周期":"按周期","遇到问题":"遇到问题","遇到问题可以通过工单与我们沟通":"遇到问题可以通过工单与我们沟通","按流量":"按流量","搜索文档":"搜索文档","技术支持":"技术支持","当前剩余佣金":"当前剩余佣金","三级分销比例":"三级分销比例","累计获得佣金":"累计获得佣金","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。","发放时间":"发放时间","{number} 人":"{number} 人","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。","再次输入密码":"再次输入密码","返回登陆":"返回登录","选填":"选填","必填":"必填","最后回复时间":"最后回复时间","请选项工单等级":"请选择工单优先级","回复":"回复","输入内容回复工单":"输入内容回复工单","已生成":"已生成","选择协议":"选择协议","自动":"自动","流量重置包":"流量重置包","复制失败":"复制失败","提示":"提示","确认退出?":"确认退出?","已退出登录":"已成功退出登录","请输入邮箱地址":"请输入邮箱地址","{second}秒后可重新发送":"{second}秒后可重新发送","发送成功":"发送成功","请输入账号密码":"请输入账号密码","请确保两次密码输入一致":"请确保两次密码输入一致","注册成功":"注册成功","重置密码成功,正在返回登录":"重置密码成功,正在返回登录","确认取消":"确认取消","请注意,变更订阅会导致当前订阅被覆盖。":"请注意,变更订阅会导致当前订阅被覆盖。","订单提交成功,正在跳转支付":"订单提交成功,正在跳转支付","回复成功":"回复成功","工单详情":"工单详情","登录成功":"登录成功","确定退出?":"确定退出?","支付成功":"支付成功","正在前往收银台":"正在前往收银台","请输入正确的划转金额":"请输入正确的划转金额","划转成功":"划转成功","提现方式不能为空":"提现方式不能为空","提现账号不能为空":"提现账号不能为空","已绑定":"已绑定","创建成功":"创建成功","关闭成功":"关闭成功","或使用第三方登录":"或使用第三方登录","正在加载 Telegram 登录...":"正在加载 Telegram 登录...","Telegram 登录失败":"Telegram 登录失败","下次流量重置时间":"下次流量重置时间:"}},Symbol.toStringTag,{value:"Module"})),HQ=Object.freeze(Object.defineProperty({__proto__:null,default:{"请求失败":"請求失敗","月付":"月繳制","季付":"季繳","半年付":"半年缴","年付":"年繳","两年付":"兩年繳","三年付":"三年繳","一次性":"一次性","重置流量包":"重置流量包","待支付":"待支付","开通中":"開通中","已取消":"已取消","已完成":"已完成","已折抵":"已折抵","待确认":"待確認","发放中":"發放中","已发放":"已發放","无效":"無效","个人中心":"您的帳戸","登出":"登出","搜索":"搜尋","仪表盘":"儀表板","订阅":"訂閱","我的订阅":"我的訂閱","购买订阅":"購買訂閱","财务":"財務","我的订单":"我的訂單","我的邀请":"我的邀請","用户":"使用者","我的工单":"我的工單","流量明细":"流量明細","使用文档":"說明文件","绑定Telegram获取更多服务":"綁定 Telegram 獲取更多服務","点击这里进行绑定":"點擊這裡進行綁定","公告":"公告","总览":"總覽","该订阅长期有效":"該訂閱長期有效","已过期":"已過期","已用 {used} / 总计 {total}":"已用 {used} / 總計 {total}","查看订阅":"查看訂閱","邮箱":"郵箱","邮箱验证码":"郵箱驗證碼","发送":"傳送","重置密码":"重設密碼","返回登入":"返回登錄","邀请码":"邀請碼","复制链接":"複製鏈接","完成时间":"完成時間","佣金":"佣金","已注册用户数":"已註冊用戶數","佣金比例":"佣金比例","确认中的佣金":"確認中的佣金","佣金将会在确认后会到达你的佣金账户。":"佣金將會在確認後到達您的佣金帳戶。","邀请码管理":"邀請碼管理","生成邀请码":"生成邀請碼","佣金发放记录":"佣金發放記錄","复制成功":"複製成功","密码":"密碼","登入":"登入","注册":"註冊","忘记密码":"忘記密碼","# 订单号":"# 訂單號","周期":"週期","订单金额":"訂單金額","订单状态":"訂單狀態","创建时间":"創建時間","操作":"操作","查看详情":"查看詳情","请选择支付方式":"請選擇支付方式","请检查信用卡支付信息":"請檢查信用卡支付資訊","订单详情":"訂單詳情","折扣":"折扣","折抵":"折抵","退款":"退款","支付方式":"支付方式","填写信用卡支付信息":"填寫信用卡支付資訊","您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。":"您的信用卡資訊只會被用作當次扣款,系統並不會保存,我們認為這是最安全的。","订单总额":"訂單總額","总计":"總計","结账":"結賬","等待支付中":"等待支付中","订单系统正在进行处理,请稍等1-3分钟。":"訂單系統正在進行處理,請稍等 1-3 分鐘。","订单由于超时支付已被取消。":"訂單由於支付超時已被取消","订单已支付并开通。":"訂單已支付並開通","选择订阅":"選擇訂閱","立即订阅":"立即訂閱","配置订阅":"配置訂閱","付款周期":"付款週期","有优惠券?":"有優惠券?","验证":"驗證","下单":"下單","变更订阅会导致当前订阅被新订阅覆盖,请注意。":"請注意,變更訂閱會導致當前訂閱被新訂閱覆蓋。","该订阅无法续费":"該訂閱無法續費","选择其他订阅":"選擇其它訂閱","我的钱包":"我的錢包","账户余额(仅消费)":"賬戶餘額(僅消費)","推广佣金(可提现)":"推廣佣金(可提現)","钱包组成部分":"錢包組成部分","划转":"劃轉","推广佣金提现":"推廣佣金提現","修改密码":"修改密碼","保存":"儲存","旧密码":"舊密碼","新密码":"新密碼","请输入旧密码":"請輸入舊密碼","请输入新密码":"請輸入新密碼","通知":"通知","到期邮件提醒":"到期郵件提醒","流量邮件提醒":"流量郵件提醒","绑定Telegram":"綁定 Telegram","立即开始":"立即開始","重置订阅信息":"重置訂閲資訊","重置":"重置","确定要重置订阅信息?":"確定要重置訂閱資訊?","如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更,需要重新进行订阅。":"如果您的訂閱位址或資訊發生洩露可以執行此操作。重置後您的 UUID 及訂閱將會變更,需要重新導入訂閱。","重置成功":"重置成功","两次新密码输入不同":"兩次新密碼輸入不同","两次密码输入不同":"兩次密碼輸入不同","邀请码(选填)":"邀請碼(選填)",'我已阅读并同意 服务条款':'我已閱讀並同意 服務條款',"请同意服务条款":"請同意服務條款","名称":"名稱","标签":"標籤","状态":"狀態","节点五分钟内节点在线情况":"五分鐘內節點線上情況","倍率":"倍率","使用的流量将乘以倍率进行扣除":"使用的流量將乘以倍率進行扣除","更多操作":"更多操作","没有可用节点,如果您未订阅或已过期请":"沒有可用節點,如果您未訂閱或已過期請","确定重置当前已用流量?":"確定重置當前已用流量?","点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。":"點擊「確定」將會跳轉到收銀台,支付訂單後系統將會清空您當月已使用流量。","确定":"確定","低":"低","中":"中","高":"高","主题":"主題","工单级别":"工單級別","工单状态":"工單狀態","最后回复":"最新回復","已关闭":"已關閉","待回复":"待回復","已回复":"已回復","查看":"檢視","关闭":"關閉","新的工单":"新的工單","确认":"確認","请输入工单主题":"請輸入工單主題","工单等级":"工單等級","请选择工单等级":"請選擇工單等級","消息":"訊息","请描述你遇到的问题":"請描述您遇到的問題","记录时间":"記錄時間","实际上行":"實際上行","实际下行":"實際下行","合计":"合計","公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量":"公式:(實際上行 + 實際下行) x 扣費倍率 = 扣除流量","复制订阅地址":"複製訂閲位址","导入到":"导入到","一键订阅":"一鍵訂閲","复制订阅":"複製訂閲","推广佣金划转至余额":"推廣佣金劃轉至餘額","划转后的余额仅用于{title}消费使用":"劃轉后的餘額僅用於 {title} 消費使用","当前推广佣金余额":"當前推廣佣金餘額","划转金额":"劃轉金額","请输入需要划转到余额的金额":"請輸入需要劃轉到餘額的金額","输入内容回复工单...":"輸入内容回復工單…","申请提现":"申請提現","取消":"取消","提现方式":"提現方式","请选择提现方式":"請選擇提現方式","提现账号":"提現賬號","请输入提现账号":"請輸入提現賬號","我知道了":"我知道了","第一步":"步驟一","第二步":"步驟二","打开Telegram搜索":"打開 Telegram 並搜索","向机器人发送你的":"向機器人發送您的","最后更新: {date}":"最後更新: {date}","还有没支付的订单":"還有未支付的訂單","立即支付":"立即支付","条工单正在处理中":"條工單正在處理中","立即查看":"立即檢視","节点状态":"節點狀態","商品信息":"商品資訊","产品名称":"產品名稱","类型/周期":"類型/週期","产品流量":"產品流量","订单信息":"訂單信息","关闭订单":"關閉訂單","订单号":"訂單號","优惠金额":"優惠金額","旧订阅折抵金额":"舊訂閲折抵金額","退款金额":"退款金額","余额支付":"餘額支付","工单历史":"工單歷史","已用流量将在 {time} 重置":"已用流量將在 {time} 重置","已用流量已在今日重置":"已用流量已在今日重置","重置已用流量":"重置已用流量","查看节点状态":"查看節點狀態","当前已使用流量达{rate}%":"當前已用流量達 {rate}%","节点名称":"節點名稱","于 {date} 到期,距离到期还有 {day} 天。":"於 {date} 到期,距離到期還有 {day} 天。","Telegram 讨论组":"Telegram 討論組","立即加入":"立即加入","该订阅无法续费,仅允许新用户购买":"該訂閲無法續費,僅允許新用戶購買","重置当月流量":"重置當月流量","流量明细仅保留近月数据以供查询。":"流量明細僅保留近一個月資料以供查詢。","扣费倍率":"扣费倍率","支付手续费":"支付手續費","续费订阅":"續費訂閲","学习如何使用":"學習如何使用","快速将节点导入对应客户端进行使用":"快速將訂閲導入對應的客戶端進行使用","对您当前的订阅进行续费":"對您的當前訂閲進行續費","对您当前的订阅进行购买":"重新購買您的當前訂閲","捷径":"捷徑","不会使用,查看使用教程":"不會使用,檢視使用檔案","使用支持扫码的客户端进行订阅":"使用支持掃碼的客戶端進行訂閲","扫描二维码订阅":"掃描二維碼訂閲","续费":"續費","购买":"購買","查看教程":"查看教程","注意":"注意","你还有未完成的订单,购买前需要先进行取消,确定取消先前的订单吗?":"您还有未完成的订单,购买前需要先取消,确定要取消之前的订单吗?","确定取消":"確定取消","返回我的订单":"返回我的訂單","如果你已经付款,取消订单可能会导致支付失败,确定取消订单吗?":"如果您已經付款,取消訂單可能會導致支付失敗,確定要取消訂單嗎?","选择最适合你的计划":"選擇最適合您的計劃","全部":"全部","按周期":"按週期","遇到问题":"遇到問題","遇到问题可以通过工单与我们沟通":"遇到問題您可以通過工單與我們溝通","按流量":"按流量","搜索文档":"搜尋文檔","技术支持":"技術支援","当前剩余佣金":"当前剩余佣金","三级分销比例":"三级分销比例","累计获得佣金":"累计获得佣金","您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。":"您邀请的用户再次邀请用户将按照订单金额乘以分销等级的比例进行分成。","发放时间":"发放时间","{number} 人":"{number} 人","当你的订阅地址或账户发生泄漏被他人滥用时,可以在此重置订阅信息。避免带来不必要的损失。":"如果您的訂閱地址或帳戶洩漏並被他人濫用,您可以在此重置訂閱資訊,以避免不必要的損失。","再次输入密码":"請再次輸入密碼","返回登陆":"返回登入","选填":"選填","必填":"必填","最后回复时间":"最後回覆時間","请选项工单等级":"請選擇工單優先級","回复":"回覆","输入内容回复工单":"輸入內容回覆工單","已生成":"已生成","选择协议":"選擇協議","自动":"自動","流量重置包":"流量重置包","复制失败":"複製失敗","提示":"提示","确认退出?":"確認退出?","已退出登录":"已成功登出","请输入邮箱地址":"請輸入電子郵件地址","{second}秒后可重新发送":"{second} 秒後可重新發送","发送成功":"發送成功","请输入账号密码":"請輸入帳號和密碼","请确保两次密码输入一致":"請確保兩次密碼輸入一致","注册成功":"註冊成功","重置密码成功,正在返回登录":"重置密碼成功,正在返回登入","确认取消":"確認取消","请注意,变更订阅会导致当前订阅被覆盖。":"請注意,變更訂閱會導致目前的訂閱被覆蓋。","订单提交成功,正在跳转支付":"訂單提交成功,正在跳轉支付","回复成功":"回覆成功","工单详情":"工單詳情","登录成功":"登入成功","确定退出?":"確定退出?","支付成功":"支付成功","正在前往收银台":"正在前往收銀台","请输入正确的划转金额":"請輸入正確的劃轉金額","划转成功":"劃轉成功","提现方式不能为空":"提現方式不能為空","提现账号不能为空":"提現帳號不能為空","已绑定":"已綁定","创建成功":"創建成功","关闭成功":"關閉成功","或使用第三方登录":"或使用第三方登入","正在加载 Telegram 登录...":"正在載入 Telegram 登入...","Telegram 登录失败":"Telegram 登入失敗","下次流量重置时间":"下次流量重置時間:"}},Symbol.toStringTag,{value:"Module"}))}},function(){return t||(0,e[i(e)[0]])((t={exports:{}}).exports,t),t.exports});export default b(); diff --git a/Xboard/theme/Xboard/config.json b/Xboard/theme/Xboard/config.json new file mode 100644 index 0000000..4268ecd --- /dev/null +++ b/Xboard/theme/Xboard/config.json @@ -0,0 +1,33 @@ +{ + "name": "Xboard", + "description": "Xboard", + "version": "1.0.0", + "images": "", + "configs": [ + { + "label": "主题色", + "placeholder": "请选择主题颜色", + "field_name": "theme_color", + "field_type": "select", + "select_options": { + "default": "默认(绿色)", + "blue": "蓝色", + "black": "黑色", + "darkblue": "暗蓝色" + }, + "default_value": "default" + }, + { + "label": "背景", + "placeholder": "请输入背景图片URL", + "field_name": "background_url", + "field_type": "input" + }, + { + "label": "自定义页脚HTML", + "placeholder": "可以实现客服JS代码的加入等", + "field_name": "custom_html", + "field_type": "textarea" + } + ] +} diff --git a/Xboard/theme/Xboard/dashboard.blade.php b/Xboard/theme/Xboard/dashboard.blade.php new file mode 100644 index 0000000..7089e3f --- /dev/null +++ b/Xboard/theme/Xboard/dashboard.blade.php @@ -0,0 +1,264 @@ + + + + + + + {{$title}} + + + + + + + +
+ + {!! $theme_config['custom_html'] !!} + + + diff --git a/Xboard/theme/Xboard/env.example.js b/Xboard/theme/Xboard/env.example.js new file mode 100644 index 0000000..87c4464 --- /dev/null +++ b/Xboard/theme/Xboard/env.example.js @@ -0,0 +1,19 @@ +// API地址 +window.routerBase = 'http://127.0.0.1:8000/' +window.settings = { + // 站点名称 + title: 'Xboard', + // 站点描述 + description: 'Xboard', + assets_path: '/assets', + // 主题色 + theme: { + color: 'default', //可选default、blue、black、、darkblue + }, + // 版本号 + version: '0.1.1-dev', + // 登陆背景 + background_url: '', + // 站点LOGO + logo: '', +} diff --git a/Xboard/theme/Xboard/env.js b/Xboard/theme/Xboard/env.js new file mode 100644 index 0000000..df2ff57 --- /dev/null +++ b/Xboard/theme/Xboard/env.js @@ -0,0 +1,18 @@ +window.routerBase = 'http://127.0.0.1:8000/' +window.settings = { + // 站点名称 + title: 'Xboard', + // 主题色 + theme: { + color: 'anyway', //可选default、blue、black、、darkblue + }, + // 站点描述 + description: 'Xboard', + assets_path: '/assets', + // 版本号 + version: '0.1.1-dev', + // 登陆背景 + background_url: '', + // 站点LOGO + logo: '', +} diff --git a/Xboard/theme/Xboard/index.html b/Xboard/theme/Xboard/index.html new file mode 100644 index 0000000..edd75bf --- /dev/null +++ b/Xboard/theme/Xboard/index.html @@ -0,0 +1 @@ +Xboard
\ No newline at end of file diff --git a/Xboard/update.sh b/Xboard/update.sh new file mode 100644 index 0000000..079be75 --- /dev/null +++ b/Xboard/update.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ ! -d ".git" ]; then + echo "Please deploy using Git." + exit 1 +fi + +if ! command -v git &> /dev/null; then + echo "Git is not installed! Please install git and try again." + exit 1 +fi + +git config --global --add safe.directory $(pwd) +git fetch --all && git reset --hard origin/master && git pull origin master +rm -rf composer.lock composer.phar +wget https://github.com/composer/composer/releases/latest/download/composer.phar -O composer.phar +php composer.phar update -vvv +git submodule update --init --recursive --force +php artisan xboard:update + +if [ -f "/etc/init.d/bt" ] || [ -f "/.dockerenv" ]; then + chown -R www:www $(pwd); +fi + +if [ -d ".docker/.data" ]; then + chmod -R 777 .docker/.data +fi \ No newline at end of file