diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..7672e37
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,10 @@
+node_modules
+dist
+logs
+.git
+.github
+.vscode
+.idea
+.env
+.env.*
+*.md
\ No newline at end of file
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..15f1117
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,45 @@
+NODE_ENV=development
+
+# PORT
+PORT=3001
+
+VITE_VERSON_ABOUT=2.1.2
+
+# 高德地图密钥
+VITE_MAMP_KEY=your_amap_key_here
+
+# 三个模式:beta测试、production发布模式、none不显示版本Avatar模式
+VITE_ABOUTSERVER=beta
+
+# 开发水印
+VITE_DEV_WHATER=true
+
+#极验验证信息
+VITE_GEETEST_CAPTCHA_ID=your_geetest_id_here
+VITE_GEETEST_CAPTCHA_KEY=your_geetest_key_here
+
+# OneAccount 授权信息
+VITE_OA_ID=your_oa_id_here
+VITE_OA_SECRET=your_oa_secret_here
+VITE_OA_URL=your_oa_url_here
+
+# Database configuration
+MYSQL_HOST=localhost
+MYSQL_PORT=3306
+MYSQL_USER=your_mysql_user_here
+MYSQL_PASSWORD=your_mysql_password_here
+MYSQL_DATABASE=your_mysql_database_here
+
+# Redis configuration
+REDIS_HOST=localhost
+REDIS_PORT=6379
+REDIS_PASSWORD=your_redis_password_here
+REDIS_DATABASE=0
+
+# Email configuration
+EMAIL_HOST=your_email_host_here
+EMAIL_PORT=your_email_port_here
+
+# JWT configuration
+JWT_SECRET=your_jwt_secret_here
+JWT_EXPIRES_IN=72h
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..dfe0770
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aed6cba
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,32 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# 环境变量文件
+.env
+.env.*
+!.env.example
+
+# Editor directories and files
+.vscode/*
+.idea/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+.pnpm-store
diff --git a/GEETEST_SETUP.md b/GEETEST_SETUP.md
new file mode 100644
index 0000000..0d6c2fd
--- /dev/null
+++ b/GEETEST_SETUP.md
@@ -0,0 +1,137 @@
+# 极验验证码配置说明
+
+## 1. 获取极验配置
+
+1. 访问 [极验官网](https://www.geetest.com/) 注册账号
+2. 在控制台创建应用,获取 `captchaId`
+3. 配置域名白名单
+
+## 2. 环境变量配置
+
+在项目根目录创建 `.env` 文件:
+
+```env
+# 极验验证码配置
+VITE_GEETEST_CAPTCHA_KEY=你的captchaId
+
+# OAuth配置
+VITE_OA_ID=你的OAuth客户端ID
+VITE_OA_URL=https://oauth.example.com
+```
+
+## 3. CSP配置(如需要)
+
+如果遇到CSP策略阻止脚本加载,需要在服务器配置中添加以下域名到白名单:
+
+```html
+
+
+
+
+Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.geetest.com https://gcaptcha4.geetest.com https://gcaptcha4.geevisit.com https://gcaptcha4.gsensebot.com https://static.geevisit.com;
+```
+
+## 4. 后端验证
+
+在登录接口中需要验证极验返回的验证结果:
+
+```javascript
+// 后端验证示例
+const verifyGeetest = async (captchaResult) => {
+ const { lot_number, captcha_output, pass_token, gen_time } = captchaResult
+
+ // 调用极验服务端验证接口
+ const response = await fetch('https://gcaptcha4.geetest.com/validate', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ lot_number,
+ captcha_output,
+ pass_token,
+ gen_time,
+ captcha_id: process.env.GEETEST_CAPTCHA_ID,
+ sign_token: process.env.GEETEST_SECRET_KEY
+ })
+ })
+
+ const result = await response.json()
+ return result.status === 'success'
+}
+```
+
+## 5. 组件使用
+
+```vue
+
+
+
+
+
+```
+
+## 6. 安全注意事项
+
+- 不要在前端代码中硬编码 `captchaId`
+- 使用环境变量管理敏感配置
+- 后端必须验证极验返回的验证结果
+- 定期更新极验配置和密钥
+
+## 7. 故障排除
+
+### 常见问题
+
+1. **验证码不显示**
+ - 检查 `captchaId` 是否正确
+ - 检查域名是否在极验白名单中
+ - 检查网络连接
+
+2. **脚本加载失败**
+ - 检查CSP策略是否阻止了极验域名
+ - 确认环境变量 `VITE_GEETEST_CAPTCHA_KEY` 已正确设置
+ - 尝试刷新页面或点击重试按钮
+ - 检查浏览器控制台错误信息
+
+3. **验证失败**
+ - 检查后端验证逻辑
+ - 确认极验密钥配置正确
+ - 检查网络连接
+
+4. **CSP策略问题**
+ - 在服务器配置中添加极验域名到白名单
+ - 或者使用 `unsafe-inline` 策略(不推荐)
+
+### 调试步骤
+
+1. 打开浏览器开发者工具
+2. 查看Console标签页的错误信息
+3. 查看Network标签页的请求状态
+4. 检查Application标签页的CSP策略
+
+### 联系支持
+
+如果问题仍然存在,请:
+1. 收集浏览器控制台的错误信息
+2. 检查网络请求状态
+3. 联系极验技术支持或项目管理员
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..815e2dd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,152 @@
+
+
+
+
+
+# Space.Tab
+
+Space.Tab是一个现代化的Web应用程序,提供多种功能,包括用户认证、服务器管理、设备信息收集、天气查询、搜索引擎集成等功能。
+
+## 技术栈
+
+- 前端:Vue 3、Tailwind CSS、GSAP
+- 后端:Express.js、MySQL、Redis
+- 构建工具:Vite
+- 其他:JWT认证、Swagger API文档
+
+## 项目设置
+
+### 环境要求
+
+- Node.js (推荐v18+)
+- MySQL
+- Redis
+- PNPM (推荐v10+)
+
+### 安装依赖
+
+```bash
+# 安装依赖
+pnpm install
+```
+
+### 环境变量配置
+
+1. 复制环境变量示例文件作为基础
+```bash
+cp .env.example .env
+```
+
+2. 编辑`.env`文件,填入你的实际配置信息:
+ - 数据库连接信息(MySQL和Redis)
+ - API密钥(高德地图、极验验证等)
+ - JWT密钥
+ - 其他应用配置
+
+### 数据库初始化
+
+1. 确保MySQL服务已启动
+2. 创建数据库并导入初始数据
+```bash
+mysql -u your_username -p < sql/spacetab-data.sql
+```
+
+## 开发
+
+启动开发服务器:
+
+```bash
+pnpm dev
+```
+
+应用将在 http://localhost:3001 (或环境变量中配置的端口) 上运行。
+
+## 构建
+
+构建生产版本:
+
+```bash
+pnpm build
+```
+
+## 部署
+
+### 标准部署
+
+```bash
+# 构建项目
+pnpm build
+
+# 启动生产服务器
+pnpm start
+```
+
+### Docker部署
+
+```bash
+# 使用生产环境配置启动Docker容器
+pnpm docker
+```
+
+## 项目结构
+
+- `src/client/` - 前端Vue应用
+- `src/server/` - 后端Express服务器
+- `sql/` - 数据库初始化脚本
+- `public/` - 静态资源文件
+
+## 环境变量说明
+
+项目使用了多种环境变量来配置不同的功能。请参考`.env.example`文件了解所有可用的配置选项。关键配置包括:
+
+### 基础配置
+- `NODE_ENV` - 运行环境(development/production)
+- `PORT` - 服务器端口
+
+### 数据库配置
+- `MYSQL_HOST` - MySQL主机地址
+- `MYSQL_PORT` - MySQL端口
+- `MYSQL_USER` - MySQL用户名
+- `MYSQL_PASSWORD` - MySQL密码
+- `MYSQL_DATABASE` - MySQL数据库名
+
+### Redis配置
+- `REDIS_HOST` - Redis主机地址
+- `REDIS_PORT` - Redis端口
+- `REDIS_PASSWORD` - Redis密码
+- `REDIS_DATABASE` - Redis数据库索引
+
+### 认证配置
+- `JWT_SECRET` - JWT签名密钥
+- `JWT_EXPIRES_IN` - JWT过期时间
+
+### 第三方服务配置
+- `VITE_MAMP_KEY` - 高德地图API密钥
+- `VITE_GEETEST_CAPTCHA_ID` - 极验验证ID
+- `VITE_GEETEST_CAPTCHA_KEY` - 极验验证密钥
+
+## 安全注意事项
+
+1. **环境变量文件**:
+ - `.env`、`.env.*` 文件包含敏感信息,已在`.gitignore`中配置为不上传
+ - 永远不要将包含实际密钥的环境文件提交到版本控制系统
+
+2. **数据库凭证**:
+ - 生产环境中使用强密码
+ - 限制数据库用户权限
+
+3. **API密钥**:
+ - 定期轮换第三方服务的API密钥
+ - 实施适当的访问控制和速率限制
+
+## 贡献指南
+
+1. Fork项目
+2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
+3. 提交更改 (`git commit -m 'Add some amazing feature'`)
+4. 推送到分支 (`git push origin feature/amazing-feature`)
+5. 创建Pull Request
+
+## 许可证
+
+本项目遵守 GPL-3.0 license 协议,在协议规范内,禁止用于商业用途
\ No newline at end of file
diff --git a/components.json b/components.json
new file mode 100644
index 0000000..e91546f
--- /dev/null
+++ b/components.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://shadcn-vue.com/schema.json",
+ "style": "new-york",
+ "typescript": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/client/style.css",
+ "baseColor": "zinc",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "composables": "@/composables",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib"
+ },
+ "iconLibrary": "lucide"
+}
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..e19c44d
--- /dev/null
+++ b/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+ Space | 搜索
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..7b6e06e
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6192 @@
+{
+ "name": "space.tab",
+ "version": "2.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "space.tab",
+ "version": "2.0.0",
+ "dependencies": {
+ "@barba/core": "^2.10.3",
+ "@supabase/supabase-js": "^2.53.0",
+ "@tailwindcss/vite": "^4.1.10",
+ "@tanstack/vue-table": "^8.21.3",
+ "@types/swagger-jsdoc": "^6.0.4",
+ "@types/swagger-ui-express": "^4.1.8",
+ "@vueuse/core": "^13.6.0",
+ "@waline/client": "^3.5.7",
+ "bcryptjs": "^3.0.2",
+ "blueimp-md5": "^2.19.0",
+ "canvas-confetti": "^1.9.3",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cors": "^2.8.5",
+ "cross-env": "^7.0.3",
+ "dayjs": "^1.11.13",
+ "dotenv": "^16.5.0",
+ "echarts": "^5.6.0",
+ "express": "^5.1.0",
+ "gsap": "^3.13.0",
+ "helmet": "^8.1.0",
+ "html2canvas": "^1.4.1",
+ "ioredis": "^5.6.1",
+ "js-cookie": "^3.0.5",
+ "jsonwebtoken": "^9.0.2",
+ "lodash-es": "^4.17.21",
+ "lucide-vue-next": "^0.523.0",
+ "moment-timezone": "^0.6.0",
+ "morgan": "^1.10.0",
+ "mysql2": "^3.14.1",
+ "pinia": "^3.0.3",
+ "reka-ui": "^2.4.1",
+ "swagger-jsdoc": "^6.2.8",
+ "swagger-ui-express": "^5.0.1",
+ "tailwind-merge": "^3.3.1",
+ "tailwindcss": "^4.1.10",
+ "tsx": "^4.19.3",
+ "tw-animate-css": "^1.3.4",
+ "typescript": "^5.8.3",
+ "uuid": "^11.1.0",
+ "vaul-vue": "^0.4.1",
+ "vite-express": "*",
+ "vue": "^3.5.13",
+ "vue-router": "^4.5.1",
+ "vue-sonner": "^2.0.2",
+ "vuex": "^4.1.0"
+ },
+ "devDependencies": {
+ "@types/cors": "^2.8.19",
+ "@types/express": "^5.0.1",
+ "@types/js-cookie": "^3.0.6",
+ "@types/jsonwebtoken": "^9.0.10",
+ "@types/morgan": "^1.9.10",
+ "@types/node": "^22.15.2",
+ "@types/uuid": "^10.0.0",
+ "@vitejs/plugin-vue": "^5.2.3",
+ "axios": "^1.11.0",
+ "canvas": "^3.1.2",
+ "cheerio": "^1.1.2",
+ "nodemon": "^3.1.10",
+ "vite": "^6.3.5",
+ "vue-tsc": "^2.2.10"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@apidevtools/json-schema-ref-parser": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmmirror.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
+ "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
+ "license": "MIT",
+ "dependencies": {
+ "@jsdevtools/ono": "^7.1.3",
+ "@types/json-schema": "^7.0.6",
+ "call-me-maybe": "^1.0.1",
+ "js-yaml": "^4.1.0"
+ }
+ },
+ "node_modules/@apidevtools/openapi-schemas": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
+ "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@apidevtools/swagger-methods": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
+ "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
+ "license": "MIT"
+ },
+ "node_modules/@apidevtools/swagger-parser": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmmirror.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
+ "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
+ "license": "MIT",
+ "dependencies": {
+ "@apidevtools/json-schema-ref-parser": "^9.0.6",
+ "@apidevtools/openapi-schemas": "^2.0.4",
+ "@apidevtools/swagger-methods": "^3.0.2",
+ "@jsdevtools/ono": "^7.1.3",
+ "call-me-maybe": "^1.0.1",
+ "z-schema": "^5.0.1"
+ },
+ "peerDependencies": {
+ "openapi-types": ">=7"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz",
+ "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.2",
+ "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz",
+ "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@barba/core": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmmirror.com/@barba/core/-/core-2.10.3.tgz",
+ "integrity": "sha512-ovVeT1wUcdMg8Lu6Iel6yEBW2+s6iA7qfRLCVKTSIfJU9csiawU0UOmVJdcoy3WNOsF2Ur9UfoTUC9aDcm4c0w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-promise": "^4.0.0",
+ "path-to-regexp": "^6.2.2"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.3.tgz",
+ "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz",
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "license": "MIT"
+ },
+ "node_modules/@floating-ui/vue": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmmirror.com/@floating-ui/vue/-/vue-1.1.8.tgz",
+ "integrity": "sha512-SNJAa1jbT8Gh1LvWw2uIIViLL0saV2bCY59ISCvJzhbut5DSb2H3LKUK49Xkd7SixTNHKX4LFu59nbwIXt9jjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10",
+ "vue-demi": ">=0.13.0"
+ }
+ },
+ "node_modules/@floating-ui/vue/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@internationalized/date": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmmirror.com/@internationalized/date/-/date-3.8.2.tgz",
+ "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@internationalized/number": {
+ "version": "3.6.4",
+ "resolved": "https://registry.npmmirror.com/@internationalized/number/-/number-3.6.4.tgz",
+ "integrity": "sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@ioredis/commands": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@ioredis/commands/-/commands-1.3.0.tgz",
+ "integrity": "sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==",
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.30",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
+ "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@jsdevtools/ono": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmmirror.com/@jsdevtools/ono/-/ono-7.1.3.tgz",
+ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
+ "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
+ "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
+ "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
+ "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
+ "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
+ "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
+ "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
+ "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
+ "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
+ "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
+ "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
+ "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
+ "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
+ "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
+ "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
+ "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
+ "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
+ "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
+ "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
+ "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@scarf/scarf": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/@scarf/scarf/-/scarf-1.4.0.tgz",
+ "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@supabase/auth-js": {
+ "version": "2.71.1",
+ "resolved": "https://registry.npmmirror.com/@supabase/auth-js/-/auth-js-2.71.1.tgz",
+ "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/functions-js": {
+ "version": "2.4.5",
+ "resolved": "https://registry.npmmirror.com/@supabase/functions-js/-/functions-js-2.4.5.tgz",
+ "integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/node-fetch": {
+ "version": "2.6.15",
+ "resolved": "https://registry.npmmirror.com/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
+ "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ }
+ },
+ "node_modules/@supabase/postgrest-js": {
+ "version": "1.19.4",
+ "resolved": "https://registry.npmmirror.com/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz",
+ "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/realtime-js": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmmirror.com/@supabase/realtime-js/-/realtime-js-2.15.1.tgz",
+ "integrity": "sha512-edRFa2IrQw50kNntvUyS38hsL7t2d/psah6om6aNTLLcWem0R6bOUq7sk7DsGeSlNfuwEwWn57FdYSva6VddYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.13",
+ "@types/phoenix": "^1.6.6",
+ "@types/ws": "^8.18.1",
+ "ws": "^8.18.2"
+ }
+ },
+ "node_modules/@supabase/storage-js": {
+ "version": "2.10.5",
+ "resolved": "https://registry.npmmirror.com/@supabase/storage-js/-/storage-js-2.10.5.tgz",
+ "integrity": "sha512-9O7DfVI+v+exwPIKFGlCn2gigx441ojHnJ5QpHPegvblPdZQnnmCrL+A0JHj7wD/e+f9fnLu/DBKAtLYrLtftw==",
+ "license": "MIT",
+ "dependencies": {
+ "@supabase/node-fetch": "^2.6.14"
+ }
+ },
+ "node_modules/@supabase/supabase-js": {
+ "version": "2.55.0",
+ "resolved": "https://registry.npmmirror.com/@supabase/supabase-js/-/supabase-js-2.55.0.tgz",
+ "integrity": "sha512-Y1uV4nEMjQV1x83DGn7+Z9LOisVVRlY1geSARrUHbXWgbyKLZ6/08dvc0Us1r6AJ4tcKpwpCZWG9yDQYo1JgHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@supabase/auth-js": "2.71.1",
+ "@supabase/functions-js": "2.4.5",
+ "@supabase/node-fetch": "2.6.15",
+ "@supabase/postgrest-js": "1.19.4",
+ "@supabase/realtime-js": "2.15.1",
+ "@supabase/storage-js": "^2.10.4"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.17",
+ "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.17.tgz",
+ "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@swc/helpers/node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/node/-/node-4.1.11.tgz",
+ "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "enhanced-resolve": "^5.18.1",
+ "jiti": "^2.4.2",
+ "lightningcss": "1.30.1",
+ "magic-string": "^0.30.17",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.11"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide/-/oxide-4.1.11.tgz",
+ "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.4",
+ "tar": "^7.4.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.11",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.11",
+ "@tailwindcss/oxide-darwin-x64": "4.1.11",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.11",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.11",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.11",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.11",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.11",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.11"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz",
+ "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz",
+ "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz",
+ "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz",
+ "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz",
+ "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz",
+ "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz",
+ "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz",
+ "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz",
+ "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz",
+ "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@emnapi/wasi-threads": "^1.0.2",
+ "@napi-rs/wasm-runtime": "^0.2.11",
+ "@tybys/wasm-util": "^0.9.0",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz",
+ "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz",
+ "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/@tailwindcss/vite/-/vite-4.1.11.tgz",
+ "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.1.11",
+ "@tailwindcss/oxide": "4.1.11",
+ "tailwindcss": "4.1.11"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7"
+ }
+ },
+ "node_modules/@tanstack/table-core": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmmirror.com/@tanstack/table-core/-/table-core-8.21.3.tgz",
+ "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.13.12",
+ "resolved": "https://registry.npmmirror.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
+ "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/vue-table": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmmirror.com/@tanstack/vue-table/-/vue-table-8.21.3.tgz",
+ "integrity": "sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/table-core": "8.21.3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "vue": ">=3.2"
+ }
+ },
+ "node_modules/@tanstack/vue-virtual": {
+ "version": "3.13.12",
+ "resolved": "https://registry.npmmirror.com/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz",
+ "integrity": "sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.13.12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "vue": "^2.7.0 || ^3.0.0"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.6",
+ "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz",
+ "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.19",
+ "resolved": "https://registry.npmmirror.com/@types/cors/-/cors-2.8.19.tgz",
+ "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/express": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmmirror.com/@types/express/-/express-5.0.3.tgz",
+ "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^5.0.0",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz",
+ "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.5.tgz",
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/js-cookie": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-3.0.6.tgz",
+ "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.10",
+ "resolved": "https://registry.npmmirror.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
+ "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/morgan": {
+ "version": "1.9.10",
+ "resolved": "https://registry.npmmirror.com/@types/morgan/-/morgan-1.9.10.tgz",
+ "integrity": "sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.17.1",
+ "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.17.1.tgz",
+ "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/phoenix": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmmirror.com/@types/phoenix/-/phoenix-1.6.6.tgz",
+ "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/send": {
+ "version": "0.17.5",
+ "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.5.tgz",
+ "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.15.8",
+ "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.8.tgz",
+ "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/swagger-jsdoc": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmmirror.com/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz",
+ "integrity": "sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/swagger-ui-express": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmmirror.com/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz",
+ "integrity": "sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/express": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmmirror.com/@types/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/web-bluetooth": {
+ "version": "0.0.21",
+ "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
+ "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/ws": {
+ "version": "8.18.1",
+ "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
+ "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0 || ^6.0.0",
+ "vue": "^3.2.25"
+ }
+ },
+ "node_modules/@volar/language-core": {
+ "version": "2.4.15",
+ "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.15.tgz",
+ "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/source-map": "2.4.15"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "2.4.15",
+ "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.15.tgz",
+ "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@volar/typescript": {
+ "version": "2.4.15",
+ "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.15.tgz",
+ "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.15",
+ "path-browserify": "^1.0.1",
+ "vscode-uri": "^3.0.8"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
+ "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.0",
+ "@vue/shared": "3.5.18",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
+ "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.18",
+ "@vue/shared": "3.5.18"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
+ "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.0",
+ "@vue/compiler-core": "3.5.18",
+ "@vue/compiler-dom": "3.5.18",
+ "@vue/compiler-ssr": "3.5.18",
+ "@vue/shared": "3.5.18",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.17",
+ "postcss": "^8.5.6",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
+ "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.18",
+ "@vue/shared": "3.5.18"
+ }
+ },
+ "node_modules/@vue/compiler-vue2": {
+ "version": "2.7.16",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
+ "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "de-indent": "^1.0.2",
+ "he": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz",
+ "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^7.7.7"
+ }
+ },
+ "node_modules/@vue/devtools-kit": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
+ "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^7.7.7",
+ "birpc": "^2.3.0",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^1.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/@vue/devtools-shared": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
+ "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
+ "node_modules/@vue/language-core": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.12.tgz",
+ "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.15",
+ "@vue/compiler-dom": "^3.5.0",
+ "@vue/compiler-vue2": "^2.7.16",
+ "@vue/shared": "^3.5.0",
+ "alien-signals": "^1.0.3",
+ "minimatch": "^9.0.3",
+ "muggle-string": "^0.4.1",
+ "path-browserify": "^1.0.1"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vue/language-core/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@vue/language-core/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.18.tgz",
+ "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.18"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.18.tgz",
+ "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.18",
+ "@vue/shared": "3.5.18"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz",
+ "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.18",
+ "@vue/runtime-core": "3.5.18",
+ "@vue/shared": "3.5.18",
+ "csstype": "^3.1.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.18.tgz",
+ "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.18",
+ "@vue/shared": "3.5.18"
+ },
+ "peerDependencies": {
+ "vue": "3.5.18"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.18.tgz",
+ "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
+ "license": "MIT"
+ },
+ "node_modules/@vueuse/core": {
+ "version": "13.6.0",
+ "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-13.6.0.tgz",
+ "integrity": "sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/web-bluetooth": "^0.0.21",
+ "@vueuse/metadata": "13.6.0",
+ "@vueuse/shared": "13.6.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "vue": "^3.5.0"
+ }
+ },
+ "node_modules/@vueuse/metadata": {
+ "version": "13.6.0",
+ "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-13.6.0.tgz",
+ "integrity": "sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/@vueuse/shared": {
+ "version": "13.6.0",
+ "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-13.6.0.tgz",
+ "integrity": "sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "vue": "^3.5.0"
+ }
+ },
+ "node_modules/@waline/api": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/@waline/api/-/api-1.0.0.tgz",
+ "integrity": "sha512-o0lWjt+/oZH1/4q3DJxTf5kdkgNbSmoLRXIiGznW225A6hq9/9IkOO1DiAijIsxGYJS6psFC+58+IzkD1EerBA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@waline/client": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmmirror.com/@waline/client/-/client-3.6.0.tgz",
+ "integrity": "sha512-GzRHOpyveRlJamS/QOQ6OItYzvU6px4wtb9enGiwtWcODHSgU+21GZd++czjLwwiyiEgMNOCe8Wqll4ianI38w==",
+ "license": "MIT",
+ "dependencies": {
+ "@vueuse/core": "^13.5.0",
+ "@waline/api": "1.0.0",
+ "autosize": "^6.0.1",
+ "marked": "^16.0.0",
+ "marked-highlight": "^2.2.2",
+ "recaptcha-v3": "^1.11.3",
+ "vue": "^3.5.17"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/alien-signals": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.13.tgz",
+ "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-hidden": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmmirror.com/aria-hidden/-/aria-hidden-1.2.6.tgz",
+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/autosize": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/autosize/-/autosize-6.0.1.tgz",
+ "integrity": "sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==",
+ "license": "MIT"
+ },
+ "node_modules/aws-ssl-profiles": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
+ "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz",
+ "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.4",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/base64-arraybuffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+ "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/basic-auth/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/bcryptjs": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/bcryptjs/-/bcryptjs-3.0.2.tgz",
+ "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==",
+ "license": "BSD-3-Clause",
+ "bin": {
+ "bcrypt": "bin/bcrypt"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/birpc": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz",
+ "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/blueimp-md5": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmmirror.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
+ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==",
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.0.tgz",
+ "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.0",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.6.3",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.0",
+ "raw-body": "^3.0.0",
+ "type-is": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-me-maybe": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
+ "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
+ "license": "MIT"
+ },
+ "node_modules/canvas": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/canvas/-/canvas-3.1.2.tgz",
+ "integrity": "sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "node-addon-api": "^7.0.0",
+ "prebuild-install": "^7.1.3"
+ },
+ "engines": {
+ "node": "^18.12.0 || >= 20.9.0"
+ }
+ },
+ "node_modules/canvas-confetti": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmmirror.com/canvas-confetti/-/canvas-confetti-1.9.3.tgz",
+ "integrity": "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==",
+ "license": "ISC",
+ "funding": {
+ "type": "donate",
+ "url": "https://www.paypal.me/kirilvatev"
+ }
+ },
+ "node_modules/cheerio": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/cheerio/-/cheerio-1.1.2.tgz",
+ "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cheerio-select": "^2.1.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.2.2",
+ "encoding-sniffer": "^0.2.1",
+ "htmlparser2": "^10.0.0",
+ "parse5": "^7.3.0",
+ "parse5-htmlparser2-tree-adapter": "^7.1.0",
+ "parse5-parser-stream": "^7.1.2",
+ "undici": "^7.12.0",
+ "whatwg-mimetype": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=20.18.1"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+ }
+ },
+ "node_modules/cheerio-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz",
+ "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-select": "^5.1.0",
+ "css-what": "^6.1.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmmirror.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cluster-key-slot": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-6.2.0.tgz",
+ "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "license": "MIT"
+ },
+ "node_modules/content-disposition": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.0.0.tgz",
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/copy-anything": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz",
+ "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+ "license": "MIT",
+ "dependencies": {
+ "is-what": "^4.1.8"
+ },
+ "engines": {
+ "node": ">=12.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-line-break": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+ "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+ "license": "MIT",
+ "dependencies": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "license": "MIT"
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
+ "license": "MIT"
+ },
+ "node_modules/de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/defu": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz",
+ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
+ "license": "MIT"
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/echarts": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
+ "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "2.3.0",
+ "zrender": "5.6.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/encoding-sniffer": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmmirror.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
+ "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "^0.6.3",
+ "whatwg-encoding": "^3.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.3",
+ "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
+ "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.9.tgz",
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.9",
+ "@esbuild/android-arm": "0.25.9",
+ "@esbuild/android-arm64": "0.25.9",
+ "@esbuild/android-x64": "0.25.9",
+ "@esbuild/darwin-arm64": "0.25.9",
+ "@esbuild/darwin-x64": "0.25.9",
+ "@esbuild/freebsd-arm64": "0.25.9",
+ "@esbuild/freebsd-x64": "0.25.9",
+ "@esbuild/linux-arm": "0.25.9",
+ "@esbuild/linux-arm64": "0.25.9",
+ "@esbuild/linux-ia32": "0.25.9",
+ "@esbuild/linux-loong64": "0.25.9",
+ "@esbuild/linux-mips64el": "0.25.9",
+ "@esbuild/linux-ppc64": "0.25.9",
+ "@esbuild/linux-riscv64": "0.25.9",
+ "@esbuild/linux-s390x": "0.25.9",
+ "@esbuild/linux-x64": "0.25.9",
+ "@esbuild/netbsd-arm64": "0.25.9",
+ "@esbuild/netbsd-x64": "0.25.9",
+ "@esbuild/openbsd-arm64": "0.25.9",
+ "@esbuild/openbsd-x64": "0.25.9",
+ "@esbuild/openharmony-arm64": "0.25.9",
+ "@esbuild/sunos-x64": "0.25.9",
+ "@esbuild/win32-arm64": "0.25.9",
+ "@esbuild/win32-ia32": "0.25.9",
+ "@esbuild/win32-x64": "0.25.9"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "dev": true,
+ "license": "(MIT OR WTFPL)",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/express/-/express-5.1.0.tgz",
+ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.0",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-static-gzip": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/express-static-gzip/-/express-static-gzip-2.2.0.tgz",
+ "integrity": "sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==",
+ "license": "MIT",
+ "dependencies": {
+ "parseurl": "^1.3.3",
+ "serve-static": "^1.16.2"
+ }
+ },
+ "node_modules/express-static-gzip/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express-static-gzip/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/express-static-gzip/node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express-static-gzip/node_modules/send": {
+ "version": "0.19.0",
+ "resolved": "https://registry.npmmirror.com/send/-/send-0.19.0.tgz",
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/express-static-gzip/node_modules/send/node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/express-static-gzip/node_modules/serve-static": {
+ "version": "1.16.2",
+ "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz",
+ "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.19.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/express-static-gzip/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-property": "^1.0.2"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
+ "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
+ "node_modules/gsap": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmmirror.com/gsap/-/gsap-3.13.0.tgz",
+ "integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==",
+ "license": "Standard 'no charge' license: https://gsap.com/standard-license."
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/helmet": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmmirror.com/helmet/-/helmet-8.1.0.tgz",
+ "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/hookable": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+ "license": "MIT"
+ },
+ "node_modules/html2canvas": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+ "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+ "license": "MIT",
+ "dependencies": {
+ "css-line-break": "^2.1.0",
+ "text-segmentation": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-10.0.0.tgz",
+ "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.2.1",
+ "entities": "^6.0.0"
+ }
+ },
+ "node_modules/htmlparser2/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-errors/node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ioredis": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmmirror.com/ioredis/-/ioredis-5.7.0.tgz",
+ "integrity": "sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@ioredis/commands": "^1.3.0",
+ "cluster-key-slot": "^1.1.0",
+ "debug": "^4.3.4",
+ "denque": "^2.1.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.isarguments": "^3.1.0",
+ "redis-errors": "^1.2.0",
+ "redis-parser": "^3.0.0",
+ "standard-as-callback": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ioredis"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "license": "MIT"
+ },
+ "node_modules/is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
+ "license": "MIT"
+ },
+ "node_modules/is-what": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
+ "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/jiti": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.5.1.tgz",
+ "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmmirror.com/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmmirror.com/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.30.1.tgz",
+ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-darwin-arm64": "1.30.1",
+ "lightningcss-darwin-x64": "1.30.1",
+ "lightningcss-freebsd-x64": "1.30.1",
+ "lightningcss-linux-arm-gnueabihf": "1.30.1",
+ "lightningcss-linux-arm64-gnu": "1.30.1",
+ "lightningcss-linux-arm64-musl": "1.30.1",
+ "lightningcss-linux-x64-gnu": "1.30.1",
+ "lightningcss-linux-x64-musl": "1.30.1",
+ "lightningcss-win32-arm64-msvc": "1.30.1",
+ "lightningcss-win32-x64-msvc": "1.30.1"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
+ "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
+ "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
+ "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
+ "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
+ "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
+ "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
+ "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
+ "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
+ "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
+ "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmmirror.com/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+ "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
+ "license": "MIT"
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isarguments": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+ "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.mergewith": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmmirror.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmmirror.com/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/lru.min": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/lru.min/-/lru.min-1.1.2.tgz",
+ "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==",
+ "license": "MIT",
+ "engines": {
+ "bun": ">=1.0.0",
+ "deno": ">=1.30.0",
+ "node": ">=8.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wellwelwel"
+ }
+ },
+ "node_modules/lucide-vue-next": {
+ "version": "0.523.0",
+ "resolved": "https://registry.npmmirror.com/lucide-vue-next/-/lucide-vue-next-0.523.0.tgz",
+ "integrity": "sha512-PLbjArdcFgUcJLtszpCzGyaGm4AUfZ/SzDKfPjhkoEpG6j5k6fS3AGbMEbwt/xGJdD9DuSEgypNT/eqSPLfHfw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "vue": ">=3.0.1"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/marked": {
+ "version": "16.1.2",
+ "resolved": "https://registry.npmmirror.com/marked/-/marked-16.1.2.tgz",
+ "integrity": "sha512-rNQt5EvRinalby7zJZu/mB+BvaAY2oz3wCuCjt1RDrWNpS1Pdf9xqMOeC9Hm5adBdcV/3XZPJpG58eT+WBc0XQ==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/marked-highlight": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmmirror.com/marked-highlight/-/marked-highlight-2.2.2.tgz",
+ "integrity": "sha512-KlHOP31DatbtPPXPaI8nx1KTrG3EW0Z5zewCwpUj65swbtKOTStteK3sNAjBqV75Pgo3fNEVNHeptg18mDuWgw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "marked": ">=4 <17"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-3.0.2.tgz",
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
+ "node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/moment-timezone": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.6.0.tgz",
+ "integrity": "sha512-ldA5lRNm3iJCWZcBCab4pnNL3HSZYXVb/3TYr75/1WCTWYuTqYUb5f/S384pncYjJ88lbO8Z4uPDvmoluHJc8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "moment": "^2.29.4"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/morgan": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmmirror.com/morgan/-/morgan-1.10.1.tgz",
+ "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
+ "license": "MIT",
+ "dependencies": {
+ "basic-auth": "~2.0.1",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/morgan/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/morgan/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/morgan/node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mysql2": {
+ "version": "3.14.3",
+ "resolved": "https://registry.npmmirror.com/mysql2/-/mysql2-3.14.3.tgz",
+ "integrity": "sha512-fD6MLV8XJ1KiNFIF0bS7Msl8eZyhlTDCDl75ajU5SJtpdx9ZPEACulJcqJWr1Y8OYyxsFc4j3+nflpmhxCU5aQ==",
+ "license": "MIT",
+ "dependencies": {
+ "aws-ssl-profiles": "^1.1.1",
+ "denque": "^2.1.0",
+ "generate-function": "^2.3.1",
+ "iconv-lite": "^0.6.3",
+ "long": "^5.2.1",
+ "lru.min": "^1.0.0",
+ "named-placeholders": "^1.1.3",
+ "seq-queue": "^0.0.5",
+ "sqlstring": "^2.3.2"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/named-placeholders": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmmirror.com/named-placeholders/-/named-placeholders-1.1.3.tgz",
+ "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "^7.14.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-build-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-abi": {
+ "version": "3.75.0",
+ "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.75.0.tgz",
+ "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nodemon": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmmirror.com/nodemon/-/nodemon-3.1.10.tgz",
+ "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^4",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^3.1.2",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ohash": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
+ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
+ "license": "MIT"
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.1.0.tgz",
+ "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/openapi-types": {
+ "version": "12.1.3",
+ "resolved": "https://registry.npmmirror.com/openapi-types/-/openapi-types-12.1.3.tgz",
+ "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
+ "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domhandler": "^5.0.3",
+ "parse5": "^7.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-parser-stream": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmmirror.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
+ "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^7.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
+ "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
+ "license": "MIT"
+ },
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pinia": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz",
+ "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^7.7.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.4.4",
+ "vue": "^2.7.0 || ^3.5.11"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prebuild-install": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.3.tgz",
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp-classic": "^0.5.3",
+ "napi-build-utils": "^2.0.0",
+ "node-abi": "^3.3.0",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^4.0.0",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmmirror.com/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz",
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.0.tgz",
+ "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.6.3",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/recaptcha-v3": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmmirror.com/recaptcha-v3/-/recaptcha-v3-1.11.3.tgz",
+ "integrity": "sha512-sEE6J0RzUkS+sKEBpgCD/AqCU0ffrAVOADGjvAx9vcttN+VLK42SWMkj/J/I6vHu3Kew+xcfbBqDVb65N0QGDw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/redis-errors": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/redis-errors/-/redis-errors-1.2.0.tgz",
+ "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/redis-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/redis-parser/-/redis-parser-3.0.0.tgz",
+ "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
+ "license": "MIT",
+ "dependencies": {
+ "redis-errors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reka-ui": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmmirror.com/reka-ui/-/reka-ui-2.4.1.tgz",
+ "integrity": "sha512-NB7DrCsODN8MH02BWtgiExygfFcuuZ5/PTn6fMgjppmFHqePvNhmSn1LEuF35nel6PFbA4v+gdj0IoGN1yZ+vw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.13",
+ "@floating-ui/vue": "^1.1.6",
+ "@internationalized/date": "^3.5.0",
+ "@internationalized/number": "^3.5.0",
+ "@tanstack/vue-virtual": "^3.12.0",
+ "@vueuse/core": "^12.5.0",
+ "@vueuse/shared": "^12.5.0",
+ "aria-hidden": "^1.2.4",
+ "defu": "^6.1.4",
+ "ohash": "^2.0.11"
+ },
+ "peerDependencies": {
+ "vue": ">= 3.2.0"
+ }
+ },
+ "node_modules/reka-ui/node_modules/@vueuse/core": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-12.8.2.tgz",
+ "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/web-bluetooth": "^0.0.21",
+ "@vueuse/metadata": "12.8.2",
+ "@vueuse/shared": "12.8.2",
+ "vue": "^3.5.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/reka-ui/node_modules/@vueuse/metadata": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.8.2.tgz",
+ "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/reka-ui/node_modules/@vueuse/shared": {
+ "version": "12.8.2",
+ "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.8.2.tgz",
+ "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==",
+ "license": "MIT",
+ "dependencies": {
+ "vue": "^3.5.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "license": "MIT"
+ },
+ "node_modules/rollup": {
+ "version": "4.46.2",
+ "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.46.2.tgz",
+ "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.46.2",
+ "@rollup/rollup-android-arm64": "4.46.2",
+ "@rollup/rollup-darwin-arm64": "4.46.2",
+ "@rollup/rollup-darwin-x64": "4.46.2",
+ "@rollup/rollup-freebsd-arm64": "4.46.2",
+ "@rollup/rollup-freebsd-x64": "4.46.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.46.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.46.2",
+ "@rollup/rollup-linux-arm64-musl": "4.46.2",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.46.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.46.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.46.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.46.2",
+ "@rollup/rollup-linux-x64-gnu": "4.46.2",
+ "@rollup/rollup-linux-x64-musl": "4.46.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.46.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.46.2",
+ "@rollup/rollup-win32-x64-msvc": "4.46.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/router/node_modules/path-to-regexp": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
+ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.5",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "mime-types": "^3.0.1",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/seq-queue": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmmirror.com/seq-queue/-/seq-queue-0.0.5.tgz",
+ "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/simple-get": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ },
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/speakingurl": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
+ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sqlstring": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmmirror.com/sqlstring/-/sqlstring-2.3.3.tgz",
+ "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/standard-as-callback": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
+ "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
+ "license": "MIT"
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/superjson": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz",
+ "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "copy-anything": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/swagger-jsdoc": {
+ "version": "6.2.8",
+ "resolved": "https://registry.npmmirror.com/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
+ "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
+ "license": "MIT",
+ "dependencies": {
+ "commander": "6.2.0",
+ "doctrine": "3.0.0",
+ "glob": "7.1.6",
+ "lodash.mergewith": "^4.6.2",
+ "swagger-parser": "^10.0.3",
+ "yaml": "2.0.0-1"
+ },
+ "bin": {
+ "swagger-jsdoc": "bin/swagger-jsdoc.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/swagger-jsdoc/node_modules/yaml": {
+ "version": "2.0.0-1",
+ "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.0.0-1.tgz",
+ "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/swagger-parser": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmmirror.com/swagger-parser/-/swagger-parser-10.0.3.tgz",
+ "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
+ "license": "MIT",
+ "dependencies": {
+ "@apidevtools/swagger-parser": "10.0.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/swagger-ui-dist": {
+ "version": "5.27.1",
+ "resolved": "https://registry.npmmirror.com/swagger-ui-dist/-/swagger-ui-dist-5.27.1.tgz",
+ "integrity": "sha512-oGtpYO3lnoaqyGtlJalvryl7TwzgRuxpOVWqEHx8af0YXI+Kt+4jMpLdgMtMcmWmuQ0QTCHLKExwrBFMSxvAUA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@scarf/scarf": "=1.4.0"
+ }
+ },
+ "node_modules/swagger-ui-express": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz",
+ "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==",
+ "license": "MIT",
+ "dependencies": {
+ "swagger-ui-dist": ">=5.0.0"
+ },
+ "engines": {
+ "node": ">= v0.10.32"
+ },
+ "peerDependencies": {
+ "express": ">=4.0.0 || >=5.0.0-beta"
+ }
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
+ "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.1.11.tgz",
+ "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.2.tgz",
+ "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmmirror.com/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.3.tgz",
+ "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-fs/node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/text-segmentation": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+ "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+ "license": "MIT",
+ "dependencies": {
+ "utrie": "^1.0.2"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/touch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/touch/-/touch-3.1.1.tgz",
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
+ "node_modules/tslib": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+ "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.20.4",
+ "resolved": "https://registry.npmmirror.com/tsx/-/tsx-4.20.4.tgz",
+ "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.25.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tw-animate-css": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmmirror.com/tw-animate-css/-/tw-animate-css-1.3.6.tgz",
+ "integrity": "sha512-9dy0R9UsYEGmgf26L8UcHiLmSFTHa9+D7+dAt/G/sF5dCnPePZbfgDYinc7/UzAM7g/baVrmS6m9yEpU46d+LA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Wombosvideo"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.2.tgz",
+ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/undefsafe/-/undefsafe-2.0.5.tgz",
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/undici": {
+ "version": "7.13.0",
+ "resolved": "https://registry.npmmirror.com/undici/-/undici-7.13.0.tgz",
+ "integrity": "sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.18.1"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/utrie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+ "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-arraybuffer": "^1.0.2"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
+ "node_modules/validator": {
+ "version": "13.15.15",
+ "resolved": "https://registry.npmmirror.com/validator/-/validator-13.15.15.tgz",
+ "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vaul-vue": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/vaul-vue/-/vaul-vue-0.4.1.tgz",
+ "integrity": "sha512-A6jOWOZX5yvyo1qMn7IveoWN91mJI5L3BUKsIwkg6qrTGgHs1Sb1JF/vyLJgnbN1rH4OOOxFbtqL9A46bOyGUQ==",
+ "dependencies": {
+ "@vueuse/core": "^10.8.0",
+ "reka-ui": "^2.0.0",
+ "vue": "^3.4.5"
+ },
+ "peerDependencies": {
+ "reka-ui": "^2.0.0",
+ "vue": "^3.3.0"
+ }
+ },
+ "node_modules/vaul-vue/node_modules/@types/web-bluetooth": {
+ "version": "0.0.20",
+ "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
+ "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
+ "license": "MIT"
+ },
+ "node_modules/vaul-vue/node_modules/@vueuse/core": {
+ "version": "10.11.1",
+ "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz",
+ "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/web-bluetooth": "^0.0.20",
+ "@vueuse/metadata": "10.11.1",
+ "@vueuse/shared": "10.11.1",
+ "vue-demi": ">=0.14.8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/vaul-vue/node_modules/@vueuse/core/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vaul-vue/node_modules/@vueuse/metadata": {
+ "version": "10.11.1",
+ "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz",
+ "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/vaul-vue/node_modules/@vueuse/shared": {
+ "version": "10.11.1",
+ "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz",
+ "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
+ "license": "MIT",
+ "dependencies": {
+ "vue-demi": ">=0.14.8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/vaul-vue/node_modules/@vueuse/shared/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.3.5",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz",
+ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-express": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmmirror.com/vite-express/-/vite-express-0.21.1.tgz",
+ "integrity": "sha512-/dz1syfdKfWwcNRSl9wxZQmH7dImrvxNR9TptbpYGqrlawWFD+USzbLR1ytWei8XJpDPDRUgOoT8dEIf/vviyQ==",
+ "license": "MIT",
+ "dependencies": {
+ "express-static-gzip": "^2.2.0",
+ "picocolors": "^1.1.1"
+ }
+ },
+ "node_modules/vite/node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/vscode-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz",
+ "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vue": {
+ "version": "3.5.18",
+ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.18.tgz",
+ "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.18",
+ "@vue/compiler-sfc": "3.5.18",
+ "@vue/runtime-dom": "3.5.18",
+ "@vue/server-renderer": "3.5.18",
+ "@vue/shared": "3.5.18"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-router": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz",
+ "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
+ "node_modules/vue-router/node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
+ "node_modules/vue-sonner": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/vue-sonner/-/vue-sonner-2.0.2.tgz",
+ "integrity": "sha512-cN6onnN6aBOZ56YWN8CMj6t4zqbz/XlenE+jx5z+mgduMMelFo6B6G+e2Q6hhxU9YFiV16MXG2MBPluZabyElQ==",
+ "license": "MIT"
+ },
+ "node_modules/vue-tsc": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.12.tgz",
+ "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/typescript": "2.4.15",
+ "@vue/language-core": "2.2.12"
+ },
+ "bin": {
+ "vue-tsc": "bin/vue-tsc.js"
+ },
+ "peerDependencies": {
+ "typescript": ">=5.0.0"
+ }
+ },
+ "node_modules/vuex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/vuex/-/vuex-4.1.0.tgz",
+ "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.0.0-beta.11"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
+ "node_modules/vuex/node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.1.tgz",
+ "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
+ "license": "ISC",
+ "optional": true,
+ "peer": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ }
+ },
+ "node_modules/z-schema": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmmirror.com/z-schema/-/z-schema-5.0.5.tgz",
+ "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash.get": "^4.4.2",
+ "lodash.isequal": "^4.5.0",
+ "validator": "^13.7.0"
+ },
+ "bin": {
+ "z-schema": "bin/z-schema"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "commander": "^9.4.1"
+ }
+ },
+ "node_modules/z-schema/node_modules/commander": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "^12.20.0 || >=14"
+ }
+ },
+ "node_modules/zrender": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz",
+ "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tslib": "2.3.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..6241ad2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,78 @@
+{
+ "name": "space.tab",
+ "private": true,
+ "version": "2.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "nodemon -w src/server -x tsx src/server/main.ts",
+ "start": "cross-env NODE_ENV=production tsx src/server/main.ts",
+ "build": "vite build",
+ "build:extension": "node scripts/build-extension.js",
+ "docker": "docker-compose --env-file .env.production up -d --build"
+ },
+ "dependencies": {
+ "@barba/core": "^2.10.3",
+ "@supabase/supabase-js": "^2.53.0",
+ "@tailwindcss/vite": "^4.1.10",
+ "@tanstack/vue-table": "^8.21.3",
+ "@types/swagger-jsdoc": "^6.0.4",
+ "@types/swagger-ui-express": "^4.1.8",
+ "@vueuse/core": "^13.6.0",
+ "@waline/client": "^3.5.7",
+ "bcryptjs": "^3.0.2",
+ "blueimp-md5": "^2.19.0",
+ "canvas-confetti": "^1.9.3",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cors": "^2.8.5",
+ "cross-env": "^7.0.3",
+ "dayjs": "^1.11.13",
+ "dotenv": "^16.5.0",
+ "echarts": "^5.6.0",
+ "express": "^5.1.0",
+ "gsap": "^3.13.0",
+ "helmet": "^8.1.0",
+ "html2canvas": "^1.4.1",
+ "ioredis": "^5.6.1",
+ "js-cookie": "^3.0.5",
+ "jsonwebtoken": "^9.0.2",
+ "lodash-es": "^4.17.21",
+ "lucide-vue-next": "^0.523.0",
+ "moment-timezone": "^0.6.0",
+ "morgan": "^1.10.0",
+ "mysql2": "^3.14.1",
+ "pinia": "^3.0.3",
+ "reka-ui": "^2.4.1",
+ "swagger-jsdoc": "^6.2.8",
+ "swagger-ui-express": "^5.0.1",
+ "tailwind-merge": "^3.3.1",
+ "tailwindcss": "^4.1.10",
+ "tsx": "^4.19.3",
+ "tw-animate-css": "^1.3.4",
+ "typescript": "^5.8.3",
+ "uuid": "^11.1.0",
+ "vaul-vue": "^0.4.1",
+ "vite-express": "*",
+ "vue": "^3.5.13",
+ "vue-router": "^4.5.1",
+ "vue-sonner": "^2.0.2",
+ "vuex": "^4.1.0"
+ },
+ "devDependencies": {
+ "@types/cors": "^2.8.19",
+ "@types/express": "^5.0.1",
+ "@types/js-cookie": "^3.0.6",
+ "@types/jsonwebtoken": "^9.0.10",
+ "@types/morgan": "^1.9.10",
+ "@types/node": "^22.15.2",
+ "@types/uuid": "^10.0.0",
+ "@vitejs/plugin-vue": "^5.2.3",
+ "axios": "^1.11.0",
+ "canvas": "^3.1.2",
+ "cheerio": "^1.1.2",
+ "nodemon": "^3.1.10",
+ "vite": "^6.3.5",
+ "vue-tsc": "^2.2.10"
+ },
+ "packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..28421f2
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,4220 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@barba/core':
+ specifier: ^2.10.3
+ version: 2.10.3
+ '@supabase/supabase-js':
+ specifier: ^2.53.0
+ version: 2.53.0
+ '@tailwindcss/vite':
+ specifier: ^4.1.10
+ version: 4.1.11(vite@6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3))
+ '@tanstack/vue-table':
+ specifier: ^8.21.3
+ version: 8.21.3(vue@3.5.18(typescript@5.9.2))
+ '@types/swagger-jsdoc':
+ specifier: ^6.0.4
+ version: 6.0.4
+ '@types/swagger-ui-express':
+ specifier: ^4.1.8
+ version: 4.1.8
+ '@vueuse/core':
+ specifier: ^13.6.0
+ version: 13.6.0(vue@3.5.18(typescript@5.9.2))
+ '@waline/client':
+ specifier: ^3.5.7
+ version: 3.6.0(typescript@5.9.2)
+ bcryptjs:
+ specifier: ^3.0.2
+ version: 3.0.2
+ blueimp-md5:
+ specifier: ^2.19.0
+ version: 2.19.0
+ canvas-confetti:
+ specifier: ^1.9.3
+ version: 1.9.3
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ cors:
+ specifier: ^2.8.5
+ version: 2.8.5
+ cross-env:
+ specifier: ^7.0.3
+ version: 7.0.3
+ dayjs:
+ specifier: ^1.11.13
+ version: 1.11.13
+ dotenv:
+ specifier: ^16.5.0
+ version: 16.6.1
+ echarts:
+ specifier: ^5.6.0
+ version: 5.6.0
+ express:
+ specifier: ^5.1.0
+ version: 5.1.0
+ gsap:
+ specifier: ^3.13.0
+ version: 3.13.0
+ helmet:
+ specifier: ^8.1.0
+ version: 8.1.0
+ html2canvas:
+ specifier: ^1.4.1
+ version: 1.4.1
+ ioredis:
+ specifier: ^5.6.1
+ version: 5.7.0
+ js-cookie:
+ specifier: ^3.0.5
+ version: 3.0.5
+ jsonwebtoken:
+ specifier: ^9.0.2
+ version: 9.0.2
+ lodash-es:
+ specifier: ^4.17.21
+ version: 4.17.21
+ lucide-vue-next:
+ specifier: ^0.523.0
+ version: 0.523.0(vue@3.5.18(typescript@5.9.2))
+ moment-timezone:
+ specifier: ^0.6.0
+ version: 0.6.0
+ morgan:
+ specifier: ^1.10.0
+ version: 1.10.1
+ mysql2:
+ specifier: ^3.14.1
+ version: 3.14.3
+ pinia:
+ specifier: ^3.0.3
+ version: 3.0.3(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2))
+ reka-ui:
+ specifier: ^2.4.1
+ version: 2.4.1(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2))
+ swagger-jsdoc:
+ specifier: ^6.2.8
+ version: 6.2.8(openapi-types@12.1.3)
+ swagger-ui-express:
+ specifier: ^5.0.1
+ version: 5.0.1(express@5.1.0)
+ tailwind-merge:
+ specifier: ^3.3.1
+ version: 3.3.1
+ tailwindcss:
+ specifier: ^4.1.10
+ version: 4.1.11
+ tsx:
+ specifier: ^4.19.3
+ version: 4.20.3
+ tw-animate-css:
+ specifier: ^1.3.4
+ version: 1.3.6
+ typescript:
+ specifier: ^5.8.3
+ version: 5.9.2
+ uuid:
+ specifier: ^11.1.0
+ version: 11.1.0
+ vaul-vue:
+ specifier: ^0.4.1
+ version: 0.4.1(reka-ui@2.4.1(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2)))(vue@3.5.18(typescript@5.9.2))
+ vite-express:
+ specifier: '*'
+ version: 0.21.1
+ vue:
+ specifier: ^3.5.13
+ version: 3.5.18(typescript@5.9.2)
+ vue-router:
+ specifier: ^4.5.1
+ version: 4.5.1(vue@3.5.18(typescript@5.9.2))
+ vue-sonner:
+ specifier: ^2.0.2
+ version: 2.0.2
+ vuex:
+ specifier: ^4.1.0
+ version: 4.1.0(vue@3.5.18(typescript@5.9.2))
+ devDependencies:
+ '@types/cors':
+ specifier: ^2.8.19
+ version: 2.8.19
+ '@types/express':
+ specifier: ^5.0.1
+ version: 5.0.3
+ '@types/js-cookie':
+ specifier: ^3.0.6
+ version: 3.0.6
+ '@types/jsonwebtoken':
+ specifier: ^9.0.10
+ version: 9.0.10
+ '@types/morgan':
+ specifier: ^1.9.10
+ version: 1.9.10
+ '@types/node':
+ specifier: ^22.15.2
+ version: 22.17.0
+ '@types/uuid':
+ specifier: ^10.0.0
+ version: 10.0.0
+ '@vitejs/plugin-vue':
+ specifier: ^5.2.3
+ version: 5.2.4(vite@6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3))(vue@3.5.18(typescript@5.9.2))
+ axios:
+ specifier: ^1.11.0
+ version: 1.11.0
+ canvas:
+ specifier: ^3.1.2
+ version: 3.1.2
+ cheerio:
+ specifier: ^1.1.2
+ version: 1.1.2
+ nodemon:
+ specifier: ^3.1.10
+ version: 3.1.10
+ vite:
+ specifier: ^6.3.5
+ version: 6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)
+ vue-tsc:
+ specifier: ^2.2.10
+ version: 2.2.12(typescript@5.9.2)
+
+packages:
+
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@apidevtools/json-schema-ref-parser@9.1.2':
+ resolution: {integrity: sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==}
+
+ '@apidevtools/openapi-schemas@2.1.0':
+ resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==}
+ engines: {node: '>=10'}
+
+ '@apidevtools/swagger-methods@3.0.2':
+ resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
+
+ '@apidevtools/swagger-parser@10.0.3':
+ resolution: {integrity: sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==}
+ peerDependencies:
+ openapi-types: '>=7'
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.0':
+ resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/types@7.28.2':
+ resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@barba/core@2.10.3':
+ resolution: {integrity: sha512-ovVeT1wUcdMg8Lu6Iel6yEBW2+s6iA7qfRLCVKTSIfJU9csiawU0UOmVJdcoy3WNOsF2Ur9UfoTUC9aDcm4c0w==}
+
+ '@esbuild/aix-ppc64@0.25.8':
+ resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.8':
+ resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.8':
+ resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.8':
+ resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.8':
+ resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.8':
+ resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.8':
+ resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.8':
+ resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.8':
+ resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.8':
+ resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.8':
+ resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.8':
+ resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.8':
+ resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.8':
+ resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.8':
+ resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.8':
+ resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.8':
+ resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.8':
+ resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.8':
+ resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.8':
+ resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.8':
+ resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.8':
+ resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.8':
+ resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.8':
+ resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.8':
+ resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.8':
+ resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@floating-ui/core@1.7.3':
+ resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
+
+ '@floating-ui/dom@1.7.3':
+ resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==}
+
+ '@floating-ui/utils@0.2.10':
+ resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
+
+ '@floating-ui/vue@1.1.8':
+ resolution: {integrity: sha512-SNJAa1jbT8Gh1LvWw2uIIViLL0saV2bCY59ISCvJzhbut5DSb2H3LKUK49Xkd7SixTNHKX4LFu59nbwIXt9jjQ==}
+
+ '@internationalized/date@3.8.2':
+ resolution: {integrity: sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==}
+
+ '@internationalized/number@3.6.4':
+ resolution: {integrity: sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg==}
+
+ '@ioredis/commands@1.3.0':
+ resolution: {integrity: sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==}
+
+ '@isaacs/fs-minipass@4.0.1':
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+
+ '@jridgewell/gen-mapping@0.3.12':
+ resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.4':
+ resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
+
+ '@jridgewell/trace-mapping@0.3.29':
+ resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
+
+ '@jsdevtools/ono@7.1.3':
+ resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
+
+ '@rollup/rollup-android-arm-eabi@4.46.2':
+ resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.46.2':
+ resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.46.2':
+ resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.46.2':
+ resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.46.2':
+ resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.46.2':
+ resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
+ resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.46.2':
+ resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-arm64-gnu@4.46.2':
+ resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm64-musl@4.46.2':
+ resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
+ resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.46.2':
+ resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.46.2':
+ resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-musl@4.46.2':
+ resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-s390x-gnu@4.46.2':
+ resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-gnu@4.46.2':
+ resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-musl@4.46.2':
+ resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-win32-arm64-msvc@4.46.2':
+ resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.46.2':
+ resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.46.2':
+ resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==}
+ cpu: [x64]
+ os: [win32]
+
+ '@scarf/scarf@1.4.0':
+ resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==}
+
+ '@supabase/auth-js@2.71.1':
+ resolution: {integrity: sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==}
+
+ '@supabase/functions-js@2.4.5':
+ resolution: {integrity: sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==}
+
+ '@supabase/node-fetch@2.6.15':
+ resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==}
+ engines: {node: 4.x || >=6.0.0}
+
+ '@supabase/postgrest-js@1.19.4':
+ resolution: {integrity: sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==}
+
+ '@supabase/realtime-js@2.11.15':
+ resolution: {integrity: sha512-HQKRnwAqdVqJW/P9TjKVK+/ETpW4yQ8tyDPPtRMKOH4Uh3vQD74vmj353CYs8+YwVBKubeUOOEpI9CT8mT4obw==}
+
+ '@supabase/storage-js@2.10.4':
+ resolution: {integrity: sha512-cvL02GarJVFcNoWe36VBybQqTVRq6wQSOCvTS64C+eyuxOruFIm1utZAY0xi2qKtHJO3EjKaj8iWJKySusDmAQ==}
+
+ '@supabase/supabase-js@2.53.0':
+ resolution: {integrity: sha512-Vg9sl0oFn55cCPaEOsDsRDbxOVccxRrK/cikjL1XbywHEOfyA5SOOEypidMvQLwgoAfnC2S4D9BQwJDcZs7/TQ==}
+
+ '@swc/helpers@0.5.17':
+ resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==}
+
+ '@tailwindcss/node@4.1.11':
+ resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.11':
+ resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.11':
+ resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.11':
+ resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.11':
+ resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
+ resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
+ resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
+ resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
+ resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.11':
+ resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.11':
+ resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
+ resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
+ resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.11':
+ resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/vite@4.1.11':
+ resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7
+
+ '@tanstack/table-core@8.21.3':
+ resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
+ engines: {node: '>=12'}
+
+ '@tanstack/virtual-core@3.13.12':
+ resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==}
+
+ '@tanstack/vue-table@8.21.3':
+ resolution: {integrity: sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ vue: '>=3.2'
+
+ '@tanstack/vue-virtual@3.13.12':
+ resolution: {integrity: sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==}
+ peerDependencies:
+ vue: ^2.7.0 || ^3.0.0
+
+ '@types/body-parser@1.19.6':
+ resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==}
+
+ '@types/connect@3.4.38':
+ resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+
+ '@types/cors@2.8.19':
+ resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/express-serve-static-core@5.0.7':
+ resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==}
+
+ '@types/express@5.0.3':
+ resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==}
+
+ '@types/http-errors@2.0.5':
+ resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
+
+ '@types/js-cookie@3.0.6':
+ resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/jsonwebtoken@9.0.10':
+ resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==}
+
+ '@types/mime@1.3.5':
+ resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
+
+ '@types/morgan@1.9.10':
+ resolution: {integrity: sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==}
+
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
+ '@types/node@22.17.0':
+ resolution: {integrity: sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==}
+
+ '@types/phoenix@1.6.6':
+ resolution: {integrity: sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==}
+
+ '@types/qs@6.14.0':
+ resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==}
+
+ '@types/range-parser@1.2.7':
+ resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
+
+ '@types/send@0.17.5':
+ resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==}
+
+ '@types/serve-static@1.15.8':
+ resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==}
+
+ '@types/swagger-jsdoc@6.0.4':
+ resolution: {integrity: sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==}
+
+ '@types/swagger-ui-express@4.1.8':
+ resolution: {integrity: sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==}
+
+ '@types/uuid@10.0.0':
+ resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==}
+
+ '@types/web-bluetooth@0.0.20':
+ resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
+
+ '@types/web-bluetooth@0.0.21':
+ resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
+
+ '@types/ws@8.18.1':
+ resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
+
+ '@vitejs/plugin-vue@5.2.4':
+ resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0
+ vue: ^3.2.25
+
+ '@volar/language-core@2.4.15':
+ resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
+
+ '@volar/source-map@2.4.15':
+ resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
+
+ '@volar/typescript@2.4.15':
+ resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
+
+ '@vue/compiler-core@3.5.18':
+ resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==}
+
+ '@vue/compiler-dom@3.5.18':
+ resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==}
+
+ '@vue/compiler-sfc@3.5.18':
+ resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==}
+
+ '@vue/compiler-ssr@3.5.18':
+ resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==}
+
+ '@vue/compiler-vue2@2.7.16':
+ resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
+
+ '@vue/devtools-api@6.6.4':
+ resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+
+ '@vue/devtools-api@7.7.7':
+ resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
+
+ '@vue/devtools-kit@7.7.7':
+ resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
+
+ '@vue/devtools-shared@7.7.7':
+ resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
+
+ '@vue/language-core@2.2.12':
+ resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vue/reactivity@3.5.18':
+ resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==}
+
+ '@vue/runtime-core@3.5.18':
+ resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==}
+
+ '@vue/runtime-dom@3.5.18':
+ resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==}
+
+ '@vue/server-renderer@3.5.18':
+ resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==}
+ peerDependencies:
+ vue: 3.5.18
+
+ '@vue/shared@3.5.18':
+ resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==}
+
+ '@vueuse/core@10.11.1':
+ resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
+
+ '@vueuse/core@12.8.2':
+ resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
+
+ '@vueuse/core@13.6.0':
+ resolution: {integrity: sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@vueuse/metadata@10.11.1':
+ resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==}
+
+ '@vueuse/metadata@12.8.2':
+ resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==}
+
+ '@vueuse/metadata@13.6.0':
+ resolution: {integrity: sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ==}
+
+ '@vueuse/shared@10.11.1':
+ resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
+
+ '@vueuse/shared@12.8.2':
+ resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==}
+
+ '@vueuse/shared@13.6.0':
+ resolution: {integrity: sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==}
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@waline/api@1.0.0':
+ resolution: {integrity: sha512-o0lWjt+/oZH1/4q3DJxTf5kdkgNbSmoLRXIiGznW225A6hq9/9IkOO1DiAijIsxGYJS6psFC+58+IzkD1EerBA==}
+ engines: {node: '>=18'}
+
+ '@waline/client@3.6.0':
+ resolution: {integrity: sha512-GzRHOpyveRlJamS/QOQ6OItYzvU6px4wtb9enGiwtWcODHSgU+21GZd++czjLwwiyiEgMNOCe8Wqll4ianI38w==}
+ engines: {node: '>=18'}
+
+ accepts@2.0.0:
+ resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
+ engines: {node: '>= 0.6'}
+
+ alien-signals@1.0.13:
+ resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ autosize@6.0.1:
+ resolution: {integrity: sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==}
+
+ aws-ssl-profiles@1.1.2:
+ resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==}
+ engines: {node: '>= 6.0.0'}
+
+ axios@1.11.0:
+ resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base64-arraybuffer@1.0.2:
+ resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
+ engines: {node: '>= 0.6.0'}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ basic-auth@2.0.1:
+ resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==}
+ engines: {node: '>= 0.8'}
+
+ bcryptjs@3.0.2:
+ resolution: {integrity: sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==}
+ hasBin: true
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ birpc@2.5.0:
+ resolution: {integrity: sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==}
+
+ bl@4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
+ blueimp-md5@2.19.0:
+ resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==}
+
+ body-parser@2.2.0:
+ resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
+ engines: {node: '>=18'}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ brace-expansion@2.0.2:
+ resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ buffer-equal-constant-time@1.0.1:
+ resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
+
+ buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+ bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ call-me-maybe@1.0.2:
+ resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
+
+ canvas-confetti@1.9.3:
+ resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==}
+
+ canvas@3.1.2:
+ resolution: {integrity: sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==}
+ engines: {node: ^18.12.0 || >= 20.9.0}
+
+ cheerio-select@2.1.0:
+ resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
+
+ cheerio@1.1.2:
+ resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==}
+ engines: {node: '>=20.18.1'}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ chownr@1.1.4:
+ resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+
+ chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
+
+ class-variance-authority@0.7.1:
+ resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ cluster-key-slot@1.1.2:
+ resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
+ engines: {node: '>=0.10.0'}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ commander@6.2.0:
+ resolution: {integrity: sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==}
+ engines: {node: '>= 6'}
+
+ commander@9.5.0:
+ resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
+ engines: {node: ^12.20.0 || >=14}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ content-disposition@1.0.0:
+ resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==}
+ engines: {node: '>= 0.6'}
+
+ content-type@1.0.5:
+ resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+ engines: {node: '>= 0.6'}
+
+ cookie-signature@1.2.2:
+ resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
+ engines: {node: '>=6.6.0'}
+
+ cookie@0.7.2:
+ resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
+ engines: {node: '>= 0.6'}
+
+ copy-anything@3.0.5:
+ resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+ engines: {node: '>=12.13'}
+
+ cors@2.8.5:
+ resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+ engines: {node: '>= 0.10'}
+
+ cross-env@7.0.3:
+ resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
+ engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
+ hasBin: true
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css-line-break@2.1.0:
+ resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
+
+ css-select@5.2.2:
+ resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
+
+ css-what@6.2.2:
+ resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
+ engines: {node: '>= 6'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ dayjs@1.11.13:
+ resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
+ de-indent@1.0.2:
+ resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+
+ debug@2.6.9:
+ resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.4.1:
+ resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
+ defu@6.1.4:
+ resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ denque@2.1.0:
+ resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
+ engines: {node: '>=0.10'}
+
+ depd@2.0.0:
+ resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+ engines: {node: '>= 0.8'}
+
+ destroy@1.2.0:
+ resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+ engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+ detect-libc@2.0.4:
+ resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
+ engines: {node: '>=8'}
+
+ doctrine@3.0.0:
+ resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+ engines: {node: '>=6.0.0'}
+
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
+ domutils@3.2.2:
+ resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+
+ dotenv@16.6.1:
+ resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
+ engines: {node: '>=12'}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ ecdsa-sig-formatter@1.0.11:
+ resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
+
+ echarts@5.6.0:
+ resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==}
+
+ ee-first@1.1.1:
+ resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+ encodeurl@1.0.2:
+ resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+ engines: {node: '>= 0.8'}
+
+ encodeurl@2.0.0:
+ resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
+ engines: {node: '>= 0.8'}
+
+ encoding-sniffer@0.2.1:
+ resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==}
+
+ end-of-stream@1.4.5:
+ resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
+
+ enhanced-resolve@5.18.3:
+ resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
+ engines: {node: '>=10.13.0'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ esbuild@0.25.8:
+ resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ etag@1.8.1:
+ resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+ engines: {node: '>= 0.6'}
+
+ expand-template@2.0.3:
+ resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
+ engines: {node: '>=6'}
+
+ express-static-gzip@2.2.0:
+ resolution: {integrity: sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==}
+
+ express@5.1.0:
+ resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==}
+ engines: {node: '>= 18'}
+
+ fdir@6.4.6:
+ resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ finalhandler@2.1.0:
+ resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
+ engines: {node: '>= 0.8'}
+
+ follow-redirects@1.15.11:
+ resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ form-data@4.0.4:
+ resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
+ engines: {node: '>= 6'}
+
+ forwarded@0.2.0:
+ resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+ engines: {node: '>= 0.6'}
+
+ fresh@0.5.2:
+ resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+ engines: {node: '>= 0.6'}
+
+ fresh@2.0.0:
+ resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
+ engines: {node: '>= 0.8'}
+
+ fs-constants@1.0.0:
+ resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ generate-function@2.3.1:
+ resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.10.1:
+ resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
+
+ github-from-package@0.0.0:
+ resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob@7.1.6:
+ resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ gsap@3.13.0:
+ resolution: {integrity: sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ he@1.2.0:
+ resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+ hasBin: true
+
+ helmet@8.1.0:
+ resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==}
+ engines: {node: '>=18.0.0'}
+
+ hookable@5.5.3:
+ resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+
+ html2canvas@1.4.1:
+ resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
+ engines: {node: '>=8.0.0'}
+
+ htmlparser2@10.0.0:
+ resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
+
+ http-errors@2.0.0:
+ resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+ engines: {node: '>= 0.8'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ ignore-by-default@1.0.1:
+ resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ ioredis@5.7.0:
+ resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==}
+ engines: {node: '>=12.22.0'}
+
+ ipaddr.js@1.9.1:
+ resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+ engines: {node: '>= 0.10'}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-promise@4.0.0:
+ resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
+
+ is-property@1.0.2:
+ resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==}
+
+ is-what@4.1.16:
+ resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+ engines: {node: '>=12.13'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ isows@1.0.7:
+ resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==}
+ peerDependencies:
+ ws: '*'
+
+ jiti@2.5.1:
+ resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==}
+ hasBin: true
+
+ js-cookie@3.0.5:
+ resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+ engines: {node: '>=14'}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsonwebtoken@9.0.2:
+ resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
+ engines: {node: '>=12', npm: '>=6'}
+
+ jwa@1.4.2:
+ resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==}
+
+ jws@3.2.2:
+ resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
+
+ lightningcss-darwin-arm64@1.30.1:
+ resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.1:
+ resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.1:
+ resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.30.1:
+ resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.1:
+ resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+ engines: {node: '>= 12.0.0'}
+
+ lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+ lodash.defaults@4.2.0:
+ resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
+
+ lodash.get@4.4.2:
+ resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
+ deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
+
+ lodash.includes@4.3.0:
+ resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
+
+ lodash.isarguments@3.1.0:
+ resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
+
+ lodash.isboolean@3.0.3:
+ resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
+
+ lodash.isequal@4.5.0:
+ resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+ deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
+
+ lodash.isinteger@4.0.4:
+ resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
+
+ lodash.isnumber@3.0.3:
+ resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
+
+ lodash.isplainobject@4.0.6:
+ resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+
+ lodash.isstring@4.0.1:
+ resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
+
+ lodash.mergewith@4.6.2:
+ resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
+
+ lodash.once@4.1.1:
+ resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
+
+ long@5.3.2:
+ resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
+
+ lru-cache@7.18.3:
+ resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
+ engines: {node: '>=12'}
+
+ lru.min@1.1.2:
+ resolution: {integrity: sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==}
+ engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'}
+
+ lucide-vue-next@0.523.0:
+ resolution: {integrity: sha512-PLbjArdcFgUcJLtszpCzGyaGm4AUfZ/SzDKfPjhkoEpG6j5k6fS3AGbMEbwt/xGJdD9DuSEgypNT/eqSPLfHfw==}
+ peerDependencies:
+ vue: '>=3.0.1'
+
+ magic-string@0.30.17:
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+
+ marked-highlight@2.2.2:
+ resolution: {integrity: sha512-KlHOP31DatbtPPXPaI8nx1KTrG3EW0Z5zewCwpUj65swbtKOTStteK3sNAjBqV75Pgo3fNEVNHeptg18mDuWgw==}
+ peerDependencies:
+ marked: '>=4 <17'
+
+ marked@16.1.2:
+ resolution: {integrity: sha512-rNQt5EvRinalby7zJZu/mB+BvaAY2oz3wCuCjt1RDrWNpS1Pdf9xqMOeC9Hm5adBdcV/3XZPJpG58eT+WBc0XQ==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ media-typer@1.1.0:
+ resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
+ engines: {node: '>= 0.8'}
+
+ merge-descriptors@2.0.0:
+ resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
+ engines: {node: '>=18'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-db@1.54.0:
+ resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@3.0.1:
+ resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
+ engines: {node: '>= 0.6'}
+
+ mime@1.6.0:
+ resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@3.0.2:
+ resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==}
+ engines: {node: '>= 18'}
+
+ mitt@3.0.1:
+ resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+
+ mkdirp-classic@0.5.3:
+ resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+
+ mkdirp@3.0.1:
+ resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ moment-timezone@0.6.0:
+ resolution: {integrity: sha512-ldA5lRNm3iJCWZcBCab4pnNL3HSZYXVb/3TYr75/1WCTWYuTqYUb5f/S384pncYjJ88lbO8Z4uPDvmoluHJc8Q==}
+
+ moment@2.30.1:
+ resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
+
+ morgan@1.10.1:
+ resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==}
+ engines: {node: '>= 0.8.0'}
+
+ ms@2.0.0:
+ resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ muggle-string@0.4.1:
+ resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+
+ mysql2@3.14.3:
+ resolution: {integrity: sha512-fD6MLV8XJ1KiNFIF0bS7Msl8eZyhlTDCDl75ajU5SJtpdx9ZPEACulJcqJWr1Y8OYyxsFc4j3+nflpmhxCU5aQ==}
+ engines: {node: '>= 8.0'}
+
+ named-placeholders@1.1.3:
+ resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==}
+ engines: {node: '>=12.0.0'}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ napi-build-utils@2.0.0:
+ resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
+
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ node-abi@3.75.0:
+ resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==}
+ engines: {node: '>=10'}
+
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ nodemon@3.1.10:
+ resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ ohash@2.0.11:
+ resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
+
+ on-finished@2.3.0:
+ resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
+ engines: {node: '>= 0.8'}
+
+ on-finished@2.4.1:
+ resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+ engines: {node: '>= 0.8'}
+
+ on-headers@1.1.0:
+ resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==}
+ engines: {node: '>= 0.8'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ openapi-types@12.1.3:
+ resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
+
+ parse5-htmlparser2-tree-adapter@7.1.0:
+ resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==}
+
+ parse5-parser-stream@7.1.2:
+ resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==}
+
+ parse5@7.3.0:
+ resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
+
+ parseurl@1.3.3:
+ resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+ engines: {node: '>= 0.8'}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-to-regexp@6.3.0:
+ resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==}
+
+ path-to-regexp@8.2.0:
+ resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
+ engines: {node: '>=16'}
+
+ perfect-debounce@1.0.0:
+ resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ pinia@3.0.3:
+ resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==}
+ peerDependencies:
+ typescript: '>=4.4.4'
+ vue: ^2.7.0 || ^3.5.11
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prebuild-install@7.1.3:
+ resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ proxy-addr@2.0.7:
+ resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+ engines: {node: '>= 0.10'}
+
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ pstree.remy@1.1.8:
+ resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
+
+ pump@3.0.3:
+ resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
+
+ qs@6.14.0:
+ resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
+ engines: {node: '>=0.6'}
+
+ range-parser@1.2.1:
+ resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+ engines: {node: '>= 0.6'}
+
+ raw-body@3.0.0:
+ resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==}
+ engines: {node: '>= 0.8'}
+
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ recaptcha-v3@1.11.3:
+ resolution: {integrity: sha512-sEE6J0RzUkS+sKEBpgCD/AqCU0ffrAVOADGjvAx9vcttN+VLK42SWMkj/J/I6vHu3Kew+xcfbBqDVb65N0QGDw==}
+
+ redis-errors@1.2.0:
+ resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==}
+ engines: {node: '>=4'}
+
+ redis-parser@3.0.0:
+ resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
+ engines: {node: '>=4'}
+
+ reka-ui@2.4.1:
+ resolution: {integrity: sha512-NB7DrCsODN8MH02BWtgiExygfFcuuZ5/PTn6fMgjppmFHqePvNhmSn1LEuF35nel6PFbA4v+gdj0IoGN1yZ+vw==}
+ peerDependencies:
+ vue: '>= 3.2.0'
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ rollup@4.46.2:
+ resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ router@2.2.0:
+ resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
+ engines: {node: '>= 18'}
+
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ semver@7.7.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ send@0.19.0:
+ resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
+ engines: {node: '>= 0.8.0'}
+
+ send@1.2.0:
+ resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
+ engines: {node: '>= 18'}
+
+ seq-queue@0.0.5:
+ resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
+
+ serve-static@1.16.2:
+ resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
+ engines: {node: '>= 0.8.0'}
+
+ serve-static@2.2.0:
+ resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
+ engines: {node: '>= 18'}
+
+ setprototypeof@1.2.0:
+ resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ simple-concat@1.0.1:
+ resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+
+ simple-get@4.0.1:
+ resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+
+ simple-update-notifier@2.0.0:
+ resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
+ engines: {node: '>=10'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ speakingurl@14.0.1:
+ resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
+ engines: {node: '>=0.10.0'}
+
+ sqlstring@2.3.3:
+ resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
+ engines: {node: '>= 0.6'}
+
+ standard-as-callback@2.1.0:
+ resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
+
+ statuses@2.0.1:
+ resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+ engines: {node: '>= 0.8'}
+
+ statuses@2.0.2:
+ resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
+ engines: {node: '>= 0.8'}
+
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+ strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+
+ superjson@2.2.2:
+ resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
+ engines: {node: '>=16'}
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ swagger-jsdoc@6.2.8:
+ resolution: {integrity: sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==}
+ engines: {node: '>=12.0.0'}
+ hasBin: true
+
+ swagger-parser@10.0.3:
+ resolution: {integrity: sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==}
+ engines: {node: '>=10'}
+
+ swagger-ui-dist@5.27.1:
+ resolution: {integrity: sha512-oGtpYO3lnoaqyGtlJalvryl7TwzgRuxpOVWqEHx8af0YXI+Kt+4jMpLdgMtMcmWmuQ0QTCHLKExwrBFMSxvAUA==}
+
+ swagger-ui-express@5.0.1:
+ resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==}
+ engines: {node: '>= v0.10.32'}
+ peerDependencies:
+ express: '>=4.0.0 || >=5.0.0-beta'
+
+ tailwind-merge@3.3.1:
+ resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==}
+
+ tailwindcss@4.1.11:
+ resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==}
+
+ tapable@2.2.2:
+ resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==}
+ engines: {node: '>=6'}
+
+ tar-fs@2.1.3:
+ resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==}
+
+ tar-stream@2.2.0:
+ resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+ engines: {node: '>=6'}
+
+ tar@7.4.3:
+ resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
+ engines: {node: '>=18'}
+
+ text-segmentation@1.0.3:
+ resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
+
+ tinyglobby@0.2.14:
+ resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
+ engines: {node: '>=12.0.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ toidentifier@1.0.1:
+ resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+ engines: {node: '>=0.6'}
+
+ touch@3.1.1:
+ resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
+ hasBin: true
+
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+ tslib@2.3.0:
+ resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ tsx@4.20.3:
+ resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ tunnel-agent@0.6.0:
+ resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+
+ tw-animate-css@1.3.6:
+ resolution: {integrity: sha512-9dy0R9UsYEGmgf26L8UcHiLmSFTHa9+D7+dAt/G/sF5dCnPePZbfgDYinc7/UzAM7g/baVrmS6m9yEpU46d+LA==}
+
+ type-is@2.0.1:
+ resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
+ engines: {node: '>= 0.6'}
+
+ typescript@5.9.2:
+ resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undefsafe@2.0.5:
+ resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ undici@7.13.0:
+ resolution: {integrity: sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==}
+ engines: {node: '>=20.18.1'}
+
+ unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ utrie@1.0.2:
+ resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
+
+ uuid@11.1.0:
+ resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
+ hasBin: true
+
+ validator@13.15.15:
+ resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==}
+ engines: {node: '>= 0.10'}
+
+ vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+
+ vaul-vue@0.4.1:
+ resolution: {integrity: sha512-A6jOWOZX5yvyo1qMn7IveoWN91mJI5L3BUKsIwkg6qrTGgHs1Sb1JF/vyLJgnbN1rH4OOOxFbtqL9A46bOyGUQ==}
+ peerDependencies:
+ reka-ui: ^2.0.0
+ vue: ^3.3.0
+
+ vite-express@0.21.1:
+ resolution: {integrity: sha512-/dz1syfdKfWwcNRSl9wxZQmH7dImrvxNR9TptbpYGqrlawWFD+USzbLR1ytWei8XJpDPDRUgOoT8dEIf/vviyQ==}
+
+ vite@6.3.5:
+ resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vscode-uri@3.1.0:
+ resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+
+ vue-demi@0.14.10:
+ resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
+ engines: {node: '>=12'}
+ hasBin: true
+ peerDependencies:
+ '@vue/composition-api': ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ vue-router@4.5.1:
+ resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
+ peerDependencies:
+ vue: ^3.2.0
+
+ vue-sonner@2.0.2:
+ resolution: {integrity: sha512-cN6onnN6aBOZ56YWN8CMj6t4zqbz/XlenE+jx5z+mgduMMelFo6B6G+e2Q6hhxU9YFiV16MXG2MBPluZabyElQ==}
+
+ vue-tsc@2.2.12:
+ resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
+ vue@3.5.18:
+ resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ vuex@4.1.0:
+ resolution: {integrity: sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==}
+ peerDependencies:
+ vue: ^3.2.0
+
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+
+ yaml@2.0.0-1:
+ resolution: {integrity: sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==}
+ engines: {node: '>= 6'}
+
+ z-schema@5.0.5:
+ resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==}
+ engines: {node: '>=8.0.0'}
+ hasBin: true
+
+ zrender@5.6.1:
+ resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
+
+snapshots:
+
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.12
+ '@jridgewell/trace-mapping': 0.3.29
+
+ '@apidevtools/json-schema-ref-parser@9.1.2':
+ dependencies:
+ '@jsdevtools/ono': 7.1.3
+ '@types/json-schema': 7.0.15
+ call-me-maybe: 1.0.2
+ js-yaml: 4.1.0
+
+ '@apidevtools/openapi-schemas@2.1.0': {}
+
+ '@apidevtools/swagger-methods@3.0.2': {}
+
+ '@apidevtools/swagger-parser@10.0.3(openapi-types@12.1.3)':
+ dependencies:
+ '@apidevtools/json-schema-ref-parser': 9.1.2
+ '@apidevtools/openapi-schemas': 2.1.0
+ '@apidevtools/swagger-methods': 3.0.2
+ '@jsdevtools/ono': 7.1.3
+ call-me-maybe: 1.0.2
+ openapi-types: 12.1.3
+ z-schema: 5.0.5
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/parser@7.28.0':
+ dependencies:
+ '@babel/types': 7.28.2
+
+ '@babel/types@7.28.2':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@barba/core@2.10.3':
+ dependencies:
+ is-promise: 4.0.0
+ path-to-regexp: 6.3.0
+
+ '@esbuild/aix-ppc64@0.25.8':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/android-arm@0.25.8':
+ optional: true
+
+ '@esbuild/android-x64@0.25.8':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.8':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.8':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.8':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.8':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.8':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.8':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.8':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.8':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.8':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.8':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.8':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.8':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.8':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.8':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.8':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.8':
+ optional: true
+
+ '@floating-ui/core@1.7.3':
+ dependencies:
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/dom@1.7.3':
+ dependencies:
+ '@floating-ui/core': 1.7.3
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/utils@0.2.10': {}
+
+ '@floating-ui/vue@1.1.8(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ '@floating-ui/dom': 1.7.3
+ '@floating-ui/utils': 0.2.10
+ vue-demi: 0.14.10(vue@3.5.18(typescript@5.9.2))
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+
+ '@internationalized/date@3.8.2':
+ dependencies:
+ '@swc/helpers': 0.5.17
+
+ '@internationalized/number@3.6.4':
+ dependencies:
+ '@swc/helpers': 0.5.17
+
+ '@ioredis/commands@1.3.0': {}
+
+ '@isaacs/fs-minipass@4.0.1':
+ dependencies:
+ minipass: 7.1.2
+
+ '@jridgewell/gen-mapping@0.3.12':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.4
+ '@jridgewell/trace-mapping': 0.3.29
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.4': {}
+
+ '@jridgewell/trace-mapping@0.3.29':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.4
+
+ '@jsdevtools/ono@7.1.3': {}
+
+ '@rollup/rollup-android-arm-eabi@4.46.2':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.46.2':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.46.2':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.46.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.46.2':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-loongarch64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.46.2':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.46.2':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.46.2':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.46.2':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.46.2':
+ optional: true
+
+ '@scarf/scarf@1.4.0': {}
+
+ '@supabase/auth-js@2.71.1':
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+
+ '@supabase/functions-js@2.4.5':
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+
+ '@supabase/node-fetch@2.6.15':
+ dependencies:
+ whatwg-url: 5.0.0
+
+ '@supabase/postgrest-js@1.19.4':
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+
+ '@supabase/realtime-js@2.11.15':
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+ '@types/phoenix': 1.6.6
+ '@types/ws': 8.18.1
+ isows: 1.0.7(ws@8.18.3)
+ ws: 8.18.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ '@supabase/storage-js@2.10.4':
+ dependencies:
+ '@supabase/node-fetch': 2.6.15
+
+ '@supabase/supabase-js@2.53.0':
+ dependencies:
+ '@supabase/auth-js': 2.71.1
+ '@supabase/functions-js': 2.4.5
+ '@supabase/node-fetch': 2.6.15
+ '@supabase/postgrest-js': 1.19.4
+ '@supabase/realtime-js': 2.11.15
+ '@supabase/storage-js': 2.10.4
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
+ '@swc/helpers@0.5.17':
+ dependencies:
+ tslib: 2.8.1
+
+ '@tailwindcss/node@4.1.11':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ enhanced-resolve: 5.18.3
+ jiti: 2.5.1
+ lightningcss: 1.30.1
+ magic-string: 0.30.17
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.11
+
+ '@tailwindcss/oxide-android-arm64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.11':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.11':
+ dependencies:
+ detect-libc: 2.0.4
+ tar: 7.4.3
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.11
+ '@tailwindcss/oxide-darwin-arm64': 4.1.11
+ '@tailwindcss/oxide-darwin-x64': 4.1.11
+ '@tailwindcss/oxide-freebsd-x64': 4.1.11
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.11
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.11
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.11
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.11
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.11
+
+ '@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3))':
+ dependencies:
+ '@tailwindcss/node': 4.1.11
+ '@tailwindcss/oxide': 4.1.11
+ tailwindcss: 4.1.11
+ vite: 6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)
+
+ '@tanstack/table-core@8.21.3': {}
+
+ '@tanstack/virtual-core@3.13.12': {}
+
+ '@tanstack/vue-table@8.21.3(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ '@tanstack/table-core': 8.21.3
+ vue: 3.5.18(typescript@5.9.2)
+
+ '@tanstack/vue-virtual@3.13.12(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ '@tanstack/virtual-core': 3.13.12
+ vue: 3.5.18(typescript@5.9.2)
+
+ '@types/body-parser@1.19.6':
+ dependencies:
+ '@types/connect': 3.4.38
+ '@types/node': 22.17.0
+
+ '@types/connect@3.4.38':
+ dependencies:
+ '@types/node': 22.17.0
+
+ '@types/cors@2.8.19':
+ dependencies:
+ '@types/node': 22.17.0
+
+ '@types/estree@1.0.8': {}
+
+ '@types/express-serve-static-core@5.0.7':
+ dependencies:
+ '@types/node': 22.17.0
+ '@types/qs': 6.14.0
+ '@types/range-parser': 1.2.7
+ '@types/send': 0.17.5
+
+ '@types/express@5.0.3':
+ dependencies:
+ '@types/body-parser': 1.19.6
+ '@types/express-serve-static-core': 5.0.7
+ '@types/serve-static': 1.15.8
+
+ '@types/http-errors@2.0.5': {}
+
+ '@types/js-cookie@3.0.6': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/jsonwebtoken@9.0.10':
+ dependencies:
+ '@types/ms': 2.1.0
+ '@types/node': 22.17.0
+
+ '@types/mime@1.3.5': {}
+
+ '@types/morgan@1.9.10':
+ dependencies:
+ '@types/node': 22.17.0
+
+ '@types/ms@2.1.0': {}
+
+ '@types/node@22.17.0':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/phoenix@1.6.6': {}
+
+ '@types/qs@6.14.0': {}
+
+ '@types/range-parser@1.2.7': {}
+
+ '@types/send@0.17.5':
+ dependencies:
+ '@types/mime': 1.3.5
+ '@types/node': 22.17.0
+
+ '@types/serve-static@1.15.8':
+ dependencies:
+ '@types/http-errors': 2.0.5
+ '@types/node': 22.17.0
+ '@types/send': 0.17.5
+
+ '@types/swagger-jsdoc@6.0.4': {}
+
+ '@types/swagger-ui-express@4.1.8':
+ dependencies:
+ '@types/express': 5.0.3
+ '@types/serve-static': 1.15.8
+
+ '@types/uuid@10.0.0': {}
+
+ '@types/web-bluetooth@0.0.20': {}
+
+ '@types/web-bluetooth@0.0.21': {}
+
+ '@types/ws@8.18.1':
+ dependencies:
+ '@types/node': 22.17.0
+
+ '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3))(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ vite: 6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3)
+ vue: 3.5.18(typescript@5.9.2)
+
+ '@volar/language-core@2.4.15':
+ dependencies:
+ '@volar/source-map': 2.4.15
+
+ '@volar/source-map@2.4.15': {}
+
+ '@volar/typescript@2.4.15':
+ dependencies:
+ '@volar/language-core': 2.4.15
+ path-browserify: 1.0.1
+ vscode-uri: 3.1.0
+
+ '@vue/compiler-core@3.5.18':
+ dependencies:
+ '@babel/parser': 7.28.0
+ '@vue/shared': 3.5.18
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.18':
+ dependencies:
+ '@vue/compiler-core': 3.5.18
+ '@vue/shared': 3.5.18
+
+ '@vue/compiler-sfc@3.5.18':
+ dependencies:
+ '@babel/parser': 7.28.0
+ '@vue/compiler-core': 3.5.18
+ '@vue/compiler-dom': 3.5.18
+ '@vue/compiler-ssr': 3.5.18
+ '@vue/shared': 3.5.18
+ estree-walker: 2.0.2
+ magic-string: 0.30.17
+ postcss: 8.5.6
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.18':
+ dependencies:
+ '@vue/compiler-dom': 3.5.18
+ '@vue/shared': 3.5.18
+
+ '@vue/compiler-vue2@2.7.16':
+ dependencies:
+ de-indent: 1.0.2
+ he: 1.2.0
+
+ '@vue/devtools-api@6.6.4': {}
+
+ '@vue/devtools-api@7.7.7':
+ dependencies:
+ '@vue/devtools-kit': 7.7.7
+
+ '@vue/devtools-kit@7.7.7':
+ dependencies:
+ '@vue/devtools-shared': 7.7.7
+ birpc: 2.5.0
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 1.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.2
+
+ '@vue/devtools-shared@7.7.7':
+ dependencies:
+ rfdc: 1.4.1
+
+ '@vue/language-core@2.2.12(typescript@5.9.2)':
+ dependencies:
+ '@volar/language-core': 2.4.15
+ '@vue/compiler-dom': 3.5.18
+ '@vue/compiler-vue2': 2.7.16
+ '@vue/shared': 3.5.18
+ alien-signals: 1.0.13
+ minimatch: 9.0.5
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ optionalDependencies:
+ typescript: 5.9.2
+
+ '@vue/reactivity@3.5.18':
+ dependencies:
+ '@vue/shared': 3.5.18
+
+ '@vue/runtime-core@3.5.18':
+ dependencies:
+ '@vue/reactivity': 3.5.18
+ '@vue/shared': 3.5.18
+
+ '@vue/runtime-dom@3.5.18':
+ dependencies:
+ '@vue/reactivity': 3.5.18
+ '@vue/runtime-core': 3.5.18
+ '@vue/shared': 3.5.18
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.5.18(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.18
+ '@vue/shared': 3.5.18
+ vue: 3.5.18(typescript@5.9.2)
+
+ '@vue/shared@3.5.18': {}
+
+ '@vueuse/core@10.11.1(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ '@types/web-bluetooth': 0.0.20
+ '@vueuse/metadata': 10.11.1
+ '@vueuse/shared': 10.11.1(vue@3.5.18(typescript@5.9.2))
+ vue-demi: 0.14.10(vue@3.5.18(typescript@5.9.2))
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+
+ '@vueuse/core@12.8.2(typescript@5.9.2)':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 12.8.2
+ '@vueuse/shared': 12.8.2(typescript@5.9.2)
+ vue: 3.5.18(typescript@5.9.2)
+ transitivePeerDependencies:
+ - typescript
+
+ '@vueuse/core@13.6.0(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 13.6.0
+ '@vueuse/shared': 13.6.0(vue@3.5.18(typescript@5.9.2))
+ vue: 3.5.18(typescript@5.9.2)
+
+ '@vueuse/metadata@10.11.1': {}
+
+ '@vueuse/metadata@12.8.2': {}
+
+ '@vueuse/metadata@13.6.0': {}
+
+ '@vueuse/shared@10.11.1(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ vue-demi: 0.14.10(vue@3.5.18(typescript@5.9.2))
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+
+ '@vueuse/shared@12.8.2(typescript@5.9.2)':
+ dependencies:
+ vue: 3.5.18(typescript@5.9.2)
+ transitivePeerDependencies:
+ - typescript
+
+ '@vueuse/shared@13.6.0(vue@3.5.18(typescript@5.9.2))':
+ dependencies:
+ vue: 3.5.18(typescript@5.9.2)
+
+ '@waline/api@1.0.0': {}
+
+ '@waline/client@3.6.0(typescript@5.9.2)':
+ dependencies:
+ '@vueuse/core': 13.6.0(vue@3.5.18(typescript@5.9.2))
+ '@waline/api': 1.0.0
+ autosize: 6.0.1
+ marked: 16.1.2
+ marked-highlight: 2.2.2(marked@16.1.2)
+ recaptcha-v3: 1.11.3
+ vue: 3.5.18(typescript@5.9.2)
+ transitivePeerDependencies:
+ - typescript
+
+ accepts@2.0.0:
+ dependencies:
+ mime-types: 3.0.1
+ negotiator: 1.0.0
+
+ alien-signals@1.0.13: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ argparse@2.0.1: {}
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ asynckit@0.4.0: {}
+
+ autosize@6.0.1: {}
+
+ aws-ssl-profiles@1.1.2: {}
+
+ axios@1.11.0:
+ dependencies:
+ follow-redirects: 1.15.11
+ form-data: 4.0.4
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ balanced-match@1.0.2: {}
+
+ base64-arraybuffer@1.0.2: {}
+
+ base64-js@1.5.1: {}
+
+ basic-auth@2.0.1:
+ dependencies:
+ safe-buffer: 5.1.2
+
+ bcryptjs@3.0.2: {}
+
+ binary-extensions@2.3.0: {}
+
+ birpc@2.5.0: {}
+
+ bl@4.1.0:
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+
+ blueimp-md5@2.19.0: {}
+
+ body-parser@2.2.0:
+ dependencies:
+ bytes: 3.1.2
+ content-type: 1.0.5
+ debug: 4.4.1(supports-color@5.5.0)
+ http-errors: 2.0.0
+ iconv-lite: 0.6.3
+ on-finished: 2.4.1
+ qs: 6.14.0
+ raw-body: 3.0.0
+ type-is: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ buffer-equal-constant-time@1.0.1: {}
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ bytes@3.1.2: {}
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ call-me-maybe@1.0.2: {}
+
+ canvas-confetti@1.9.3: {}
+
+ canvas@3.1.2:
+ dependencies:
+ node-addon-api: 7.1.1
+ prebuild-install: 7.1.3
+
+ cheerio-select@2.1.0:
+ dependencies:
+ boolbase: 1.0.0
+ css-select: 5.2.2
+ css-what: 6.2.2
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+
+ cheerio@1.1.2:
+ dependencies:
+ cheerio-select: 2.1.0
+ dom-serializer: 2.0.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ encoding-sniffer: 0.2.1
+ htmlparser2: 10.0.0
+ parse5: 7.3.0
+ parse5-htmlparser2-tree-adapter: 7.1.0
+ parse5-parser-stream: 7.1.2
+ undici: 7.13.0
+ whatwg-mimetype: 4.0.0
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chownr@1.1.4: {}
+
+ chownr@3.0.0: {}
+
+ class-variance-authority@0.7.1:
+ dependencies:
+ clsx: 2.1.1
+
+ clsx@2.1.1: {}
+
+ cluster-key-slot@1.1.2: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ commander@6.2.0: {}
+
+ commander@9.5.0:
+ optional: true
+
+ concat-map@0.0.1: {}
+
+ content-disposition@1.0.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ content-type@1.0.5: {}
+
+ cookie-signature@1.2.2: {}
+
+ cookie@0.7.2: {}
+
+ copy-anything@3.0.5:
+ dependencies:
+ is-what: 4.1.16
+
+ cors@2.8.5:
+ dependencies:
+ object-assign: 4.1.1
+ vary: 1.1.2
+
+ cross-env@7.0.3:
+ dependencies:
+ cross-spawn: 7.0.6
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css-line-break@2.1.0:
+ dependencies:
+ utrie: 1.0.2
+
+ css-select@5.2.2:
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 6.2.2
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ nth-check: 2.1.1
+
+ css-what@6.2.2: {}
+
+ csstype@3.1.3: {}
+
+ dayjs@1.11.13: {}
+
+ de-indent@1.0.2: {}
+
+ debug@2.6.9:
+ dependencies:
+ ms: 2.0.0
+
+ debug@4.4.1(supports-color@5.5.0):
+ dependencies:
+ ms: 2.1.3
+ optionalDependencies:
+ supports-color: 5.5.0
+
+ decompress-response@6.0.0:
+ dependencies:
+ mimic-response: 3.1.0
+
+ deep-extend@0.6.0: {}
+
+ defu@6.1.4: {}
+
+ delayed-stream@1.0.0: {}
+
+ denque@2.1.0: {}
+
+ depd@2.0.0: {}
+
+ destroy@1.2.0: {}
+
+ detect-libc@2.0.4: {}
+
+ doctrine@3.0.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ domutils@3.2.2:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dotenv@16.6.1: {}
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ ecdsa-sig-formatter@1.0.11:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ echarts@5.6.0:
+ dependencies:
+ tslib: 2.3.0
+ zrender: 5.6.1
+
+ ee-first@1.1.1: {}
+
+ encodeurl@1.0.2: {}
+
+ encodeurl@2.0.0: {}
+
+ encoding-sniffer@0.2.1:
+ dependencies:
+ iconv-lite: 0.6.3
+ whatwg-encoding: 3.1.1
+
+ end-of-stream@1.4.5:
+ dependencies:
+ once: 1.4.0
+
+ enhanced-resolve@5.18.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.2
+
+ entities@4.5.0: {}
+
+ entities@6.0.1: {}
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ esbuild@0.25.8:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.8
+ '@esbuild/android-arm': 0.25.8
+ '@esbuild/android-arm64': 0.25.8
+ '@esbuild/android-x64': 0.25.8
+ '@esbuild/darwin-arm64': 0.25.8
+ '@esbuild/darwin-x64': 0.25.8
+ '@esbuild/freebsd-arm64': 0.25.8
+ '@esbuild/freebsd-x64': 0.25.8
+ '@esbuild/linux-arm': 0.25.8
+ '@esbuild/linux-arm64': 0.25.8
+ '@esbuild/linux-ia32': 0.25.8
+ '@esbuild/linux-loong64': 0.25.8
+ '@esbuild/linux-mips64el': 0.25.8
+ '@esbuild/linux-ppc64': 0.25.8
+ '@esbuild/linux-riscv64': 0.25.8
+ '@esbuild/linux-s390x': 0.25.8
+ '@esbuild/linux-x64': 0.25.8
+ '@esbuild/netbsd-arm64': 0.25.8
+ '@esbuild/netbsd-x64': 0.25.8
+ '@esbuild/openbsd-arm64': 0.25.8
+ '@esbuild/openbsd-x64': 0.25.8
+ '@esbuild/openharmony-arm64': 0.25.8
+ '@esbuild/sunos-x64': 0.25.8
+ '@esbuild/win32-arm64': 0.25.8
+ '@esbuild/win32-ia32': 0.25.8
+ '@esbuild/win32-x64': 0.25.8
+
+ escape-html@1.0.3: {}
+
+ estree-walker@2.0.2: {}
+
+ esutils@2.0.3: {}
+
+ etag@1.8.1: {}
+
+ expand-template@2.0.3: {}
+
+ express-static-gzip@2.2.0:
+ dependencies:
+ parseurl: 1.3.3
+ serve-static: 1.16.2
+ transitivePeerDependencies:
+ - supports-color
+
+ express@5.1.0:
+ dependencies:
+ accepts: 2.0.0
+ body-parser: 2.2.0
+ content-disposition: 1.0.0
+ content-type: 1.0.5
+ cookie: 0.7.2
+ cookie-signature: 1.2.2
+ debug: 4.4.1(supports-color@5.5.0)
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ finalhandler: 2.1.0
+ fresh: 2.0.0
+ http-errors: 2.0.0
+ merge-descriptors: 2.0.0
+ mime-types: 3.0.1
+ on-finished: 2.4.1
+ once: 1.4.0
+ parseurl: 1.3.3
+ proxy-addr: 2.0.7
+ qs: 6.14.0
+ range-parser: 1.2.1
+ router: 2.2.0
+ send: 1.2.0
+ serve-static: 2.2.0
+ statuses: 2.0.2
+ type-is: 2.0.1
+ vary: 1.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ fdir@6.4.6(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ finalhandler@2.1.0:
+ dependencies:
+ debug: 4.4.1(supports-color@5.5.0)
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ on-finished: 2.4.1
+ parseurl: 1.3.3
+ statuses: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ follow-redirects@1.15.11: {}
+
+ form-data@4.0.4:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+
+ forwarded@0.2.0: {}
+
+ fresh@0.5.2: {}
+
+ fresh@2.0.0: {}
+
+ fs-constants@1.0.0: {}
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ generate-function@2.3.1:
+ dependencies:
+ is-property: 1.0.2
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-tsconfig@4.10.1:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ github-from-package@0.0.0: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@7.1.6:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ gsap@3.13.0: {}
+
+ has-flag@3.0.0: {}
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ he@1.2.0: {}
+
+ helmet@8.1.0: {}
+
+ hookable@5.5.3: {}
+
+ html2canvas@1.4.1:
+ dependencies:
+ css-line-break: 2.1.0
+ text-segmentation: 1.0.3
+
+ htmlparser2@10.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ entities: 6.0.1
+
+ http-errors@2.0.0:
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.1
+ toidentifier: 1.0.1
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ieee754@1.2.1: {}
+
+ ignore-by-default@1.0.1: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ ioredis@5.7.0:
+ dependencies:
+ '@ioredis/commands': 1.3.0
+ cluster-key-slot: 1.1.2
+ debug: 4.4.1(supports-color@5.5.0)
+ denque: 2.1.0
+ lodash.defaults: 4.2.0
+ lodash.isarguments: 3.1.0
+ redis-errors: 1.2.0
+ redis-parser: 3.0.0
+ standard-as-callback: 2.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ ipaddr.js@1.9.1: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-extglob@2.1.1: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ is-promise@4.0.0: {}
+
+ is-property@1.0.2: {}
+
+ is-what@4.1.16: {}
+
+ isexe@2.0.0: {}
+
+ isows@1.0.7(ws@8.18.3):
+ dependencies:
+ ws: 8.18.3
+
+ jiti@2.5.1: {}
+
+ js-cookie@3.0.5: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsonwebtoken@9.0.2:
+ dependencies:
+ jws: 3.2.2
+ lodash.includes: 4.3.0
+ lodash.isboolean: 3.0.3
+ lodash.isinteger: 4.0.4
+ lodash.isnumber: 3.0.3
+ lodash.isplainobject: 4.0.6
+ lodash.isstring: 4.0.1
+ lodash.once: 4.1.1
+ ms: 2.1.3
+ semver: 7.7.2
+
+ jwa@1.4.2:
+ dependencies:
+ buffer-equal-constant-time: 1.0.1
+ ecdsa-sig-formatter: 1.0.11
+ safe-buffer: 5.2.1
+
+ jws@3.2.2:
+ dependencies:
+ jwa: 1.4.2
+ safe-buffer: 5.2.1
+
+ lightningcss-darwin-arm64@1.30.1:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.1:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.1:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ optional: true
+
+ lightningcss@1.30.1:
+ dependencies:
+ detect-libc: 2.0.4
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.30.1
+ lightningcss-darwin-x64: 1.30.1
+ lightningcss-freebsd-x64: 1.30.1
+ lightningcss-linux-arm-gnueabihf: 1.30.1
+ lightningcss-linux-arm64-gnu: 1.30.1
+ lightningcss-linux-arm64-musl: 1.30.1
+ lightningcss-linux-x64-gnu: 1.30.1
+ lightningcss-linux-x64-musl: 1.30.1
+ lightningcss-win32-arm64-msvc: 1.30.1
+ lightningcss-win32-x64-msvc: 1.30.1
+
+ lodash-es@4.17.21: {}
+
+ lodash.defaults@4.2.0: {}
+
+ lodash.get@4.4.2: {}
+
+ lodash.includes@4.3.0: {}
+
+ lodash.isarguments@3.1.0: {}
+
+ lodash.isboolean@3.0.3: {}
+
+ lodash.isequal@4.5.0: {}
+
+ lodash.isinteger@4.0.4: {}
+
+ lodash.isnumber@3.0.3: {}
+
+ lodash.isplainobject@4.0.6: {}
+
+ lodash.isstring@4.0.1: {}
+
+ lodash.mergewith@4.6.2: {}
+
+ lodash.once@4.1.1: {}
+
+ long@5.3.2: {}
+
+ lru-cache@7.18.3: {}
+
+ lru.min@1.1.2: {}
+
+ lucide-vue-next@0.523.0(vue@3.5.18(typescript@5.9.2)):
+ dependencies:
+ vue: 3.5.18(typescript@5.9.2)
+
+ magic-string@0.30.17:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.4
+
+ marked-highlight@2.2.2(marked@16.1.2):
+ dependencies:
+ marked: 16.1.2
+
+ marked@16.1.2: {}
+
+ math-intrinsics@1.1.0: {}
+
+ media-typer@1.1.0: {}
+
+ merge-descriptors@2.0.0: {}
+
+ mime-db@1.52.0: {}
+
+ mime-db@1.54.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mime-types@3.0.1:
+ dependencies:
+ mime-db: 1.54.0
+
+ mime@1.6.0: {}
+
+ mimic-response@3.1.0: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimist@1.2.8: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@3.0.2:
+ dependencies:
+ minipass: 7.1.2
+
+ mitt@3.0.1: {}
+
+ mkdirp-classic@0.5.3: {}
+
+ mkdirp@3.0.1: {}
+
+ moment-timezone@0.6.0:
+ dependencies:
+ moment: 2.30.1
+
+ moment@2.30.1: {}
+
+ morgan@1.10.1:
+ dependencies:
+ basic-auth: 2.0.1
+ debug: 2.6.9
+ depd: 2.0.0
+ on-finished: 2.3.0
+ on-headers: 1.1.0
+ transitivePeerDependencies:
+ - supports-color
+
+ ms@2.0.0: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ mysql2@3.14.3:
+ dependencies:
+ aws-ssl-profiles: 1.1.2
+ denque: 2.1.0
+ generate-function: 2.3.1
+ iconv-lite: 0.6.3
+ long: 5.3.2
+ lru.min: 1.1.2
+ named-placeholders: 1.1.3
+ seq-queue: 0.0.5
+ sqlstring: 2.3.3
+
+ named-placeholders@1.1.3:
+ dependencies:
+ lru-cache: 7.18.3
+
+ nanoid@3.3.11: {}
+
+ napi-build-utils@2.0.0: {}
+
+ negotiator@1.0.0: {}
+
+ node-abi@3.75.0:
+ dependencies:
+ semver: 7.7.2
+
+ node-addon-api@7.1.1: {}
+
+ nodemon@3.1.10:
+ dependencies:
+ chokidar: 3.6.0
+ debug: 4.4.1(supports-color@5.5.0)
+ ignore-by-default: 1.0.1
+ minimatch: 3.1.2
+ pstree.remy: 1.1.8
+ semver: 7.7.2
+ simple-update-notifier: 2.0.0
+ supports-color: 5.5.0
+ touch: 3.1.1
+ undefsafe: 2.0.5
+
+ normalize-path@3.0.0: {}
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ object-assign@4.1.1: {}
+
+ object-inspect@1.13.4: {}
+
+ ohash@2.0.11: {}
+
+ on-finished@2.3.0:
+ dependencies:
+ ee-first: 1.1.1
+
+ on-finished@2.4.1:
+ dependencies:
+ ee-first: 1.1.1
+
+ on-headers@1.1.0: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ openapi-types@12.1.3: {}
+
+ parse5-htmlparser2-tree-adapter@7.1.0:
+ dependencies:
+ domhandler: 5.0.3
+ parse5: 7.3.0
+
+ parse5-parser-stream@7.1.2:
+ dependencies:
+ parse5: 7.3.0
+
+ parse5@7.3.0:
+ dependencies:
+ entities: 6.0.1
+
+ parseurl@1.3.3: {}
+
+ path-browserify@1.0.1: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-to-regexp@6.3.0: {}
+
+ path-to-regexp@8.2.0: {}
+
+ perfect-debounce@1.0.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ pinia@3.0.3(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2)):
+ dependencies:
+ '@vue/devtools-api': 7.7.7
+ vue: 3.5.18(typescript@5.9.2)
+ optionalDependencies:
+ typescript: 5.9.2
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prebuild-install@7.1.3:
+ dependencies:
+ detect-libc: 2.0.4
+ expand-template: 2.0.3
+ github-from-package: 0.0.0
+ minimist: 1.2.8
+ mkdirp-classic: 0.5.3
+ napi-build-utils: 2.0.0
+ node-abi: 3.75.0
+ pump: 3.0.3
+ rc: 1.2.8
+ simple-get: 4.0.1
+ tar-fs: 2.1.3
+ tunnel-agent: 0.6.0
+
+ proxy-addr@2.0.7:
+ dependencies:
+ forwarded: 0.2.0
+ ipaddr.js: 1.9.1
+
+ proxy-from-env@1.1.0: {}
+
+ pstree.remy@1.1.8: {}
+
+ pump@3.0.3:
+ dependencies:
+ end-of-stream: 1.4.5
+ once: 1.4.0
+
+ qs@6.14.0:
+ dependencies:
+ side-channel: 1.1.0
+
+ range-parser@1.2.1: {}
+
+ raw-body@3.0.0:
+ dependencies:
+ bytes: 3.1.2
+ http-errors: 2.0.0
+ iconv-lite: 0.6.3
+ unpipe: 1.0.0
+
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ recaptcha-v3@1.11.3: {}
+
+ redis-errors@1.2.0: {}
+
+ redis-parser@3.0.0:
+ dependencies:
+ redis-errors: 1.2.0
+
+ reka-ui@2.4.1(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2)):
+ dependencies:
+ '@floating-ui/dom': 1.7.3
+ '@floating-ui/vue': 1.1.8(vue@3.5.18(typescript@5.9.2))
+ '@internationalized/date': 3.8.2
+ '@internationalized/number': 3.6.4
+ '@tanstack/vue-virtual': 3.13.12(vue@3.5.18(typescript@5.9.2))
+ '@vueuse/core': 12.8.2(typescript@5.9.2)
+ '@vueuse/shared': 12.8.2(typescript@5.9.2)
+ aria-hidden: 1.2.6
+ defu: 6.1.4
+ ohash: 2.0.11
+ vue: 3.5.18(typescript@5.9.2)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - typescript
+
+ resolve-pkg-maps@1.0.0: {}
+
+ rfdc@1.4.1: {}
+
+ rollup@4.46.2:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.46.2
+ '@rollup/rollup-android-arm64': 4.46.2
+ '@rollup/rollup-darwin-arm64': 4.46.2
+ '@rollup/rollup-darwin-x64': 4.46.2
+ '@rollup/rollup-freebsd-arm64': 4.46.2
+ '@rollup/rollup-freebsd-x64': 4.46.2
+ '@rollup/rollup-linux-arm-gnueabihf': 4.46.2
+ '@rollup/rollup-linux-arm-musleabihf': 4.46.2
+ '@rollup/rollup-linux-arm64-gnu': 4.46.2
+ '@rollup/rollup-linux-arm64-musl': 4.46.2
+ '@rollup/rollup-linux-loongarch64-gnu': 4.46.2
+ '@rollup/rollup-linux-ppc64-gnu': 4.46.2
+ '@rollup/rollup-linux-riscv64-gnu': 4.46.2
+ '@rollup/rollup-linux-riscv64-musl': 4.46.2
+ '@rollup/rollup-linux-s390x-gnu': 4.46.2
+ '@rollup/rollup-linux-x64-gnu': 4.46.2
+ '@rollup/rollup-linux-x64-musl': 4.46.2
+ '@rollup/rollup-win32-arm64-msvc': 4.46.2
+ '@rollup/rollup-win32-ia32-msvc': 4.46.2
+ '@rollup/rollup-win32-x64-msvc': 4.46.2
+ fsevents: 2.3.3
+
+ router@2.2.0:
+ dependencies:
+ debug: 4.4.1(supports-color@5.5.0)
+ depd: 2.0.0
+ is-promise: 4.0.0
+ parseurl: 1.3.3
+ path-to-regexp: 8.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ safe-buffer@5.1.2: {}
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ semver@7.7.2: {}
+
+ send@0.19.0:
+ dependencies:
+ debug: 2.6.9
+ depd: 2.0.0
+ destroy: 1.2.0
+ encodeurl: 1.0.2
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 0.5.2
+ http-errors: 2.0.0
+ mime: 1.6.0
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ send@1.2.0:
+ dependencies:
+ debug: 4.4.1(supports-color@5.5.0)
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 2.0.0
+ http-errors: 2.0.0
+ mime-types: 3.0.1
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ seq-queue@0.0.5: {}
+
+ serve-static@1.16.2:
+ dependencies:
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ parseurl: 1.3.3
+ send: 0.19.0
+ transitivePeerDependencies:
+ - supports-color
+
+ serve-static@2.2.0:
+ dependencies:
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ parseurl: 1.3.3
+ send: 1.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ setprototypeof@1.2.0: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ simple-concat@1.0.1: {}
+
+ simple-get@4.0.1:
+ dependencies:
+ decompress-response: 6.0.0
+ once: 1.4.0
+ simple-concat: 1.0.1
+
+ simple-update-notifier@2.0.0:
+ dependencies:
+ semver: 7.7.2
+
+ source-map-js@1.2.1: {}
+
+ speakingurl@14.0.1: {}
+
+ sqlstring@2.3.3: {}
+
+ standard-as-callback@2.1.0: {}
+
+ statuses@2.0.1: {}
+
+ statuses@2.0.2: {}
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ strip-json-comments@2.0.1: {}
+
+ superjson@2.2.2:
+ dependencies:
+ copy-anything: 3.0.5
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ swagger-jsdoc@6.2.8(openapi-types@12.1.3):
+ dependencies:
+ commander: 6.2.0
+ doctrine: 3.0.0
+ glob: 7.1.6
+ lodash.mergewith: 4.6.2
+ swagger-parser: 10.0.3(openapi-types@12.1.3)
+ yaml: 2.0.0-1
+ transitivePeerDependencies:
+ - openapi-types
+
+ swagger-parser@10.0.3(openapi-types@12.1.3):
+ dependencies:
+ '@apidevtools/swagger-parser': 10.0.3(openapi-types@12.1.3)
+ transitivePeerDependencies:
+ - openapi-types
+
+ swagger-ui-dist@5.27.1:
+ dependencies:
+ '@scarf/scarf': 1.4.0
+
+ swagger-ui-express@5.0.1(express@5.1.0):
+ dependencies:
+ express: 5.1.0
+ swagger-ui-dist: 5.27.1
+
+ tailwind-merge@3.3.1: {}
+
+ tailwindcss@4.1.11: {}
+
+ tapable@2.2.2: {}
+
+ tar-fs@2.1.3:
+ dependencies:
+ chownr: 1.1.4
+ mkdirp-classic: 0.5.3
+ pump: 3.0.3
+ tar-stream: 2.2.0
+
+ tar-stream@2.2.0:
+ dependencies:
+ bl: 4.1.0
+ end-of-stream: 1.4.5
+ fs-constants: 1.0.0
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+
+ tar@7.4.3:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.0.2
+ mkdirp: 3.0.1
+ yallist: 5.0.0
+
+ text-segmentation@1.0.3:
+ dependencies:
+ utrie: 1.0.2
+
+ tinyglobby@0.2.14:
+ dependencies:
+ fdir: 6.4.6(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ toidentifier@1.0.1: {}
+
+ touch@3.1.1: {}
+
+ tr46@0.0.3: {}
+
+ tslib@2.3.0: {}
+
+ tslib@2.8.1: {}
+
+ tsx@4.20.3:
+ dependencies:
+ esbuild: 0.25.8
+ get-tsconfig: 4.10.1
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ tunnel-agent@0.6.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ tw-animate-css@1.3.6: {}
+
+ type-is@2.0.1:
+ dependencies:
+ content-type: 1.0.5
+ media-typer: 1.1.0
+ mime-types: 3.0.1
+
+ typescript@5.9.2: {}
+
+ undefsafe@2.0.5: {}
+
+ undici-types@6.21.0: {}
+
+ undici@7.13.0: {}
+
+ unpipe@1.0.0: {}
+
+ util-deprecate@1.0.2: {}
+
+ utrie@1.0.2:
+ dependencies:
+ base64-arraybuffer: 1.0.2
+
+ uuid@11.1.0: {}
+
+ validator@13.15.15: {}
+
+ vary@1.1.2: {}
+
+ vaul-vue@0.4.1(reka-ui@2.4.1(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2)))(vue@3.5.18(typescript@5.9.2)):
+ dependencies:
+ '@vueuse/core': 10.11.1(vue@3.5.18(typescript@5.9.2))
+ reka-ui: 2.4.1(typescript@5.9.2)(vue@3.5.18(typescript@5.9.2))
+ vue: 3.5.18(typescript@5.9.2)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+
+ vite-express@0.21.1:
+ dependencies:
+ express-static-gzip: 2.2.0
+ picocolors: 1.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ vite@6.3.5(@types/node@22.17.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.3):
+ dependencies:
+ esbuild: 0.25.8
+ fdir: 6.4.6(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.46.2
+ tinyglobby: 0.2.14
+ optionalDependencies:
+ '@types/node': 22.17.0
+ fsevents: 2.3.3
+ jiti: 2.5.1
+ lightningcss: 1.30.1
+ tsx: 4.20.3
+
+ vscode-uri@3.1.0: {}
+
+ vue-demi@0.14.10(vue@3.5.18(typescript@5.9.2)):
+ dependencies:
+ vue: 3.5.18(typescript@5.9.2)
+
+ vue-router@4.5.1(vue@3.5.18(typescript@5.9.2)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.18(typescript@5.9.2)
+
+ vue-sonner@2.0.2: {}
+
+ vue-tsc@2.2.12(typescript@5.9.2):
+ dependencies:
+ '@volar/typescript': 2.4.15
+ '@vue/language-core': 2.2.12(typescript@5.9.2)
+ typescript: 5.9.2
+
+ vue@3.5.18(typescript@5.9.2):
+ dependencies:
+ '@vue/compiler-dom': 3.5.18
+ '@vue/compiler-sfc': 3.5.18
+ '@vue/runtime-dom': 3.5.18
+ '@vue/server-renderer': 3.5.18(vue@3.5.18(typescript@5.9.2))
+ '@vue/shared': 3.5.18
+ optionalDependencies:
+ typescript: 5.9.2
+
+ vuex@4.1.0(vue@3.5.18(typescript@5.9.2)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.18(typescript@5.9.2)
+
+ webidl-conversions@3.0.1: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ wrappy@1.0.2: {}
+
+ ws@8.18.3: {}
+
+ yallist@5.0.0: {}
+
+ yaml@2.0.0-1: {}
+
+ z-schema@5.0.5:
+ dependencies:
+ lodash.get: 4.4.2
+ lodash.isequal: 4.5.0
+ validator: 13.15.15
+ optionalDependencies:
+ commander: 9.5.0
+
+ zrender@5.6.1:
+ dependencies:
+ tslib: 2.3.0
diff --git a/public/aoteam.svg b/public/aoteam.svg
new file mode 100644
index 0000000..7e433a5
--- /dev/null
+++ b/public/aoteam.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/public/gt4.js b/public/gt4.js
new file mode 100644
index 0000000..772e096
--- /dev/null
+++ b/public/gt4.js
@@ -0,0 +1,487 @@
+"v4.2.0 Geetest Inc.";
+
+(function (window) {
+ "use strict";
+ if (typeof window === 'undefined') {
+ throw new Error('Geetest requires browser environment');
+ }
+
+var document = window.document;
+var Math = window.Math;
+var head = document.getElementsByTagName("head")[0];
+var TIMEOUT = 10000;
+
+function _Object(obj) {
+ this._obj = obj;
+}
+
+_Object.prototype = {
+ _each: function (process) {
+ var _obj = this._obj;
+ for (var k in _obj) {
+ if (_obj.hasOwnProperty(k)) {
+ process(k, _obj[k]);
+ }
+ }
+ return this;
+ },
+ _extend: function (obj){
+ var self = this;
+ new _Object(obj)._each(function (key, value){
+ self._obj[key] = value;
+ })
+ }
+};
+
+var uuid = function () {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+ var r = Math.random() * 16 | 0;
+ var v = c === 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+ };
+
+function Config(config) {
+ var self = this;
+ new _Object(config)._each(function (key, value) {
+ self[key] = value;
+ });
+}
+
+Config.prototype = {
+ apiServers: ['gcaptcha4.geetest.com','gcaptcha4.geevisit.com','gcaptcha4.gsensebot.com'],
+ staticServers: ["static.geetest.com",'static.geevisit.com'],
+ protocol: 'http://',
+ typePath: '/load',
+ fallback_config: {
+ bypass: {
+ staticServers: ["static.geetest.com",'static.geevisit.com'],
+ type: 'bypass',
+ bypass: '/v4/bypass.js'
+ }
+ },
+ _get_fallback_config: function () {
+ var self = this;
+ if (isString(self.type)) {
+ return self.fallback_config[self.type];
+ } else {
+ return self.fallback_config.bypass;
+ }
+ },
+ _extend: function (obj) {
+ var self = this;
+ new _Object(obj)._each(function (key, value) {
+ self[key] = value;
+ })
+ }
+};
+var isNumber = function (value) {
+ return (typeof value === 'number');
+};
+var isString = function (value) {
+ return (typeof value === 'string');
+};
+var isBoolean = function (value) {
+ return (typeof value === 'boolean');
+};
+var isObject = function (value) {
+ return (typeof value === 'object' && value !== null);
+};
+var isFunction = function (value) {
+ return (typeof value === 'function');
+};
+var MOBILE = /Mobi/i.test(navigator.userAgent);
+
+var callbacks = {};
+var status = {};
+
+var random = function () {
+ return parseInt(Math.random() * 10000) + (new Date()).valueOf();
+};
+
+// bind 函数polify, 不带new功能的bind
+
+var bind = function(target,context){
+ if(typeof target !== 'function'){
+ return;
+ }
+ var args = Array.prototype.slice.call(arguments,2);
+
+ if(Function.prototype.bind){
+ return target.bind(context, args);
+ }else {
+ return function(){
+ var _args = Array.prototype.slice.call(arguments);
+ return target.apply(context,args.concat(_args));
+ }
+ }
+}
+
+
+
+var toString = Object.prototype.toString;
+
+var _isFunction = function(obj) {
+ return typeof(obj) === 'function';
+};
+var _isObject = function(obj) {
+ return obj === Object(obj);
+};
+var _isArray = function(obj) {
+ return toString.call(obj) == '[object Array]';
+};
+var _isDate = function(obj) {
+ return toString.call(obj) == '[object Date]';
+};
+var _isRegExp = function(obj) {
+ return toString.call(obj) == '[object RegExp]';
+};
+var _isBoolean = function(obj) {
+ return toString.call(obj) == '[object Boolean]';
+};
+
+
+function resolveKey(input){
+ return input.replace(/(\S)(_([a-zA-Z]))/g, function(match, $1, $2, $3){
+ return $1 + $3.toUpperCase() || "";
+ })
+}
+
+function camelizeKeys(input, convert){
+ if(!_isObject(input) || _isDate(input) || _isRegExp(input) || _isBoolean(input) || _isFunction(input)){
+ return convert ? resolveKey(input) : input;
+ }
+
+ if(_isArray(input)){
+ var temp = [];
+ for(var i = 0; i < input.length; i++){
+ temp.push(camelizeKeys(input[i]));
+ }
+
+ }else {
+ var temp = {};
+ for(var prop in input){
+ if(input.hasOwnProperty(prop)){
+ temp[camelizeKeys(prop, true)] = camelizeKeys(input[prop]);
+ }
+ }
+ }
+ return temp;
+}
+
+var loadScript = function (url, cb, timeout) {
+ var script = document.createElement("script");
+ script.charset = "UTF-8";
+ script.async = true;
+
+ // 对geetest的静态资源添加 crossOrigin
+ if ( /static\.geetest\.com/g.test(url)) {
+ script.crossOrigin = "anonymous";
+ }
+
+ script.onerror = function () {
+ cb(true);
+ // 错误触发了,超时逻辑就不用了
+ loaded = true;
+ };
+ var loaded = false;
+ script.onload = script.onreadystatechange = function () {
+ if (!loaded &&
+ (!script.readyState ||
+ "loaded" === script.readyState ||
+ "complete" === script.readyState)) {
+
+ loaded = true;
+ setTimeout(function () {
+ cb(false);
+ }, 0);
+ }
+ };
+ script.src = url;
+ head.appendChild(script);
+
+ setTimeout(function () {
+ if (!loaded) {
+ script.onerror = script.onload = null;
+ script.remove && script.remove();
+ cb(true);
+ }
+ }, timeout || TIMEOUT);
+};
+
+var normalizeDomain = function (domain) {
+ // special domain: uems.sysu.edu.cn/jwxt/geetest/
+ // return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
+ return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
+};
+var normalizePath = function (path) {
+
+ path = path && path.replace(/\/+/g, '/');
+ if (path.indexOf('/') !== 0) {
+ path = '/' + path;
+ }
+ return path;
+};
+var normalizeQuery = function (query) {
+ if (!query) {
+ return '';
+ }
+ var q = '?';
+ new _Object(query)._each(function (key, value) {
+ if (isString(value) || isNumber(value) || isBoolean(value)) {
+ q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
+ }
+ });
+ if (q === '?') {
+ q = '';
+ }
+ return q.replace(/&$/, '');
+};
+var makeURL = function (protocol, domain, path, query) {
+ domain = normalizeDomain(domain);
+
+ var url = normalizePath(path) + normalizeQuery(query);
+ if (domain) {
+ url = protocol + domain + url;
+ }
+
+ return url;
+};
+
+var load = function (config, protocol, domains, path, query, cb, handleCb) {
+ var tryRequest = function (at) {
+ // 处理jsonp回调,这里为了保证每个不同jsonp都有唯一的回调函数
+ if(handleCb){
+ var cbName = "geetest_" + random();
+ // 需要与预先定义好cbname参数,删除对象
+ window[cbName] = bind(handleCb, null, cbName);
+ query.callback = cbName;
+ }
+ var url = makeURL(protocol, domains[at], path, query);
+ loadScript(url, function (err) {
+ if (err) {
+ // 超时或者出错的时候 移除回调
+ if(cbName){
+ try {
+ window[cbName] = function(){
+ window[cbName] = null;
+ }
+ } catch (e) {}
+ }
+
+ if (at >= domains.length - 1) {
+ cb(true);
+ // report gettype error
+ } else {
+ tryRequest(at + 1);
+ }
+ } else {
+ cb(false);
+ }
+ }, config.timeout);
+ };
+ tryRequest(0);
+};
+
+
+var jsonp = function (domains, path, config, callback) {
+
+ var handleCb = function (cbName, data) {
+
+ // 保证只执行一次,全部超时的情况下不会再触发;
+
+ if (data.status == 'success') {
+ callback(data.data);
+ } else if (!data.status) {
+ callback(data);
+ } else {
+ //接口有返回,但是返回了错误状态,进入报错逻辑
+ callback(data);
+ }
+ window[cbName] = undefined;
+ try {
+ delete window[cbName];
+ } catch (e) {
+ }
+ };
+ load(config, config.protocol, domains, path, {
+ callback: '',
+ captcha_id: config.captchaId,
+ challenge: config.challenge || uuid(),
+ client_type: config.clientType ? config.clientType : (MOBILE? 'h5':'web'),
+ risk_type: config.riskType,
+ user_info: config.userInfo,
+ call_type: config.callType,
+ lang: config.language? config.language : navigator.appName === 'Netscape' ? navigator.language.toLowerCase() : navigator.userLanguage.toLowerCase()
+ }, function (err) {
+ // 网络问题接口没有返回,直接使用本地验证码,走宕机模式
+ // 这里可以添加用户的逻辑
+ if(err && typeof config.offlineCb === 'function'){
+ // 执行自己的宕机
+ config.offlineCb();
+ return;
+ }
+ if(err){
+ callback(config._get_fallback_config());
+ }
+ }, handleCb);
+};
+
+var reportError = function (config, url) {
+ load(config, config.protocol, ['monitor.geetest.com'], '/monitor/send', {
+ time: Date.now().getTime(),
+ captcha_id: config.gt,
+ challenge: config.challenge,
+ exception_url: url,
+ error_code: config.error_code
+ }, function (err) {})
+}
+
+var throwError = function (errorType, config, errObj) {
+ var errors = {
+ networkError: '网络错误',
+ gtTypeError: 'gt字段不是字符串类型'
+ };
+ if (typeof config.onError === 'function') {
+ config.onError({
+ desc: errObj.desc,
+ msg: errObj.msg,
+ code: errObj.code
+ });
+ } else {
+ throw new Error(errors[errorType]);
+ }
+};
+
+var detect = function () {
+ return window.Geetest || document.getElementById("gt_lib");
+};
+
+if (detect()) {
+ status.slide = "loaded";
+}
+var GeetestIsLoad = function (fname) {
+ var GeetestIsLoad = false;
+ var tags = { js: 'script', css: 'link' };
+ var tagname = fname && tags[fname.split('.').pop()];
+ if (tagname !== undefined) {
+ var elts = document.getElementsByTagName(tagname);
+ for (var i in elts) {
+ if ((elts[i].href && elts[i].href.toString().indexOf(fname) > 0)
+ || (elts[i].src && elts[i].src.toString().indexOf(fname) > 0)) {
+ GeetestIsLoad = true;
+ }
+ }
+ }
+ return GeetestIsLoad;
+};
+window.initGeetest4 = function (userConfig,callback) {
+
+ var config = new Config(userConfig);
+ if (userConfig.https) {
+ config.protocol = 'https://';
+ } else if (!userConfig.protocol) {
+ config.protocol = window.location.protocol + '//';
+ }
+
+
+ if (isObject(userConfig.getType)) {
+ config._extend(userConfig.getType);
+ }
+
+ jsonp(config.apiServers , config.typePath, config, function (newConfig) {
+ //错误捕获,第一个load请求可能直接报错
+ var newConfig = camelizeKeys(newConfig);
+
+ if(newConfig.status === 'error'){
+ return throwError('networkError', config, newConfig);
+ }
+
+ var type = newConfig.type;
+ if(config.debug){
+ new _Object(newConfig)._extend(config.debug)
+ }
+ var init = function () {
+ config._extend(newConfig);
+ callback(new window.Geetest4(config));
+ };
+
+ callbacks[type] = callbacks[type] || [];
+
+ var s = status[type] || 'init';
+ if (s === 'init') {
+ status[type] = 'loading';
+
+ callbacks[type].push(init);
+
+ if(newConfig.gctPath){
+ load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers , newConfig.gctPath, null, function (err){
+ if(err){
+ throwError('networkError', config, {
+ code: '60205',
+ msg: 'Network failure',
+ desc: {
+ detail: 'gct resource load timeout'
+ }
+ });
+ }
+ })
+ }
+
+ load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers, newConfig.bypass || (newConfig.staticPath + newConfig.js), null, function (err) {
+ if (err) {
+ status[type] = 'fail';
+ throwError('networkError', config, {
+ code: '60204',
+ msg: 'Network failure',
+ desc: {
+ detail: 'js resource load timeout'
+ }
+ });
+ } else {
+
+ status[type] = 'loaded';
+ var cbs = callbacks[type];
+ for (var i = 0, len = cbs.length; i < len; i = i + 1) {
+ var cb = cbs[i];
+ if (isFunction(cb)) {
+ cb();
+ }
+ }
+ callbacks[type] = [];
+ status[type] = 'init';
+ }
+ });
+ } else if (s === "loaded") {
+ // 判断gct是否需要重新加载
+ if(newConfig.gctPath && !GeetestIsLoad(newConfig.gctPath)){
+ load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers , newConfig.gctPath, null, function (err){
+ if(err){
+ throwError('networkError', config, {
+ code: '60205',
+ msg: 'Network failure',
+ desc: {
+ detail: 'gct resource load timeout'
+ }
+ });
+ }
+ })
+ }
+ return init();
+ } else if (s === "fail") {
+ throwError('networkError', config, {
+ code: '60204',
+ msg: 'Network failure',
+ desc: {
+ detail: 'js resource load timeout'
+ }
+ });
+ } else if (s === "loading") {
+ callbacks[type].push(init);
+ }
+ });
+
+};
+
+
+})(window);
diff --git a/public/vite.svg b/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/sql/init.sql b/sql/init.sql
new file mode 100644
index 0000000..71828cd
--- /dev/null
+++ b/sql/init.sql
@@ -0,0 +1,3 @@
+CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY 'GUYJ45G3KU45HG3K245JH2G52UJ3Y45G2U34Y2IK';
+GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
+FLUSH PRIVILEGES;
diff --git a/sql/spacetab-data.sql b/sql/spacetab-data.sql
new file mode 100644
index 0000000..5097258
--- /dev/null
+++ b/sql/spacetab-data.sql
@@ -0,0 +1,112 @@
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+CREATE DATABASE IF NOT EXISTS `lunovastudio-wlserver-data`;
+USE `lunovastudio-wlserver-data`;
+-- ----------------------------
+-- Table structure for equipment_disposition
+-- ----------------------------
+DROP TABLE IF EXISTS `equipment_disposition`;
+CREATE TABLE `equipment_disposition` (
+ `info_uuid` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `equipment_uuid` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `system_architecture` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `system` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `system_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `ram_info` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `date` datetime NOT NULL,
+ `wonderlab_run_file` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `wonderlab_v` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ PRIMARY KEY (`info_uuid`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for equipment_info
+-- ----------------------------
+DROP TABLE IF EXISTS `equipment_info`;
+CREATE TABLE `equipment_info` (
+ `info_uuid` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `bios_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `country` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `user_id` int NOT NULL,
+ `date` datetime NOT NULL,
+ PRIMARY KEY (`info_uuid`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for github_mirror_info
+-- ----------------------------
+DROP TABLE IF EXISTS `github_mirror_info`;
+CREATE TABLE `github_mirror_info` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '镜像源名称',
+ `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '镜像源URL',
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ PRIMARY KEY (`id`) USING BTREE,
+ UNIQUE INDEX `name`(`name` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'GitHub镜像源信息表' ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for server_about
+-- ----------------------------
+DROP TABLE IF EXISTS `server_about`;
+CREATE TABLE `server_about` (
+ `id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '记录ID(UUID)',
+ `about` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '服务器描述(md)',
+ `docs_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '服务器文档链接',
+ `info_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '关联服务器ID',
+ `ip` json NOT NULL COMMENT '服务器地址(支持多个IP)',
+ `server_type` enum('原版服务器','插件服务器','MOD服务器','插件MOD混合服务器') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '服务器类型',
+ `server_oauth` enum('mojang','none','yggdrasil') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '验证类型',
+ `qq_group` json NULL COMMENT '服务器群(支持多个群)',
+ `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '信息记录日期',
+ `audit_status` enum('yes','no','lock') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'no' COMMENT '审核状态',
+ `audit_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '审核原因',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for server_info
+-- ----------------------------
+DROP TABLE IF EXISTS `server_info`;
+CREATE TABLE `server_info` (
+ `id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '服务器记录ID(UUID)',
+ `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '服务器名称',
+ `server_icon_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '服务器图标URL',
+ `server_about_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '服务器当前数据表ID',
+ `country` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '设备地区',
+ `user_id` bigint NOT NULL COMMENT '关联用户id',
+ `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '服务器登记日期(UTC+8)',
+ `todo` int NULL DEFAULT 0 COMMENT '服务器被访问数',
+ `audit_status` enum('yes','no','lock') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'no' COMMENT '审核状态',
+ `audit_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '审核原因',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for users
+-- ----------------------------
+DROP TABLE IF EXISTS `users`;
+CREATE TABLE `users` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `is_banned` tinyint(1) NOT NULL DEFAULT 0,
+ `ban_reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
+ `ban_expiration` datetime NULL DEFAULT NULL,
+ `is_admin` tinyint(1) NOT NULL DEFAULT 0,
+ `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` datetime NULL DEFAULT NULL,
+ `last_login` datetime NULL DEFAULT NULL,
+ `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ `qq_num` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ `email_verified` tinyint(1) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`) USING BTREE,
+ UNIQUE INDEX `email`(`email` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 10006 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
+
+SET FOREIGN_KEY_CHECKS = 1;
+
+
+INSERT INTO `lunovastudio-wlserver-data`.`users` (`id`, `username`, `email`, `password`, `is_banned`, `ban_reason`, `ban_expiration`, `is_admin`, `created_at`, `updated_at`, `last_login`, `avatar`, `qq_num`, `email_verified`) VALUES (10000, 'admin', 'support@lunova.studio', '$2b$12$R847j17qb/aUoQ4sEAPDdOXuFfg6Vb.HGiUMrjyB5Xrv9OI9.kvG2', 0, NULL, NULL, 1, '2025-06-25 15:41:58', '2025-06-28 21:21:01', '2025-07-30 20:00:36', NULL, '1723327932', 0);
+
diff --git a/src/background.js b/src/background.js
new file mode 100644
index 0000000..160eee4
--- /dev/null
+++ b/src/background.js
@@ -0,0 +1,7 @@
+// 浏览器扩展的后台脚本
+console.log('Space.Tab 扩展已加载');
+
+// 监听扩展安装事件
+chrome.runtime.onInstalled.addListener(() => {
+ console.log('Space.Tab 扩展已安装');
+});
\ No newline at end of file
diff --git a/src/client/App.vue b/src/client/App.vue
new file mode 100644
index 0000000..897700a
--- /dev/null
+++ b/src/client/App.vue
@@ -0,0 +1,561 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/assets/aoteam.svg b/src/client/assets/aoteam.svg
new file mode 100644
index 0000000..7e433a5
--- /dev/null
+++ b/src/client/assets/aoteam.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/assets/vue.svg b/src/client/assets/vue.svg
new file mode 100644
index 0000000..770e9d3
--- /dev/null
+++ b/src/client/assets/vue.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/client/components/AdminPermissionCheck.vue b/src/client/components/AdminPermissionCheck.vue
new file mode 100644
index 0000000..bd0866c
--- /dev/null
+++ b/src/client/components/AdminPermissionCheck.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 访问权限不足
+
+
+
+
+ 您的帐户无运营权限,请向在职运营请求运营权限
+
+
+
+
+ 回到仪表盘
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/AppFooter.vue b/src/client/components/AppFooter.vue
new file mode 100644
index 0000000..9039eb9
--- /dev/null
+++ b/src/client/components/AppFooter.vue
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/AppHeader.vue b/src/client/components/AppHeader.vue
new file mode 100644
index 0000000..14505ca
--- /dev/null
+++ b/src/client/components/AppHeader.vue
@@ -0,0 +1,426 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/AppSidebar.vue b/src/client/components/AppSidebar.vue
new file mode 100644
index 0000000..1be76a9
--- /dev/null
+++ b/src/client/components/AppSidebar.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+ Space.Tab
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 管理
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 登录
+
+
+
+
+ 注册
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/GeetestCaptcha.vue b/src/client/components/GeetestCaptcha.vue
new file mode 100644
index 0000000..e1bd90f
--- /dev/null
+++ b/src/client/components/GeetestCaptcha.vue
@@ -0,0 +1,332 @@
+
+
+
+
+
+
+
+
{{ error }}
+
+ {{ loading ? '重试中...' : '点击重试' }}
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/HelloWorld.vue b/src/client/components/HelloWorld.vue
new file mode 100644
index 0000000..2bc3cdc
--- /dev/null
+++ b/src/client/components/HelloWorld.vue
@@ -0,0 +1,37 @@
+
+
+
+ {{ msg }}
+
+
+
count is {{ count }}
+
+ Edit components/HelloWorld.vue to test HMR
+
+
+
+
+ Check out
+ create-vue , the official Vue + Vite
+ starter
+
+
+ Install
+ Volar
+ in your IDE for a better DX
+
+ Click on the Vite and Vue logos to learn more
+
+
+
diff --git a/src/client/components/LoginForm.vue b/src/client/components/LoginForm.vue
new file mode 100644
index 0000000..21fdabf
--- /dev/null
+++ b/src/client/components/LoginForm.vue
@@ -0,0 +1,282 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 正在登录...
+
+
+
+ 点击继续,表示您同意并遵守 Lingke Network 隐私策略 和 Lingke Network 产品使用条款 .
+
+
+
+
+
+
+
diff --git a/src/client/components/NavMain.vue b/src/client/components/NavMain.vue
new file mode 100644
index 0000000..538a7a3
--- /dev/null
+++ b/src/client/components/NavMain.vue
@@ -0,0 +1,48 @@
+
+
+
+
+ 综合
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
diff --git a/src/client/components/NavProjects.vue b/src/client/components/NavProjects.vue
new file mode 100644
index 0000000..0afc3e5
--- /dev/null
+++ b/src/client/components/NavProjects.vue
@@ -0,0 +1,85 @@
+
+
+
+
+ 项目
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+ More
+
+
+
+
+
+ View Project
+
+
+
+ Share Project
+
+
+
+
+ Delete Project
+
+
+
+
+
+
+
+ More
+
+
+
+
+
diff --git a/src/client/components/NavSecondary.vue b/src/client/components/NavSecondary.vue
new file mode 100644
index 0000000..2843180
--- /dev/null
+++ b/src/client/components/NavSecondary.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
diff --git a/src/client/components/NavUser.vue b/src/client/components/NavUser.vue
new file mode 100644
index 0000000..a6d2c4d
--- /dev/null
+++ b/src/client/components/NavUser.vue
@@ -0,0 +1,224 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ console.log('QQ头像加载失败,使用回退头像')" />
+ {{ fallbackAvatar || userInitials }}
+
+
+
+ {{ userStore.userInfo?.username || '用户' }}
+
+ PRO
+
+
+
{{ userStore.userInfo?.email || '' }}
+
+
+
+
+
+
+
+
+
+ 仪表盘
+
+
+
+
+
+ Today At Space
+
+
+
+
+
+ 系统设置
+
+
+
+
+
显示模式
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 退出登录
+
+
+
+
+
+
diff --git a/src/client/components/RegisterForm.vue b/src/client/components/RegisterForm.vue
new file mode 100644
index 0000000..dfaacea
--- /dev/null
+++ b/src/client/components/RegisterForm.vue
@@ -0,0 +1,266 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 点击继续,表示您同意并遵守 Lingke Network 隐私策略 和 Lingke Network 产品使用条款 .
+
+
+
+
+
+
+
+
diff --git a/src/client/components/VersionAvatar.vue b/src/client/components/VersionAvatar.vue
new file mode 100644
index 0000000..7af3a6e
--- /dev/null
+++ b/src/client/components/VersionAvatar.vue
@@ -0,0 +1,113 @@
+
+
+
+
+ {{ avatarText }}
+
+
+
+
diff --git a/src/client/components/WallpaperSettings.vue b/src/client/components/WallpaperSettings.vue
new file mode 100644
index 0000000..2a03e10
--- /dev/null
+++ b/src/client/components/WallpaperSettings.vue
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+ 功能正在调试
+
+ 此功能仍然在测试,我们正在优化体验,在后续版本可能会有更多改动!如有问题请多多包涵
+
+
+
+
+
+
+
+
{{ type.name }}
+
{{ type.description }}
+
+
+
+
+
+
+
+
+ 选择图片
+
+
+
+
+
+
预览:
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/Watermark.vue b/src/client/components/Watermark.vue
new file mode 100644
index 0000000..7351c4a
--- /dev/null
+++ b/src/client/components/Watermark.vue
@@ -0,0 +1,66 @@
+
+
+
Space Tab
+ 测试版本,不代表最终品质
+ USER·{{ user.username }}·{{ user.email }}
+ 测试版本: V.{{ versionNumber }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/WeatherWidget.vue b/src/client/components/WeatherWidget.vue
new file mode 100644
index 0000000..38d86ca
--- /dev/null
+++ b/src/client/components/WeatherWidget.vue
@@ -0,0 +1,301 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/ui/alert-dialog/AlertDialog.vue b/src/client/components/ui/alert-dialog/AlertDialog.vue
new file mode 100644
index 0000000..e4fc7b8
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialog.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogAction.vue b/src/client/components/ui/alert-dialog/AlertDialogAction.vue
new file mode 100644
index 0000000..ee53328
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogAction.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogCancel.vue b/src/client/components/ui/alert-dialog/AlertDialogCancel.vue
new file mode 100644
index 0000000..f9cba4e
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogCancel.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogContent.vue b/src/client/components/ui/alert-dialog/AlertDialogContent.vue
new file mode 100644
index 0000000..ab18b73
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogContent.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogDescription.vue b/src/client/components/ui/alert-dialog/AlertDialogDescription.vue
new file mode 100644
index 0000000..ab09406
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogDescription.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogFooter.vue b/src/client/components/ui/alert-dialog/AlertDialogFooter.vue
new file mode 100644
index 0000000..b6dde32
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogFooter.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogHeader.vue b/src/client/components/ui/alert-dialog/AlertDialogHeader.vue
new file mode 100644
index 0000000..22cff27
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogHeader.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogTitle.vue b/src/client/components/ui/alert-dialog/AlertDialogTitle.vue
new file mode 100644
index 0000000..c36a797
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogTitle.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/AlertDialogTrigger.vue b/src/client/components/ui/alert-dialog/AlertDialogTrigger.vue
new file mode 100644
index 0000000..4199286
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/AlertDialogTrigger.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert-dialog/index.ts b/src/client/components/ui/alert-dialog/index.ts
new file mode 100644
index 0000000..448d519
--- /dev/null
+++ b/src/client/components/ui/alert-dialog/index.ts
@@ -0,0 +1,9 @@
+export { default as AlertDialog } from './AlertDialog.vue'
+export { default as AlertDialogAction } from './AlertDialogAction.vue'
+export { default as AlertDialogCancel } from './AlertDialogCancel.vue'
+export { default as AlertDialogContent } from './AlertDialogContent.vue'
+export { default as AlertDialogDescription } from './AlertDialogDescription.vue'
+export { default as AlertDialogFooter } from './AlertDialogFooter.vue'
+export { default as AlertDialogHeader } from './AlertDialogHeader.vue'
+export { default as AlertDialogTitle } from './AlertDialogTitle.vue'
+export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue'
diff --git a/src/client/components/ui/alert/Alert.vue b/src/client/components/ui/alert/Alert.vue
new file mode 100644
index 0000000..fbe1221
--- /dev/null
+++ b/src/client/components/ui/alert/Alert.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert/AlertDescription.vue b/src/client/components/ui/alert/AlertDescription.vue
new file mode 100644
index 0000000..f77e466
--- /dev/null
+++ b/src/client/components/ui/alert/AlertDescription.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert/AlertTitle.vue b/src/client/components/ui/alert/AlertTitle.vue
new file mode 100644
index 0000000..91a9299
--- /dev/null
+++ b/src/client/components/ui/alert/AlertTitle.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/alert/index.ts b/src/client/components/ui/alert/index.ts
new file mode 100644
index 0000000..3ff70b4
--- /dev/null
+++ b/src/client/components/ui/alert/index.ts
@@ -0,0 +1,23 @@
+import { cva, type VariantProps } from 'class-variance-authority'
+
+export { default as Alert } from './Alert.vue'
+export { default as AlertDescription } from './AlertDescription.vue'
+export { default as AlertTitle } from './AlertTitle.vue'
+
+export const alertVariants = cva(
+ 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
+ {
+ variants: {
+ variant: {
+ default: 'bg-card text-card-foreground',
+ destructive:
+ 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ },
+ },
+)
+
+export type AlertVariants = VariantProps
diff --git a/src/client/components/ui/avatar/Avatar.vue b/src/client/components/ui/avatar/Avatar.vue
new file mode 100644
index 0000000..5aa263d
--- /dev/null
+++ b/src/client/components/ui/avatar/Avatar.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/avatar/AvatarFallback.vue b/src/client/components/ui/avatar/AvatarFallback.vue
new file mode 100644
index 0000000..dc20f80
--- /dev/null
+++ b/src/client/components/ui/avatar/AvatarFallback.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/avatar/AvatarImage.vue b/src/client/components/ui/avatar/AvatarImage.vue
new file mode 100644
index 0000000..801392b
--- /dev/null
+++ b/src/client/components/ui/avatar/AvatarImage.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/avatar/index.ts b/src/client/components/ui/avatar/index.ts
new file mode 100644
index 0000000..6a90410
--- /dev/null
+++ b/src/client/components/ui/avatar/index.ts
@@ -0,0 +1,3 @@
+export { default as Avatar } from './Avatar.vue'
+export { default as AvatarFallback } from './AvatarFallback.vue'
+export { default as AvatarImage } from './AvatarImage.vue'
diff --git a/src/client/components/ui/badge/Badge.vue b/src/client/components/ui/badge/Badge.vue
new file mode 100644
index 0000000..e42b898
--- /dev/null
+++ b/src/client/components/ui/badge/Badge.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/badge/index.ts b/src/client/components/ui/badge/index.ts
new file mode 100644
index 0000000..61d5cad
--- /dev/null
+++ b/src/client/components/ui/badge/index.ts
@@ -0,0 +1,25 @@
+import { cva, type VariantProps } from 'class-variance-authority'
+
+export { default as Badge } from './Badge.vue'
+
+export const badgeVariants = cva(
+ 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
+ {
+ variants: {
+ variant: {
+ default:
+ 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
+ secondary:
+ 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
+ destructive:
+ 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
+ outline:
+ 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ },
+ },
+)
+export type BadgeVariants = VariantProps
diff --git a/src/client/components/ui/breadcrumb/Breadcrumb.vue b/src/client/components/ui/breadcrumb/Breadcrumb.vue
new file mode 100644
index 0000000..b99dcea
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/Breadcrumb.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/breadcrumb/BreadcrumbEllipsis.vue b/src/client/components/ui/breadcrumb/BreadcrumbEllipsis.vue
new file mode 100644
index 0000000..b147851
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/BreadcrumbEllipsis.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+ More
+
+
diff --git a/src/client/components/ui/breadcrumb/BreadcrumbItem.vue b/src/client/components/ui/breadcrumb/BreadcrumbItem.vue
new file mode 100644
index 0000000..f50f69d
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/BreadcrumbItem.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/breadcrumb/BreadcrumbLink.vue b/src/client/components/ui/breadcrumb/BreadcrumbLink.vue
new file mode 100644
index 0000000..c94995e
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/BreadcrumbLink.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/breadcrumb/BreadcrumbList.vue b/src/client/components/ui/breadcrumb/BreadcrumbList.vue
new file mode 100644
index 0000000..3a92c48
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/BreadcrumbList.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/breadcrumb/BreadcrumbPage.vue b/src/client/components/ui/breadcrumb/BreadcrumbPage.vue
new file mode 100644
index 0000000..cc667bc
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/BreadcrumbPage.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/breadcrumb/BreadcrumbSeparator.vue b/src/client/components/ui/breadcrumb/BreadcrumbSeparator.vue
new file mode 100644
index 0000000..7ee2d01
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/BreadcrumbSeparator.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/breadcrumb/index.ts b/src/client/components/ui/breadcrumb/index.ts
new file mode 100644
index 0000000..0590983
--- /dev/null
+++ b/src/client/components/ui/breadcrumb/index.ts
@@ -0,0 +1,7 @@
+export { default as Breadcrumb } from './Breadcrumb.vue'
+export { default as BreadcrumbEllipsis } from './BreadcrumbEllipsis.vue'
+export { default as BreadcrumbItem } from './BreadcrumbItem.vue'
+export { default as BreadcrumbLink } from './BreadcrumbLink.vue'
+export { default as BreadcrumbList } from './BreadcrumbList.vue'
+export { default as BreadcrumbPage } from './BreadcrumbPage.vue'
+export { default as BreadcrumbSeparator } from './BreadcrumbSeparator.vue'
diff --git a/src/client/components/ui/button/Button.vue b/src/client/components/ui/button/Button.vue
new file mode 100644
index 0000000..ecf3fe3
--- /dev/null
+++ b/src/client/components/ui/button/Button.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/button/index.ts b/src/client/components/ui/button/index.ts
new file mode 100644
index 0000000..616d1d3
--- /dev/null
+++ b/src/client/components/ui/button/index.ts
@@ -0,0 +1,36 @@
+import { cva, type VariantProps } from 'class-variance-authority'
+
+export { default as Button } from './Button.vue'
+
+export const buttonVariants = cva(
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
+ {
+ variants: {
+ variant: {
+ default:
+ 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
+ destructive:
+ 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
+ outline:
+ 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
+ secondary:
+ 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
+ ghost:
+ 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
+ link: 'text-primary underline-offset-4 hover:underline',
+ },
+ size: {
+ default: 'h-9 px-4 py-2 has-[>svg]:px-3',
+ sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
+ lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
+ icon: 'size-9',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+)
+
+export type ButtonVariants = VariantProps
diff --git a/src/client/components/ui/card/Card.vue b/src/client/components/ui/card/Card.vue
new file mode 100644
index 0000000..9154a6a
--- /dev/null
+++ b/src/client/components/ui/card/Card.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/card/CardAction.vue b/src/client/components/ui/card/CardAction.vue
new file mode 100644
index 0000000..c2beb20
--- /dev/null
+++ b/src/client/components/ui/card/CardAction.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/card/CardContent.vue b/src/client/components/ui/card/CardContent.vue
new file mode 100644
index 0000000..6bff4bc
--- /dev/null
+++ b/src/client/components/ui/card/CardContent.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/card/CardDescription.vue b/src/client/components/ui/card/CardDescription.vue
new file mode 100644
index 0000000..2a0a755
--- /dev/null
+++ b/src/client/components/ui/card/CardDescription.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/card/CardFooter.vue b/src/client/components/ui/card/CardFooter.vue
new file mode 100644
index 0000000..1f3648d
--- /dev/null
+++ b/src/client/components/ui/card/CardFooter.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/card/CardHeader.vue b/src/client/components/ui/card/CardHeader.vue
new file mode 100644
index 0000000..f693a6c
--- /dev/null
+++ b/src/client/components/ui/card/CardHeader.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/card/CardTitle.vue b/src/client/components/ui/card/CardTitle.vue
new file mode 100644
index 0000000..caa7e06
--- /dev/null
+++ b/src/client/components/ui/card/CardTitle.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/card/index.ts b/src/client/components/ui/card/index.ts
new file mode 100644
index 0000000..73d985f
--- /dev/null
+++ b/src/client/components/ui/card/index.ts
@@ -0,0 +1,7 @@
+export { default as Card } from './Card.vue'
+export { default as CardAction } from './CardAction.vue'
+export { default as CardContent } from './CardContent.vue'
+export { default as CardDescription } from './CardDescription.vue'
+export { default as CardFooter } from './CardFooter.vue'
+export { default as CardHeader } from './CardHeader.vue'
+export { default as CardTitle } from './CardTitle.vue'
diff --git a/src/client/components/ui/collapsible/Collapsible.vue b/src/client/components/ui/collapsible/Collapsible.vue
new file mode 100644
index 0000000..dcd58e2
--- /dev/null
+++ b/src/client/components/ui/collapsible/Collapsible.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/collapsible/CollapsibleContent.vue b/src/client/components/ui/collapsible/CollapsibleContent.vue
new file mode 100644
index 0000000..4120d2f
--- /dev/null
+++ b/src/client/components/ui/collapsible/CollapsibleContent.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/collapsible/CollapsibleTrigger.vue b/src/client/components/ui/collapsible/CollapsibleTrigger.vue
new file mode 100644
index 0000000..be603a4
--- /dev/null
+++ b/src/client/components/ui/collapsible/CollapsibleTrigger.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/collapsible/index.ts b/src/client/components/ui/collapsible/index.ts
new file mode 100644
index 0000000..abab956
--- /dev/null
+++ b/src/client/components/ui/collapsible/index.ts
@@ -0,0 +1,3 @@
+export { default as Collapsible } from './Collapsible.vue'
+export { default as CollapsibleContent } from './CollapsibleContent.vue'
+export { default as CollapsibleTrigger } from './CollapsibleTrigger.vue'
diff --git a/src/client/components/ui/combobox/Combobox.vue b/src/client/components/ui/combobox/Combobox.vue
new file mode 100644
index 0000000..c605206
--- /dev/null
+++ b/src/client/components/ui/combobox/Combobox.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
{{ options.find(opt => opt.value === modelValue)?.label ?? placeholder }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ opt.label }}
+ ✔
+
+
+ 暂无选项
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/components/ui/context-menu/ContextMenu.vue b/src/client/components/ui/context-menu/ContextMenu.vue
new file mode 100644
index 0000000..b4c6c09
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenu.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuCheckboxItem.vue b/src/client/components/ui/context-menu/ContextMenuCheckboxItem.vue
new file mode 100644
index 0000000..b670591
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuCheckboxItem.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuContent.vue b/src/client/components/ui/context-menu/ContextMenuContent.vue
new file mode 100644
index 0000000..e8d6251
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuContent.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuGroup.vue b/src/client/components/ui/context-menu/ContextMenuGroup.vue
new file mode 100644
index 0000000..9af9b26
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuGroup.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuItem.vue b/src/client/components/ui/context-menu/ContextMenuItem.vue
new file mode 100644
index 0000000..38f16d2
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuItem.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuLabel.vue b/src/client/components/ui/context-menu/ContextMenuLabel.vue
new file mode 100644
index 0000000..80d0519
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuLabel.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuPortal.vue b/src/client/components/ui/context-menu/ContextMenuPortal.vue
new file mode 100644
index 0000000..c50a2b3
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuPortal.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuRadioGroup.vue b/src/client/components/ui/context-menu/ContextMenuRadioGroup.vue
new file mode 100644
index 0000000..6fef4fb
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuRadioGroup.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuRadioItem.vue b/src/client/components/ui/context-menu/ContextMenuRadioItem.vue
new file mode 100644
index 0000000..b46ed92
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuRadioItem.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuSeparator.vue b/src/client/components/ui/context-menu/ContextMenuSeparator.vue
new file mode 100644
index 0000000..373be5e
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuSeparator.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuShortcut.vue b/src/client/components/ui/context-menu/ContextMenuShortcut.vue
new file mode 100644
index 0000000..2631d68
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuShortcut.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuSub.vue b/src/client/components/ui/context-menu/ContextMenuSub.vue
new file mode 100644
index 0000000..875e972
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuSub.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuSubContent.vue b/src/client/components/ui/context-menu/ContextMenuSubContent.vue
new file mode 100644
index 0000000..bf83a4a
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuSubContent.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuSubTrigger.vue b/src/client/components/ui/context-menu/ContextMenuSubTrigger.vue
new file mode 100644
index 0000000..eda8800
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuSubTrigger.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/ContextMenuTrigger.vue b/src/client/components/ui/context-menu/ContextMenuTrigger.vue
new file mode 100644
index 0000000..70a8143
--- /dev/null
+++ b/src/client/components/ui/context-menu/ContextMenuTrigger.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/context-menu/index.ts b/src/client/components/ui/context-menu/index.ts
new file mode 100644
index 0000000..5919fd3
--- /dev/null
+++ b/src/client/components/ui/context-menu/index.ts
@@ -0,0 +1,14 @@
+export { default as ContextMenu } from "./ContextMenu.vue"
+export { default as ContextMenuCheckboxItem } from "./ContextMenuCheckboxItem.vue"
+export { default as ContextMenuContent } from "./ContextMenuContent.vue"
+export { default as ContextMenuGroup } from "./ContextMenuGroup.vue"
+export { default as ContextMenuItem } from "./ContextMenuItem.vue"
+export { default as ContextMenuLabel } from "./ContextMenuLabel.vue"
+export { default as ContextMenuRadioGroup } from "./ContextMenuRadioGroup.vue"
+export { default as ContextMenuRadioItem } from "./ContextMenuRadioItem.vue"
+export { default as ContextMenuSeparator } from "./ContextMenuSeparator.vue"
+export { default as ContextMenuShortcut } from "./ContextMenuShortcut.vue"
+export { default as ContextMenuSub } from "./ContextMenuSub.vue"
+export { default as ContextMenuSubContent } from "./ContextMenuSubContent.vue"
+export { default as ContextMenuSubTrigger } from "./ContextMenuSubTrigger.vue"
+export { default as ContextMenuTrigger } from "./ContextMenuTrigger.vue"
diff --git a/src/client/components/ui/dialog/Dialog.vue b/src/client/components/ui/dialog/Dialog.vue
new file mode 100644
index 0000000..bb8bf72
--- /dev/null
+++ b/src/client/components/ui/dialog/Dialog.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogClose.vue b/src/client/components/ui/dialog/DialogClose.vue
new file mode 100644
index 0000000..7574168
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogClose.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogContent.vue b/src/client/components/ui/dialog/DialogContent.vue
new file mode 100644
index 0000000..a3e4cd9
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogContent.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+ Close
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogDescription.vue b/src/client/components/ui/dialog/DialogDescription.vue
new file mode 100644
index 0000000..cc81b1d
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogDescription.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogFooter.vue b/src/client/components/ui/dialog/DialogFooter.vue
new file mode 100644
index 0000000..c3b4b55
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogFooter.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogHeader.vue b/src/client/components/ui/dialog/DialogHeader.vue
new file mode 100644
index 0000000..0fc2f5f
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogHeader.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogOverlay.vue b/src/client/components/ui/dialog/DialogOverlay.vue
new file mode 100644
index 0000000..e2bef33
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogOverlay.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogScrollContent.vue b/src/client/components/ui/dialog/DialogScrollContent.vue
new file mode 100644
index 0000000..5173953
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogScrollContent.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+ {
+ const originalEvent = event.detail.originalEvent;
+ const target = originalEvent.target as HTMLElement;
+ if (originalEvent.offsetX > target.clientWidth || originalEvent.offsetY > target.clientHeight) {
+ event.preventDefault();
+ }
+ }"
+ >
+
+
+
+
+ Close
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogTitle.vue b/src/client/components/ui/dialog/DialogTitle.vue
new file mode 100644
index 0000000..7db6de6
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogTitle.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/DialogTrigger.vue b/src/client/components/ui/dialog/DialogTrigger.vue
new file mode 100644
index 0000000..a870f66
--- /dev/null
+++ b/src/client/components/ui/dialog/DialogTrigger.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dialog/index.ts b/src/client/components/ui/dialog/index.ts
new file mode 100644
index 0000000..baa7d61
--- /dev/null
+++ b/src/client/components/ui/dialog/index.ts
@@ -0,0 +1,10 @@
+export { default as Dialog } from './Dialog.vue'
+export { default as DialogClose } from './DialogClose.vue'
+export { default as DialogContent } from './DialogContent.vue'
+export { default as DialogDescription } from './DialogDescription.vue'
+export { default as DialogFooter } from './DialogFooter.vue'
+export { default as DialogHeader } from './DialogHeader.vue'
+export { default as DialogOverlay } from './DialogOverlay.vue'
+export { default as DialogScrollContent } from './DialogScrollContent.vue'
+export { default as DialogTitle } from './DialogTitle.vue'
+export { default as DialogTrigger } from './DialogTrigger.vue'
diff --git a/src/client/components/ui/drawer/Drawer.vue b/src/client/components/ui/drawer/Drawer.vue
new file mode 100644
index 0000000..1f773cc
--- /dev/null
+++ b/src/client/components/ui/drawer/Drawer.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerClose.vue b/src/client/components/ui/drawer/DrawerClose.vue
new file mode 100644
index 0000000..3a76798
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerClose.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerContent.vue b/src/client/components/ui/drawer/DrawerContent.vue
new file mode 100644
index 0000000..623b05b
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerContent.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerDescription.vue b/src/client/components/ui/drawer/DrawerDescription.vue
new file mode 100644
index 0000000..69f8c9d
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerDescription.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerFooter.vue b/src/client/components/ui/drawer/DrawerFooter.vue
new file mode 100644
index 0000000..c6884b5
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerFooter.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerHeader.vue b/src/client/components/ui/drawer/DrawerHeader.vue
new file mode 100644
index 0000000..c26b8fe
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerHeader.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerOverlay.vue b/src/client/components/ui/drawer/DrawerOverlay.vue
new file mode 100644
index 0000000..36a8032
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerOverlay.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerTitle.vue b/src/client/components/ui/drawer/DrawerTitle.vue
new file mode 100644
index 0000000..cad79ac
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerTitle.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/DrawerTrigger.vue b/src/client/components/ui/drawer/DrawerTrigger.vue
new file mode 100644
index 0000000..f219564
--- /dev/null
+++ b/src/client/components/ui/drawer/DrawerTrigger.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/drawer/index.ts b/src/client/components/ui/drawer/index.ts
new file mode 100644
index 0000000..6e6b18f
--- /dev/null
+++ b/src/client/components/ui/drawer/index.ts
@@ -0,0 +1,9 @@
+export { default as Drawer } from './Drawer.vue'
+export { default as DrawerClose } from './DrawerClose.vue'
+export { default as DrawerContent } from './DrawerContent.vue'
+export { default as DrawerDescription } from './DrawerDescription.vue'
+export { default as DrawerFooter } from './DrawerFooter.vue'
+export { default as DrawerHeader } from './DrawerHeader.vue'
+export { default as DrawerOverlay } from './DrawerOverlay.vue'
+export { default as DrawerTitle } from './DrawerTitle.vue'
+export { default as DrawerTrigger } from './DrawerTrigger.vue'
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenu.vue b/src/client/components/ui/dropdown-menu/DropdownMenu.vue
new file mode 100644
index 0000000..e386052
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenu.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue b/src/client/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue
new file mode 100644
index 0000000..f63c967
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuContent.vue b/src/client/components/ui/dropdown-menu/DropdownMenuContent.vue
new file mode 100644
index 0000000..0aac75f
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuContent.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuGroup.vue b/src/client/components/ui/dropdown-menu/DropdownMenuGroup.vue
new file mode 100644
index 0000000..c7eb308
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuGroup.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuItem.vue b/src/client/components/ui/dropdown-menu/DropdownMenuItem.vue
new file mode 100644
index 0000000..f0a9495
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuItem.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuLabel.vue b/src/client/components/ui/dropdown-menu/DropdownMenuLabel.vue
new file mode 100644
index 0000000..61015cc
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuLabel.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue b/src/client/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue
new file mode 100644
index 0000000..d205d0b
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuRadioItem.vue b/src/client/components/ui/dropdown-menu/DropdownMenuRadioItem.vue
new file mode 100644
index 0000000..3d6d908
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuRadioItem.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuSeparator.vue b/src/client/components/ui/dropdown-menu/DropdownMenuSeparator.vue
new file mode 100644
index 0000000..5d3fde6
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuSeparator.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuShortcut.vue b/src/client/components/ui/dropdown-menu/DropdownMenuShortcut.vue
new file mode 100644
index 0000000..1bcbb88
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuShortcut.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuSub.vue b/src/client/components/ui/dropdown-menu/DropdownMenuSub.vue
new file mode 100644
index 0000000..7329bca
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuSub.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuSubContent.vue b/src/client/components/ui/dropdown-menu/DropdownMenuSubContent.vue
new file mode 100644
index 0000000..bb70eef
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuSubContent.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue b/src/client/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
new file mode 100644
index 0000000..c641d0a
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/DropdownMenuTrigger.vue b/src/client/components/ui/dropdown-menu/DropdownMenuTrigger.vue
new file mode 100644
index 0000000..7bc7339
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/DropdownMenuTrigger.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/dropdown-menu/index.ts b/src/client/components/ui/dropdown-menu/index.ts
new file mode 100644
index 0000000..f488d39
--- /dev/null
+++ b/src/client/components/ui/dropdown-menu/index.ts
@@ -0,0 +1,16 @@
+export { default as DropdownMenu } from './DropdownMenu.vue'
+
+export { default as DropdownMenuCheckboxItem } from './DropdownMenuCheckboxItem.vue'
+export { default as DropdownMenuContent } from './DropdownMenuContent.vue'
+export { default as DropdownMenuGroup } from './DropdownMenuGroup.vue'
+export { default as DropdownMenuItem } from './DropdownMenuItem.vue'
+export { default as DropdownMenuLabel } from './DropdownMenuLabel.vue'
+export { default as DropdownMenuRadioGroup } from './DropdownMenuRadioGroup.vue'
+export { default as DropdownMenuRadioItem } from './DropdownMenuRadioItem.vue'
+export { default as DropdownMenuSeparator } from './DropdownMenuSeparator.vue'
+export { default as DropdownMenuShortcut } from './DropdownMenuShortcut.vue'
+export { default as DropdownMenuSub } from './DropdownMenuSub.vue'
+export { default as DropdownMenuSubContent } from './DropdownMenuSubContent.vue'
+export { default as DropdownMenuSubTrigger } from './DropdownMenuSubTrigger.vue'
+export { default as DropdownMenuTrigger } from './DropdownMenuTrigger.vue'
+export { DropdownMenuPortal } from 'reka-ui'
diff --git a/src/client/components/ui/input/Input.vue b/src/client/components/ui/input/Input.vue
new file mode 100644
index 0000000..14f5e71
--- /dev/null
+++ b/src/client/components/ui/input/Input.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
diff --git a/src/client/components/ui/input/index.ts b/src/client/components/ui/input/index.ts
new file mode 100644
index 0000000..a691dd6
--- /dev/null
+++ b/src/client/components/ui/input/index.ts
@@ -0,0 +1 @@
+export { default as Input } from './Input.vue'
diff --git a/src/client/components/ui/label/Label.vue b/src/client/components/ui/label/Label.vue
new file mode 100644
index 0000000..3199723
--- /dev/null
+++ b/src/client/components/ui/label/Label.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/label/index.ts b/src/client/components/ui/label/index.ts
new file mode 100644
index 0000000..572c2f0
--- /dev/null
+++ b/src/client/components/ui/label/index.ts
@@ -0,0 +1 @@
+export { default as Label } from './Label.vue'
diff --git a/src/client/components/ui/navigation-menu/NavigationMenu.vue b/src/client/components/ui/navigation-menu/NavigationMenu.vue
new file mode 100644
index 0000000..f88da5f
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenu.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/NavigationMenuContent.vue b/src/client/components/ui/navigation-menu/NavigationMenuContent.vue
new file mode 100644
index 0000000..a12ab1a
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenuContent.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/NavigationMenuIndicator.vue b/src/client/components/ui/navigation-menu/NavigationMenuIndicator.vue
new file mode 100644
index 0000000..5d48e88
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenuIndicator.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/NavigationMenuItem.vue b/src/client/components/ui/navigation-menu/NavigationMenuItem.vue
new file mode 100644
index 0000000..03c3e5a
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenuItem.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/NavigationMenuLink.vue b/src/client/components/ui/navigation-menu/NavigationMenuLink.vue
new file mode 100644
index 0000000..7a13b24
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenuLink.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/NavigationMenuList.vue b/src/client/components/ui/navigation-menu/NavigationMenuList.vue
new file mode 100644
index 0000000..f6248e6
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenuList.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/NavigationMenuTrigger.vue b/src/client/components/ui/navigation-menu/NavigationMenuTrigger.vue
new file mode 100644
index 0000000..32f72da
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenuTrigger.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/NavigationMenuViewport.vue b/src/client/components/ui/navigation-menu/NavigationMenuViewport.vue
new file mode 100644
index 0000000..5fa1210
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/NavigationMenuViewport.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/navigation-menu/index.ts b/src/client/components/ui/navigation-menu/index.ts
new file mode 100644
index 0000000..90e0e2d
--- /dev/null
+++ b/src/client/components/ui/navigation-menu/index.ts
@@ -0,0 +1,14 @@
+import { cva } from 'class-variance-authority'
+
+export { default as NavigationMenu } from './NavigationMenu.vue'
+export { default as NavigationMenuContent } from './NavigationMenuContent.vue'
+export { default as NavigationMenuIndicator } from './NavigationMenuIndicator.vue'
+export { default as NavigationMenuItem } from './NavigationMenuItem.vue'
+export { default as NavigationMenuLink } from './NavigationMenuLink.vue'
+export { default as NavigationMenuList } from './NavigationMenuList.vue'
+export { default as NavigationMenuTrigger } from './NavigationMenuTrigger.vue'
+export { default as NavigationMenuViewport } from './NavigationMenuViewport.vue'
+
+export const navigationMenuTriggerStyle = cva(
+ 'group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1',
+)
diff --git a/src/client/components/ui/pin-input/PinInput.vue b/src/client/components/ui/pin-input/PinInput.vue
new file mode 100644
index 0000000..1136555
--- /dev/null
+++ b/src/client/components/ui/pin-input/PinInput.vue
@@ -0,0 +1,63 @@
+
+
+
+ onInput(e, i-1)"
+ @keydown="e => onKeydown(e, i-1)"
+ autocomplete="one-time-code"
+ inputmode="numeric"
+ />
+
+
\ No newline at end of file
diff --git a/src/client/components/ui/progress/Progress.vue b/src/client/components/ui/progress/Progress.vue
new file mode 100644
index 0000000..57f2db9
--- /dev/null
+++ b/src/client/components/ui/progress/Progress.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/progress/index.ts b/src/client/components/ui/progress/index.ts
new file mode 100644
index 0000000..8598b96
--- /dev/null
+++ b/src/client/components/ui/progress/index.ts
@@ -0,0 +1 @@
+export { default as Progress } from "./Progress.vue"
diff --git a/src/client/components/ui/radio-group/RadioGroup.vue b/src/client/components/ui/radio-group/RadioGroup.vue
new file mode 100644
index 0000000..f9eccd2
--- /dev/null
+++ b/src/client/components/ui/radio-group/RadioGroup.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/radio-group/RadioGroupItem.vue b/src/client/components/ui/radio-group/RadioGroupItem.vue
new file mode 100644
index 0000000..b018106
--- /dev/null
+++ b/src/client/components/ui/radio-group/RadioGroupItem.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/radio-group/index.ts b/src/client/components/ui/radio-group/index.ts
new file mode 100644
index 0000000..7cb0e3b
--- /dev/null
+++ b/src/client/components/ui/radio-group/index.ts
@@ -0,0 +1,2 @@
+export { default as RadioGroup } from "./RadioGroup.vue"
+export { default as RadioGroupItem } from "./RadioGroupItem.vue"
diff --git a/src/client/components/ui/scroll-area/ScrollArea.vue b/src/client/components/ui/scroll-area/ScrollArea.vue
new file mode 100644
index 0000000..21d25d7
--- /dev/null
+++ b/src/client/components/ui/scroll-area/ScrollArea.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/scroll-area/ScrollBar.vue b/src/client/components/ui/scroll-area/ScrollBar.vue
new file mode 100644
index 0000000..a0b6f9b
--- /dev/null
+++ b/src/client/components/ui/scroll-area/ScrollBar.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/scroll-area/index.ts b/src/client/components/ui/scroll-area/index.ts
new file mode 100644
index 0000000..c416759
--- /dev/null
+++ b/src/client/components/ui/scroll-area/index.ts
@@ -0,0 +1,2 @@
+export { default as ScrollArea } from "./ScrollArea.vue"
+export { default as ScrollBar } from "./ScrollBar.vue"
diff --git a/src/client/components/ui/select/Select.vue b/src/client/components/ui/select/Select.vue
new file mode 100644
index 0000000..dc1f83d
--- /dev/null
+++ b/src/client/components/ui/select/Select.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectContent.vue b/src/client/components/ui/select/SelectContent.vue
new file mode 100644
index 0000000..71a6061
--- /dev/null
+++ b/src/client/components/ui/select/SelectContent.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectGroup.vue b/src/client/components/ui/select/SelectGroup.vue
new file mode 100644
index 0000000..4f36d92
--- /dev/null
+++ b/src/client/components/ui/select/SelectGroup.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectItem.vue b/src/client/components/ui/select/SelectItem.vue
new file mode 100644
index 0000000..1cdfa63
--- /dev/null
+++ b/src/client/components/ui/select/SelectItem.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectItemText.vue b/src/client/components/ui/select/SelectItemText.vue
new file mode 100644
index 0000000..cff32ac
--- /dev/null
+++ b/src/client/components/ui/select/SelectItemText.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectLabel.vue b/src/client/components/ui/select/SelectLabel.vue
new file mode 100644
index 0000000..37a84dd
--- /dev/null
+++ b/src/client/components/ui/select/SelectLabel.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectScrollDownButton.vue b/src/client/components/ui/select/SelectScrollDownButton.vue
new file mode 100644
index 0000000..f03ff40
--- /dev/null
+++ b/src/client/components/ui/select/SelectScrollDownButton.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectScrollUpButton.vue b/src/client/components/ui/select/SelectScrollUpButton.vue
new file mode 100644
index 0000000..078331b
--- /dev/null
+++ b/src/client/components/ui/select/SelectScrollUpButton.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectSeparator.vue b/src/client/components/ui/select/SelectSeparator.vue
new file mode 100644
index 0000000..5ec7155
--- /dev/null
+++ b/src/client/components/ui/select/SelectSeparator.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectTrigger.vue b/src/client/components/ui/select/SelectTrigger.vue
new file mode 100644
index 0000000..5ffd488
--- /dev/null
+++ b/src/client/components/ui/select/SelectTrigger.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/SelectValue.vue b/src/client/components/ui/select/SelectValue.vue
new file mode 100644
index 0000000..f198b96
--- /dev/null
+++ b/src/client/components/ui/select/SelectValue.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/select/index.ts b/src/client/components/ui/select/index.ts
new file mode 100644
index 0000000..31b9294
--- /dev/null
+++ b/src/client/components/ui/select/index.ts
@@ -0,0 +1,11 @@
+export { default as Select } from './Select.vue'
+export { default as SelectContent } from './SelectContent.vue'
+export { default as SelectGroup } from './SelectGroup.vue'
+export { default as SelectItem } from './SelectItem.vue'
+export { default as SelectItemText } from './SelectItemText.vue'
+export { default as SelectLabel } from './SelectLabel.vue'
+export { default as SelectScrollDownButton } from './SelectScrollDownButton.vue'
+export { default as SelectScrollUpButton } from './SelectScrollUpButton.vue'
+export { default as SelectSeparator } from './SelectSeparator.vue'
+export { default as SelectTrigger } from './SelectTrigger.vue'
+export { default as SelectValue } from './SelectValue.vue'
diff --git a/src/client/components/ui/separator/Separator.vue b/src/client/components/ui/separator/Separator.vue
new file mode 100644
index 0000000..d397e08
--- /dev/null
+++ b/src/client/components/ui/separator/Separator.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/src/client/components/ui/separator/index.ts b/src/client/components/ui/separator/index.ts
new file mode 100644
index 0000000..2287bcb
--- /dev/null
+++ b/src/client/components/ui/separator/index.ts
@@ -0,0 +1 @@
+export { default as Separator } from './Separator.vue'
diff --git a/src/client/components/ui/sheet/Sheet.vue b/src/client/components/ui/sheet/Sheet.vue
new file mode 100644
index 0000000..e2a3374
--- /dev/null
+++ b/src/client/components/ui/sheet/Sheet.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetClose.vue b/src/client/components/ui/sheet/SheetClose.vue
new file mode 100644
index 0000000..05077ea
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetClose.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetContent.vue b/src/client/components/ui/sheet/SheetContent.vue
new file mode 100644
index 0000000..8e55613
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetContent.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+ Close
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetDescription.vue b/src/client/components/ui/sheet/SheetDescription.vue
new file mode 100644
index 0000000..e3682d3
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetDescription.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetFooter.vue b/src/client/components/ui/sheet/SheetFooter.vue
new file mode 100644
index 0000000..d2ac823
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetFooter.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetHeader.vue b/src/client/components/ui/sheet/SheetHeader.vue
new file mode 100644
index 0000000..ab20557
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetHeader.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetOverlay.vue b/src/client/components/ui/sheet/SheetOverlay.vue
new file mode 100644
index 0000000..3f27a86
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetOverlay.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetTitle.vue b/src/client/components/ui/sheet/SheetTitle.vue
new file mode 100644
index 0000000..262fb5b
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetTitle.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/SheetTrigger.vue b/src/client/components/ui/sheet/SheetTrigger.vue
new file mode 100644
index 0000000..5981f8e
--- /dev/null
+++ b/src/client/components/ui/sheet/SheetTrigger.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sheet/index.ts b/src/client/components/ui/sheet/index.ts
new file mode 100644
index 0000000..ee33431
--- /dev/null
+++ b/src/client/components/ui/sheet/index.ts
@@ -0,0 +1,8 @@
+export { default as Sheet } from './Sheet.vue'
+export { default as SheetClose } from './SheetClose.vue'
+export { default as SheetContent } from './SheetContent.vue'
+export { default as SheetDescription } from './SheetDescription.vue'
+export { default as SheetFooter } from './SheetFooter.vue'
+export { default as SheetHeader } from './SheetHeader.vue'
+export { default as SheetTitle } from './SheetTitle.vue'
+export { default as SheetTrigger } from './SheetTrigger.vue'
diff --git a/src/client/components/ui/sidebar/Sidebar.vue b/src/client/components/ui/sidebar/Sidebar.vue
new file mode 100644
index 0000000..c75db5d
--- /dev/null
+++ b/src/client/components/ui/sidebar/Sidebar.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarContent.vue b/src/client/components/ui/sidebar/SidebarContent.vue
new file mode 100644
index 0000000..3b7536c
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarContent.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarFooter.vue b/src/client/components/ui/sidebar/SidebarFooter.vue
new file mode 100644
index 0000000..c324dff
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarFooter.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarGroup.vue b/src/client/components/ui/sidebar/SidebarGroup.vue
new file mode 100644
index 0000000..57a099f
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarGroup.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarGroupAction.vue b/src/client/components/ui/sidebar/SidebarGroupAction.vue
new file mode 100644
index 0000000..eb9a3ec
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarGroupAction.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarGroupContent.vue b/src/client/components/ui/sidebar/SidebarGroupContent.vue
new file mode 100644
index 0000000..5d5e2dd
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarGroupContent.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarGroupLabel.vue b/src/client/components/ui/sidebar/SidebarGroupLabel.vue
new file mode 100644
index 0000000..8e1c1e3
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarGroupLabel.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarHeader.vue b/src/client/components/ui/sidebar/SidebarHeader.vue
new file mode 100644
index 0000000..00673f7
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarHeader.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarInput.vue b/src/client/components/ui/sidebar/SidebarInput.vue
new file mode 100644
index 0000000..ecaa977
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarInput.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarInset.vue b/src/client/components/ui/sidebar/SidebarInset.vue
new file mode 100644
index 0000000..386c622
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarInset.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenu.vue b/src/client/components/ui/sidebar/SidebarMenu.vue
new file mode 100644
index 0000000..59ec071
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenu.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuAction.vue b/src/client/components/ui/sidebar/SidebarMenuAction.vue
new file mode 100644
index 0000000..4852f28
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuAction.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuBadge.vue b/src/client/components/ui/sidebar/SidebarMenuBadge.vue
new file mode 100644
index 0000000..3984b33
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuBadge.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuButton.vue b/src/client/components/ui/sidebar/SidebarMenuButton.vue
new file mode 100644
index 0000000..1d934b2
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuButton.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ tooltip }}
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuButtonChild.vue b/src/client/components/ui/sidebar/SidebarMenuButtonChild.vue
new file mode 100644
index 0000000..82998df
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuButtonChild.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuItem.vue b/src/client/components/ui/sidebar/SidebarMenuItem.vue
new file mode 100644
index 0000000..b7b4d3f
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuItem.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuSkeleton.vue b/src/client/components/ui/sidebar/SidebarMenuSkeleton.vue
new file mode 100644
index 0000000..6302c35
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuSkeleton.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuSub.vue b/src/client/components/ui/sidebar/SidebarMenuSub.vue
new file mode 100644
index 0000000..11aec64
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuSub.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuSubButton.vue b/src/client/components/ui/sidebar/SidebarMenuSubButton.vue
new file mode 100644
index 0000000..f9fae8f
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuSubButton.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarMenuSubItem.vue b/src/client/components/ui/sidebar/SidebarMenuSubItem.vue
new file mode 100644
index 0000000..f0c35bc
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarMenuSubItem.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarProvider.vue b/src/client/components/ui/sidebar/SidebarProvider.vue
new file mode 100644
index 0000000..2e08dec
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarProvider.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarRail.vue b/src/client/components/ui/sidebar/SidebarRail.vue
new file mode 100644
index 0000000..07e59be
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarRail.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarSeparator.vue b/src/client/components/ui/sidebar/SidebarSeparator.vue
new file mode 100644
index 0000000..1803bff
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarSeparator.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/sidebar/SidebarTrigger.vue b/src/client/components/ui/sidebar/SidebarTrigger.vue
new file mode 100644
index 0000000..59f29b9
--- /dev/null
+++ b/src/client/components/ui/sidebar/SidebarTrigger.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+ Toggle Sidebar
+
+
diff --git a/src/client/components/ui/sidebar/index.ts b/src/client/components/ui/sidebar/index.ts
new file mode 100644
index 0000000..48da274
--- /dev/null
+++ b/src/client/components/ui/sidebar/index.ts
@@ -0,0 +1,60 @@
+import type { VariantProps } from 'class-variance-authority'
+import type { HTMLAttributes } from 'vue'
+import { cva } from 'class-variance-authority'
+
+export interface SidebarProps {
+ side?: 'left' | 'right'
+ variant?: 'sidebar' | 'floating' | 'inset'
+ collapsible?: 'offcanvas' | 'icon' | 'none'
+ class?: HTMLAttributes['class']
+}
+
+export { default as Sidebar } from './Sidebar.vue'
+export { default as SidebarContent } from './SidebarContent.vue'
+export { default as SidebarFooter } from './SidebarFooter.vue'
+export { default as SidebarGroup } from './SidebarGroup.vue'
+export { default as SidebarGroupAction } from './SidebarGroupAction.vue'
+export { default as SidebarGroupContent } from './SidebarGroupContent.vue'
+export { default as SidebarGroupLabel } from './SidebarGroupLabel.vue'
+export { default as SidebarHeader } from './SidebarHeader.vue'
+export { default as SidebarInput } from './SidebarInput.vue'
+export { default as SidebarInset } from './SidebarInset.vue'
+export { default as SidebarMenu } from './SidebarMenu.vue'
+export { default as SidebarMenuAction } from './SidebarMenuAction.vue'
+export { default as SidebarMenuBadge } from './SidebarMenuBadge.vue'
+export { default as SidebarMenuButton } from './SidebarMenuButton.vue'
+export { default as SidebarMenuItem } from './SidebarMenuItem.vue'
+export { default as SidebarMenuSkeleton } from './SidebarMenuSkeleton.vue'
+export { default as SidebarMenuSub } from './SidebarMenuSub.vue'
+export { default as SidebarMenuSubButton } from './SidebarMenuSubButton.vue'
+export { default as SidebarMenuSubItem } from './SidebarMenuSubItem.vue'
+export { default as SidebarProvider } from './SidebarProvider.vue'
+export { default as SidebarRail } from './SidebarRail.vue'
+export { default as SidebarSeparator } from './SidebarSeparator.vue'
+export { default as SidebarTrigger } from './SidebarTrigger.vue'
+
+export { useSidebar } from './utils'
+
+export const sidebarMenuButtonVariants = cva(
+ 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
+ {
+ variants: {
+ variant: {
+ default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
+ outline:
+ 'bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]',
+ },
+ size: {
+ default: 'h-8 text-sm',
+ sm: 'h-7 text-xs',
+ lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+)
+
+export type SidebarMenuButtonVariants = VariantProps
diff --git a/src/client/components/ui/sidebar/utils.ts b/src/client/components/ui/sidebar/utils.ts
new file mode 100644
index 0000000..6edb140
--- /dev/null
+++ b/src/client/components/ui/sidebar/utils.ts
@@ -0,0 +1,19 @@
+import type { ComputedRef, Ref } from 'vue'
+import { createContext } from 'reka-ui'
+
+export const SIDEBAR_COOKIE_NAME = 'sidebar_state'
+export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
+export const SIDEBAR_WIDTH = '16rem'
+export const SIDEBAR_WIDTH_MOBILE = '18rem'
+export const SIDEBAR_WIDTH_ICON = '3rem'
+export const SIDEBAR_KEYBOARD_SHORTCUT = 'b'
+
+export const [useSidebar, provideSidebarContext] = createContext<{
+ state: ComputedRef<'expanded' | 'collapsed'>
+ open: Ref
+ setOpen: (value: boolean) => void
+ isMobile: Ref
+ openMobile: Ref
+ setOpenMobile: (value: boolean) => void
+ toggleSidebar: () => void
+}>('Sidebar')
diff --git a/src/client/components/ui/skeleton/Skeleton.vue b/src/client/components/ui/skeleton/Skeleton.vue
new file mode 100644
index 0000000..153fe2c
--- /dev/null
+++ b/src/client/components/ui/skeleton/Skeleton.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/src/client/components/ui/skeleton/index.ts b/src/client/components/ui/skeleton/index.ts
new file mode 100644
index 0000000..be21fad
--- /dev/null
+++ b/src/client/components/ui/skeleton/index.ts
@@ -0,0 +1 @@
+export { default as Skeleton } from './Skeleton.vue'
diff --git a/src/client/components/ui/sonner/Sonner.vue b/src/client/components/ui/sonner/Sonner.vue
new file mode 100644
index 0000000..1c70dca
--- /dev/null
+++ b/src/client/components/ui/sonner/Sonner.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/client/components/ui/sonner/index.ts b/src/client/components/ui/sonner/index.ts
new file mode 100644
index 0000000..6673112
--- /dev/null
+++ b/src/client/components/ui/sonner/index.ts
@@ -0,0 +1 @@
+export { default as Toaster } from "./Sonner.vue"
diff --git a/src/client/components/ui/switch/Switch.vue b/src/client/components/ui/switch/Switch.vue
new file mode 100644
index 0000000..36741ca
--- /dev/null
+++ b/src/client/components/ui/switch/Switch.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/switch/index.ts b/src/client/components/ui/switch/index.ts
new file mode 100644
index 0000000..cc081f3
--- /dev/null
+++ b/src/client/components/ui/switch/index.ts
@@ -0,0 +1 @@
+export { default as Switch } from "./Switch.vue"
diff --git a/src/client/components/ui/table/Table.vue b/src/client/components/ui/table/Table.vue
new file mode 100644
index 0000000..669b0d4
--- /dev/null
+++ b/src/client/components/ui/table/Table.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableBody.vue b/src/client/components/ui/table/TableBody.vue
new file mode 100644
index 0000000..af688cb
--- /dev/null
+++ b/src/client/components/ui/table/TableBody.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableCaption.vue b/src/client/components/ui/table/TableCaption.vue
new file mode 100644
index 0000000..e6ab397
--- /dev/null
+++ b/src/client/components/ui/table/TableCaption.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableCell.vue b/src/client/components/ui/table/TableCell.vue
new file mode 100644
index 0000000..841557d
--- /dev/null
+++ b/src/client/components/ui/table/TableCell.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableEmpty.vue b/src/client/components/ui/table/TableEmpty.vue
new file mode 100644
index 0000000..4ea2da7
--- /dev/null
+++ b/src/client/components/ui/table/TableEmpty.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableFooter.vue b/src/client/components/ui/table/TableFooter.vue
new file mode 100644
index 0000000..8055c55
--- /dev/null
+++ b/src/client/components/ui/table/TableFooter.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableHead.vue b/src/client/components/ui/table/TableHead.vue
new file mode 100644
index 0000000..a2ca70e
--- /dev/null
+++ b/src/client/components/ui/table/TableHead.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableHeader.vue b/src/client/components/ui/table/TableHeader.vue
new file mode 100644
index 0000000..0b0f481
--- /dev/null
+++ b/src/client/components/ui/table/TableHeader.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/TableRow.vue b/src/client/components/ui/table/TableRow.vue
new file mode 100644
index 0000000..5befaef
--- /dev/null
+++ b/src/client/components/ui/table/TableRow.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/table/index.ts b/src/client/components/ui/table/index.ts
new file mode 100644
index 0000000..2b4ce39
--- /dev/null
+++ b/src/client/components/ui/table/index.ts
@@ -0,0 +1,9 @@
+export { default as Table } from './Table.vue'
+export { default as TableBody } from './TableBody.vue'
+export { default as TableCaption } from './TableCaption.vue'
+export { default as TableCell } from './TableCell.vue'
+export { default as TableEmpty } from './TableEmpty.vue'
+export { default as TableFooter } from './TableFooter.vue'
+export { default as TableHead } from './TableHead.vue'
+export { default as TableHeader } from './TableHeader.vue'
+export { default as TableRow } from './TableRow.vue'
diff --git a/src/client/components/ui/table/utils.ts b/src/client/components/ui/table/utils.ts
new file mode 100644
index 0000000..d812d4b
--- /dev/null
+++ b/src/client/components/ui/table/utils.ts
@@ -0,0 +1,9 @@
+import type { Updater } from '@tanstack/vue-table'
+import type { Ref } from 'vue'
+
+export function valueUpdater>(updaterOrValue: T, ref: Ref) {
+ ref.value
+ = typeof updaterOrValue === 'function'
+ ? updaterOrValue(ref.value)
+ : updaterOrValue
+}
diff --git a/src/client/components/ui/tabs/Tabs.vue b/src/client/components/ui/tabs/Tabs.vue
new file mode 100644
index 0000000..cdfd48c
--- /dev/null
+++ b/src/client/components/ui/tabs/Tabs.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tabs/TabsContent.vue b/src/client/components/ui/tabs/TabsContent.vue
new file mode 100644
index 0000000..a07f7a7
--- /dev/null
+++ b/src/client/components/ui/tabs/TabsContent.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tabs/TabsList.vue b/src/client/components/ui/tabs/TabsList.vue
new file mode 100644
index 0000000..4df3287
--- /dev/null
+++ b/src/client/components/ui/tabs/TabsList.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tabs/TabsTrigger.vue b/src/client/components/ui/tabs/TabsTrigger.vue
new file mode 100644
index 0000000..4230433
--- /dev/null
+++ b/src/client/components/ui/tabs/TabsTrigger.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tabs/index.ts b/src/client/components/ui/tabs/index.ts
new file mode 100644
index 0000000..a5e58dc
--- /dev/null
+++ b/src/client/components/ui/tabs/index.ts
@@ -0,0 +1,4 @@
+export { default as Tabs } from './Tabs.vue'
+export { default as TabsContent } from './TabsContent.vue'
+export { default as TabsList } from './TabsList.vue'
+export { default as TabsTrigger } from './TabsTrigger.vue'
diff --git a/src/client/components/ui/textarea/Textarea.vue b/src/client/components/ui/textarea/Textarea.vue
new file mode 100644
index 0000000..790f10c
--- /dev/null
+++ b/src/client/components/ui/textarea/Textarea.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/src/client/components/ui/textarea/index.ts b/src/client/components/ui/textarea/index.ts
new file mode 100644
index 0000000..8ce91e9
--- /dev/null
+++ b/src/client/components/ui/textarea/index.ts
@@ -0,0 +1 @@
+export { default as Textarea } from "./Textarea.vue"
diff --git a/src/client/components/ui/tooltip/Tooltip.vue b/src/client/components/ui/tooltip/Tooltip.vue
new file mode 100644
index 0000000..3ebeecc
--- /dev/null
+++ b/src/client/components/ui/tooltip/Tooltip.vue
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tooltip/TooltipContent.vue b/src/client/components/ui/tooltip/TooltipContent.vue
new file mode 100644
index 0000000..62712e6
--- /dev/null
+++ b/src/client/components/ui/tooltip/TooltipContent.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tooltip/TooltipProvider.vue b/src/client/components/ui/tooltip/TooltipProvider.vue
new file mode 100644
index 0000000..4b4203e
--- /dev/null
+++ b/src/client/components/ui/tooltip/TooltipProvider.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tooltip/TooltipTrigger.vue b/src/client/components/ui/tooltip/TooltipTrigger.vue
new file mode 100644
index 0000000..36253c6
--- /dev/null
+++ b/src/client/components/ui/tooltip/TooltipTrigger.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/src/client/components/ui/tooltip/index.ts b/src/client/components/ui/tooltip/index.ts
new file mode 100644
index 0000000..5ab9653
--- /dev/null
+++ b/src/client/components/ui/tooltip/index.ts
@@ -0,0 +1,4 @@
+export { default as Tooltip } from './Tooltip.vue'
+export { default as TooltipContent } from './TooltipContent.vue'
+export { default as TooltipProvider } from './TooltipProvider.vue'
+export { default as TooltipTrigger } from './TooltipTrigger.vue'
diff --git a/src/client/composables/useWallpaper.ts b/src/client/composables/useWallpaper.ts
new file mode 100644
index 0000000..57b5aac
--- /dev/null
+++ b/src/client/composables/useWallpaper.ts
@@ -0,0 +1,131 @@
+import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
+import { safeLocalStorage } from '../utils/safeStorage'
+
+export function useWallpaper() {
+ // 壁纸相关状态
+ const wallpaperLoaded = ref(false)
+ const wallpaperError = ref(false)
+ const customBgImage = ref(safeLocalStorage.getItem('customBgImage', null))
+ const wallpaperOpacity = ref(parseInt(safeLocalStorage.getItem('wallpaperOpacity', '80')))
+ const wallpaperBlur = ref(parseInt(safeLocalStorage.getItem('wallpaperBlur', '0')))
+ const bingDailyWallpaperUrl = ref("https://bing.img.run/uhd.php")
+ const bingRandomWallpaperUrl = ref("https://bing.img.run/rand_uhd.php")
+
+ // 确保壁纸类型已设置,首次访问时默认为纯净模式
+ const ensureWallpaperType = () => {
+ if (typeof window !== 'undefined' && window.localStorage) {
+ if (!localStorage.getItem('wallpaperType')) {
+ localStorage.setItem('wallpaperType', 'pure')
+ console.log('首次访问,壁纸类型设置为纯净模式')
+ }
+ }
+ }
+
+ // 当前壁纸URL的计算属性
+ const currentWallpaperUrl = computed(() => {
+ // 确保壁纸类型已设置
+ ensureWallpaperType()
+
+ const wallpaperType = safeLocalStorage.getItem('wallpaperType', 'pure')
+
+ if (wallpaperType === 'bing-daily') {
+ return bingDailyWallpaperUrl.value
+ } else if (wallpaperType === 'bing-random') {
+ return bingRandomWallpaperUrl.value
+ } else if (wallpaperType === 'custom' && customBgImage.value) {
+ return customBgImage.value
+ }
+
+ return ''
+ })
+
+ // 壁纸加载处理函数
+ const handleWallpaperLoaded = () => {
+ wallpaperLoaded.value = true
+ wallpaperError.value = false
+ console.log('壁纸加载成功')
+ }
+
+ // 壁纸加载错误处理函数
+ const handleWallpaperError = () => {
+ wallpaperError.value = true
+ console.error('壁纸加载失败')
+ }
+
+ // 计算壁纸样式
+ const wallpaperStyle = computed(() => {
+ return {
+ opacity: wallpaperOpacity.value / 100,
+ filter: `blur(${wallpaperBlur.value}px)`
+ }
+ })
+
+ // 设置壁纸变化监听器
+ const setupWallpaperListener = () => {
+ // 监听壁纸设置变化事件
+ window.addEventListener('wallpaper-settings-changed', (event: CustomEvent) => {
+ const { type, value } = event.detail
+ if (type === 'opacity') {
+ wallpaperOpacity.value = value
+ } else if (type === 'blur') {
+ wallpaperBlur.value = value
+ }
+ })
+
+ // 监听壁纸刷新事件
+ window.addEventListener('wallpaper-refresh', (event: CustomEvent) => {
+ const { timestamp } = event.detail
+ // 添加时间戳参数以强制刷新缓存
+ if (bingDailyWallpaperUrl.value.includes('?')) {
+ bingDailyWallpaperUrl.value = bingDailyWallpaperUrl.value.split('?')[0] + '?t=' + timestamp
+ } else {
+ bingDailyWallpaperUrl.value = bingDailyWallpaperUrl.value + '?t=' + timestamp
+ }
+
+ if (bingRandomWallpaperUrl.value.includes('?')) {
+ bingRandomWallpaperUrl.value = bingRandomWallpaperUrl.value.split('?')[0] + '?t=' + timestamp
+ } else {
+ bingRandomWallpaperUrl.value = bingRandomWallpaperUrl.value + '?t=' + timestamp
+ }
+ })
+ }
+
+ // 移除壁纸变化监听器
+ const removeWallpaperListener = () => {
+ window.removeEventListener('wallpaper-settings-changed', (event: CustomEvent) => {
+ const { type, value } = event.detail
+ })
+
+ window.removeEventListener('wallpaper-changed', () => {})
+
+ window.removeEventListener('wallpaper-refresh', (event: CustomEvent) => {
+ const { timestamp } = event.detail
+ })
+ }
+
+ onMounted(() => {
+ // 确保壁纸类型已设置
+ ensureWallpaperType()
+
+ // 设置壁纸变化监听器
+ setupWallpaperListener()
+ })
+
+ onBeforeUnmount(() => {
+ // 移除壁纸变化监听器
+ removeWallpaperListener()
+ })
+
+ return {
+ wallpaperLoaded,
+ wallpaperError,
+ customBgImage,
+ wallpaperOpacity,
+ wallpaperBlur,
+ currentWallpaperUrl,
+ wallpaperStyle,
+ handleWallpaperLoaded,
+ handleWallpaperError,
+ ensureWallpaperType
+ }
+}
\ No newline at end of file
diff --git a/src/client/data/aiSites.ts b/src/client/data/aiSites.ts
new file mode 100644
index 0000000..8397056
--- /dev/null
+++ b/src/client/data/aiSites.ts
@@ -0,0 +1,125 @@
+// AI类网站数据
+import { FeaturedSite } from '../utils/featuredSites';
+
+export const aiSites: FeaturedSite[] = [
+ {
+ id: "chatgpt",
+ url: "https://chat.openai.com",
+ title: "ChatGPT",
+ description: "OpenAI开发的对话式AI助手,可回答问题和提供帮助",
+ detailedDescription: "ChatGPT是由OpenAI开发的先进对话式AI助手,基于GPT(生成式预训练变换器)模型。它能够理解和生成自然语言,回答问题,提供解释,创作内容,辅助编程等。ChatGPT可以记住对话上下文,进行连贯的交流,适应用户的需求和风格。它在教育、创意写作、编程辅助、信息查询等方面有广泛应用。ChatGPT不断通过更新迭代提升能力,如GPT-4模型带来了更强的理解力和创造力。对于需要智能对话助手的个人和专业用户来说,ChatGPT提供了强大而灵活的AI支持。",
+ category: "AI",
+ tags: ["AI助手", "对话", "GPT"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "midjourney",
+ url: "https://www.midjourney.com",
+ title: "Midjourney",
+ description: "AI图像生成工具,通过文本描述创建艺术作品",
+ detailedDescription: "Midjourney是一个强大的AI图像生成工具,允许用户通过文本提示创建高质量的艺术作品。它使用深度学习技术将文字描述转化为视觉图像,风格多样,从写实到抽象都能呈现。Midjourney的操作基于Discord平台,用户通过简单的命令与AI交互。它支持参数调整,用户可以控制图像的风格、比例、细节程度等。Midjourney定期更新模型,不断提升图像质量和多样性。对于设计师、艺术家、创意工作者和内容创作者来说,Midjourney是一个激发创意、辅助设计的强大工具。",
+ category: "AI",
+ tags: ["图像生成", "艺术", "创意"],
+ rating: 4.8,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "claude",
+ url: "https://claude.ai",
+ title: "Claude",
+ description: "Anthropic开发的AI助手,专注于有用、无害和诚实的对话",
+ detailedDescription: "Claude是由Anthropic开发的AI助手,以其自然的对话能力和负责任的回应而著称。它被设计为有用、无害和诚实,能够理解复杂指令并提供深思熟虑的回答。Claude擅长长文本处理,可以分析和总结大量内容,适合文档审阅和研究辅助。它支持多种语言,并能理解上下文,进行连贯的对话。Claude特别注重安全性和减少有害输出,适合需要可靠AI助手的专业环境。对于寻求高质量对话体验和内容创作支持的用户来说,Claude提供了平衡的智能和责任感。",
+ category: "AI",
+ tags: ["AI助手", "对话", "内容创作"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "perplexity",
+ url: "https://www.perplexity.ai",
+ title: "Perplexity AI",
+ description: "AI驱动的搜索引擎,提供有来源的答案和实时信息",
+ detailedDescription: "Perplexity AI是一个创新的AI搜索引擎,结合了大语言模型和网络搜索功能,为用户提供有来源依据的答案。它不仅回答问题,还提供相关信息的链接和引用,增强可信度和透明度。Perplexity支持实时信息查询,能够获取最新的新闻和数据。它的对话式界面允许用户进行后续提问和澄清,实现连贯的信息探索体验。Perplexity还提供专业模式,针对学术研究和深度分析提供更详细的回答。对于需要快速获取可靠信息和深入研究特定主题的用户来说,Perplexity AI是传统搜索引擎的强大替代品。",
+ category: "AI",
+ tags: ["搜索", "信息", "研究"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "huggingface",
+ url: "https://huggingface.co",
+ title: "Hugging Face",
+ description: "AI社区和模型库,提供开源机器学习工具和资源",
+ detailedDescription: "Hugging Face是机器学习和人工智能领域的中心平台,提供开源模型、数据集和工具。它的模型中心包含数万个预训练模型,涵盖自然语言处理、计算机视觉、音频处理等多个领域。Hugging Face的Transformers库是NLP领域的标准工具,支持多种深度学习框架。平台提供Spaces功能,允许用户部署和分享AI应用演示。它还有活跃的社区和论坛,促进知识分享和协作。对于AI研究人员、开发者和学习者来说,Hugging Face是获取资源、分享成果和学习最新技术的重要平台。",
+ category: "AI",
+ tags: ["机器学习", "开源", "模型"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "stability",
+ url: "https://stability.ai",
+ title: "Stability AI",
+ description: "开源AI图像生成技术的领导者,Stable Diffusion的创建者",
+ detailedDescription: "Stability AI是开源人工智能的领导者,以开发Stable Diffusion模型而闻名,这是一个强大的文本到图像生成系统。公司致力于推动AI民主化,将先进技术提供给广大开发者和创作者。Stability AI的产品包括DreamStudio(用户友好的图像生成平台)和各种专业API服务。他们的模型支持多种创意应用,从艺术创作到产品设计。Stability AI积极与研究机构合作,推动AI技术的边界。对于希望利用最新图像生成技术的创意专业人士和开发者来说,Stability AI提供了强大而灵活的工具和资源。",
+ category: "AI",
+ tags: ["图像生成", "开源", "创意"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "runway",
+ url: "https://runwayml.com",
+ title: "Runway",
+ description: "创意AI工具套件,用于视频编辑、生成和特效",
+ detailedDescription: "Runway是一个面向创意专业人士的AI平台,提供视频编辑、生成和特效工具。它的Gen-2模型可以从文本或图像生成视频,创造前所未有的视觉内容。Runway的Magic Tools套件包括背景移除、物体擦除、画面扩展等功能,简化了复杂的视频编辑任务。它支持协作工作流程,团队成员可以共享项目和资源。Runway定期更新其AI模型,不断提升生成内容的质量和多样性。对于电影制作者、视频编辑、动画师和数字艺术家来说,Runway提供了革命性的创意工具,大幅提高工作效率和创作可能性。",
+ category: "AI",
+ tags: ["视频", "创意", "编辑"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "synthesia",
+ url: "https://www.synthesia.io",
+ title: "Synthesia",
+ description: "AI视频生成平台,通过文本创建专业视频和虚拟人物",
+ detailedDescription: "Synthesia是一个AI视频生成平台,允许用户通过简单的文本输入创建专业质量的视频内容。它提供多种虚拟人物(AI演示者),能够用自然的语音和表情传达信息。Synthesia支持多种语言和口音,使内容本地化变得简单。平台提供模板库和场景定制,用户可以根据需求调整视频风格和布局。Synthesia的企业版支持品牌定制和高级编辑功能。对于培训视频、营销内容、产品演示和教育材料的创作者来说,Synthesia大大降低了视频制作的成本和复杂度,同时保持专业质量。",
+ category: "AI",
+ tags: ["视频生成", "虚拟人物", "内容创作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "elevenlabs",
+ url: "https://elevenlabs.io",
+ title: "ElevenLabs",
+ description: "AI语音生成和克隆平台,创建自然逼真的语音内容",
+ detailedDescription: "ElevenLabs是一个先进的AI语音技术平台,提供超逼真的文本转语音和语音克隆功能。它的语音合成技术能够生成带有自然情感和语调变化的语音,几乎无法与人类语音区分。ElevenLabs支持多种语言和口音,适合全球内容创作。平台提供语音库,用户可以选择不同风格和特点的预设声音。它的语音克隆功能允许用户从短音频样本创建个性化语音模型。ElevenLabs适用于有声读物、播客、视频配音、游戏角色和虚拟助手等多种应用场景。对于需要高质量语音内容的创作者和企业来说,ElevenLabs提供了突破性的AI语音解决方案。",
+ category: "AI",
+ tags: ["语音", "配音", "文本转语音"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "anthropic",
+ url: "https://www.anthropic.com",
+ title: "Anthropic",
+ description: "开发Claude AI助手的公司,专注于AI安全和研究",
+ detailedDescription: "Anthropic是一家AI研究公司,致力于开发可靠、可解释和可控的AI系统。公司由前OpenAI研究人员创立,专注于AI安全和负责任的AI发展。Anthropic的旗舰产品是Claude AI助手,以其自然对话能力和安全性能而著称。公司采用"宪法AI"方法,通过人类反馈和明确的价值观指导AI行为。Anthropic积极参与AI安全研究,发表关于AI风险评估和缓解的学术论文。对于关注AI发展方向和寻求负责任AI解决方案的组织和个人来说,Anthropic代表了以安全和人类价值为中心的AI创新方向。",
+ category: "AI",
+ tags: ["AI研究", "安全", "对话系统"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ }
+];
\ No newline at end of file
diff --git a/src/client/data/devSites.ts b/src/client/data/devSites.ts
new file mode 100644
index 0000000..29f44a5
--- /dev/null
+++ b/src/client/data/devSites.ts
@@ -0,0 +1,125 @@
+// 开发类网站数据
+import { FeaturedSite } from '../utils/featuredSites';
+
+export const devSites: FeaturedSite[] = [
+ {
+ id: "github",
+ url: "https://github.com",
+ title: "GitHub",
+ description: "全球最大的代码托管平台,支持Git版本控制和协作开发",
+ detailedDescription: "GitHub是全球最大的代码托管和协作开发平台,为超过8300万开发者提供服务。它基于Git版本控制系统,提供代码仓库托管、分支管理、代码审查和项目管理功能。GitHub的核心特性包括Issues(问题跟踪)、Pull Requests(代码审查和合并)、Actions(自动化工作流)和Pages(静态网站托管)。平台支持开源社区发展,同时为企业提供私有仓库和团队协作工具。GitHub已成为现代软件开发的中心枢纽,不仅用于代码管理,还用于文档共享、项目规划和社区建设。对于开发者来说,GitHub既是技术工具,也是展示作品和参与开源项目的社交平台。",
+ category: "开发",
+ tags: ["代码托管", "Git", "协作"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "stackoverflow",
+ url: "https://stackoverflow.com",
+ title: "Stack Overflow",
+ description: "程序员问答社区,解决编程问题的最大资源库",
+ detailedDescription: "Stack Overflow是全球最大的程序员问答社区,每月有超过1亿访问者。平台采用问答形式,用户可以提问、回答问题,并通过投票系统突出最有价值的内容。Stack Overflow的声誉系统鼓励高质量的贡献,用户通过有用的回答和问题获得声誉点数和徽章。网站涵盖几乎所有编程语言、框架和技术问题,成为开发者解决技术难题的首选资源。除了技术问答,Stack Overflow还提供开发者调查、工作板块和团队协作版本。对于从初学者到专业开发者的所有技术人员来说,Stack Overflow是学习、解决问题和分享知识的重要平台。",
+ category: "开发",
+ tags: ["问答", "社区", "编程"],
+ rating: 4.9,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "vercel",
+ url: "https://vercel.com",
+ title: "Vercel",
+ description: "前端开发和部署平台,专为现代Web应用优化",
+ detailedDescription: "Vercel是一个为前端开发者设计的云平台,专注于提供无缝的开发、预览和部署体验。它为Next.js、React、Vue等现代框架提供优化的托管环境,自动处理构建过程和全球CDN分发。Vercel的预览部署功能为每个Git提交自动生成唯一URL,便于团队审查和测试变更。平台提供无服务器函数支持,允许开发者创建API端点而无需管理服务器。Vercel的分析工具提供性能监控和用户体验数据。对于追求高性能、现代化开发流程的前端团队来说,Vercel提供了从开发到部署的完整解决方案,大大简化了工作流程。",
+ category: "开发",
+ tags: ["前端", "部署", "托管"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "netlify",
+ url: "https://www.netlify.com",
+ title: "Netlify",
+ description: "现代网站开发和托管平台,自动化部署流程",
+ detailedDescription: "Netlify是一个面向现代web项目的开发和托管平台,提供从本地开发到全球部署的完整工作流。它与Git仓库直接集成,实现持续部署,每次代码推送后自动构建和发布网站。Netlify的边缘网络确保全球快速加载,内置CDN和自动HTTPS加密保障性能和安全。平台提供丰富的功能,包括表单处理、身份验证、无服务器函数和大型媒体优化。Netlify还支持分支部署和预览URL,便于团队协作和测试。对于静态网站、JAMstack应用和前端项目,Netlify提供了简单而强大的托管解决方案,使开发者能够专注于代码而非基础设施。",
+ category: "开发",
+ tags: ["托管", "静态网站", "JAMstack"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "codepen",
+ url: "https://codepen.io",
+ title: "CodePen",
+ description: "在线代码编辑器和社区,专注于前端开发",
+ detailedDescription: "CodePen是一个面向前端开发者的在线代码编辑器和社交开发环境。它允许用户在浏览器中编写HTML、CSS和JavaScript代码,并实时预览结果。CodePen的核心功能是Pen(代码片段),用户可以创建、保存和分享自己的前端作品。平台支持预处理器(如Sass、LESS、TypeScript)和流行框架(如React、Vue、Angular)。CodePen的社区功能丰富,用户可以关注他人、收藏作品、参与挑战和探索精选内容。对于前端开发者来说,CodePen既是学习和实验的工具,也是展示创意和寻找灵感的平台,特别适合原型设计、概念验证和技术分享。",
+ category: "开发",
+ tags: ["编辑器", "前端", "社区"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "replit",
+ url: "https://replit.com",
+ title: "Replit",
+ description: "在线IDE和协作编码平台,支持多种编程语言",
+ detailedDescription: "Replit是一个功能强大的在线集成开发环境(IDE),支持超过50种编程语言,从Python、JavaScript到C++、Java等。它提供完整的开发环境,包括代码编辑器、文件系统、终端和版本控制,全部在浏览器中运行,无需本地安装。Replit的实时协作功能允许多人同时编辑同一个项目,类似Google Docs的体验。平台内置了部署功能,用户可以一键托管web应用和API。Replit还提供教育工具,支持课堂管理和作业分发。对于学生、教师、远程团队和希望快速原型开发的程序员来说,Replit提供了便捷、强大且无需配置的编码环境。",
+ category: "开发",
+ tags: ["IDE", "协作", "云开发"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "devto",
+ url: "https://dev.to",
+ title: "DEV Community",
+ description: "开发者社区平台,分享文章、讨论和最佳实践",
+ detailedDescription: "DEV Community (dev.to) 是一个开放、包容的开发者社区平台,专注于技术内容分享和讨论。它采用博客风格,允许开发者发布文章、教程、经验分享和职业建议。平台支持Markdown编写,集成了代码高亮和嵌入功能。DEV的互动功能包括评论、点赞、收藏和关注,促进社区参与。它的标签系统帮助用户发现特定技术领域的内容,如JavaScript、Python、Web开发等。DEV强调积极、支持性的社区文化,欢迎各级开发者参与。对于希望分享知识、学习新技术或与同行交流的开发者来说,DEV提供了一个友好、专业的在线社区空间。",
+ category: "开发",
+ tags: ["社区", "博客", "技术文章"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "digitalocean",
+ url: "https://www.digitalocean.com",
+ title: "DigitalOcean",
+ description: "简单易用的云服务平台,专为开发者设计",
+ detailedDescription: "DigitalOcean是一个面向开发者的云计算平台,以其简单性和可预测的定价而著称。它提供虚拟服务器(Droplets)、托管Kubernetes、托管数据库、对象存储和负载均衡器等核心云服务。DigitalOcean的控制面板直观易用,新用户可以在几分钟内启动服务器和部署应用。平台的文档和教程库(Community)是业内最全面的技术资源之一,涵盖各种开发和系统管理主题。DigitalOcean特别适合中小型企业、初创公司和个人开发者,提供了AWS和Azure等大型云服务的简化替代方案。对于需要可靠、易于管理且价格透明的云基础设施的开发团队来说,DigitalOcean是一个理想的选择。",
+ category: "开发",
+ tags: ["云服务", "服务器", "基础设施"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "hashnode",
+ url: "https://hashnode.com",
+ title: "Hashnode",
+ description: "开发者博客平台,支持个人技术博客和社区互动",
+ detailedDescription: "Hashnode是一个为开发者设计的现代博客平台,允许用户创建个人技术博客,同时保持对内容的完全所有权。它支持自定义域名,博客内容可以映射到用户自己的域名下,而不是子域名。Hashnode的编辑器支持Markdown、代码高亮和嵌入媒体,提供流畅的写作体验。平台内置了开发者社区,文章可以在个人博客和社区feed中同时展示,增加曝光度。Hashnode的货币化功能允许作者通过赞助和会员订阅获得收入。它还提供分析工具,帮助博主了解读者行为和文章表现。对于希望建立个人品牌、分享技术知识并与同行交流的开发者来说,Hashnode提供了一个专业、无广告的博客解决方案。",
+ category: "开发",
+ tags: ["博客", "写作", "社区"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "glitch",
+ url: "https://glitch.com",
+ title: "Glitch",
+ description: "创意应用开发和托管平台,强调简单性和协作",
+ detailedDescription: "Glitch是一个创新的web应用开发和托管平台,专注于简化创建过程和促进协作。它提供浏览器内编码环境,支持HTML、CSS、JavaScript和Node.js等技术。Glitch的特色是即时部署,代码更改会立即生效并在线可访问,无需手动部署步骤。平台支持实时协作,多人可以同时编辑项目,类似Google Docs的体验。Glitch鼓励代码重用和学习,用户可以查看、复制和修改公开项目。它还提供简单的数据库集成、环境变量管理和HTTPS支持。对于原型开发、教学演示、API实验和小型web应用,Glitch提供了一个低门槛、高效率的开发环境,特别适合初学者和快速创意实现。",
+ category: "开发",
+ tags: ["web开发", "协作", "托管"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ }
+];
\ No newline at end of file
diff --git a/src/client/data/entertainmentSites.ts b/src/client/data/entertainmentSites.ts
new file mode 100644
index 0000000..9a23739
--- /dev/null
+++ b/src/client/data/entertainmentSites.ts
@@ -0,0 +1,130 @@
+// 娱乐类网站数据
+import { FeaturedSite } from '../utils/featuredSites';
+
+export const entertainmentSites: FeaturedSite[] = [
+ {
+ id: "bilibili",
+ url: "https://www.bilibili.com",
+ title: "哔哩哔哩",
+ description: "中国领先的视频平台,涵盖动画、游戏、科技等多种内容",
+ detailedDescription: "哔哩哔哩(B站)是中国领先的视频平台,最初以ACG(动画、漫画、游戏)内容为主,现已发展为涵盖多元内容的综合性平台。B站特色的弹幕系统让观众可以在视频上方发送评论,创造独特的互动体验。平台拥有丰富的UP主生态,创作者涵盖知识分享、生活记录、娱乐、游戏等多个领域。B站的社区文化浓厚,用户粘性高,形成了独特的次元文化和社区氛围。平台还提供直播、番剧、影视、专栏等多种内容形式。对于年轻用户群体,B站不仅是娱乐平台,也是学习交流和文化社区的重要场所。",
+ category: "娱乐",
+ tags: ["视频", "弹幕", "社区"],
+ rating: 4.8,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "youtube",
+ url: "https://www.youtube.com",
+ title: "YouTube",
+ description: "全球最大的视频分享平台,内容覆盖几乎所有领域",
+ detailedDescription: "YouTube是全球最大的视频分享平台,每月活跃用户超过20亿。平台内容极其丰富,涵盖教育、娱乐、音乐、游戏、科技、生活方式等几乎所有领域。YouTube的创作者生态系统完善,从个人博主到大型媒体公司都在平台上发布内容。它的推荐算法根据用户观看历史和偏好提供个性化内容。YouTube提供多种互动方式,包括评论、点赞、分享和频道订阅。平台还有YouTube Premium订阅服务,提供无广告观看、背景播放和YouTube Music等增值功能。对于内容创作者,YouTube提供了广告分成、频道会员、超级感谢等多种变现途径。作为视频领域的主导平台,YouTube既是娱乐来源,也是学习工具和文化影响力的重要渠道。",
+ category: "娱乐",
+ tags: ["视频", "创作者", "直播"],
+ rating: 4.9,
+ featured: true,
+ addedDate: "2025-08-05",
+ unchina: true
+ },
+ {
+ id: "netflix",
+ url: "https://www.netflix.com",
+ title: "Netflix",
+ description: "全球领先的流媒体服务,提供电影、剧集和原创内容",
+ detailedDescription: "Netflix是全球领先的流媒体娱乐服务,提供丰富的电影、电视剧、纪录片和原创内容。平台以订阅制运营,用户支付月费即可无限观看所有内容,无广告干扰。Netflix以其高质量的原创内容著称,如《怪奇物语》、《纸牌屋》和《王冠》等,多次获得艾美奖和奥斯卡提名。平台采用先进的推荐算法,根据用户观看历史提供个性化内容建议。Netflix支持多设备访问,允许下载部分内容供离线观看。它的界面简洁直观,支持多种语言和字幕选项。作为娱乐产业的重要参与者,Netflix不仅改变了人们消费视频内容的方式,也对全球影视制作产生了深远影响。",
+ category: "娱乐",
+ tags: ["流媒体", "电影", "剧集"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "spotify",
+ url: "https://www.spotify.com",
+ title: "Spotify",
+ description: "全球最大的音乐流媒体服务,提供数百万首歌曲和播客",
+ detailedDescription: "Spotify是全球领先的音乐流媒体服务,拥有超过8000万首歌曲和400万个播客节目。平台提供免费(含广告)和付费订阅两种模式,付费用户可享受无广告、更高音质和离线下载等特权。Spotify的个性化推荐系统是其核心优势,通过'发现周刊'和'每日混音'等功能,根据用户听歌习惯推荐新内容。平台支持创建和分享播放列表,关注艺人和好友的活动。Spotify还大力投资播客领域,成为音频内容的综合平台。它的社交功能允许用户分享正在收听的内容,查看好友的音乐品味。对于音乐爱好者来说,Spotify不仅是听歌工具,也是发现新音乐和连接音乐社区的平台。",
+ category: "娱乐",
+ tags: ["音乐", "播客", "流媒体"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "twitch",
+ url: "https://www.twitch.tv",
+ title: "Twitch",
+ description: "领先的直播平台,主要面向游戏、创意和生活内容",
+ detailedDescription: "Twitch是全球最大的直播流媒体平台,最初专注于游戏直播,现已扩展到创意艺术、音乐、聊天节目和现实生活内容。平台的互动性极强,观众可以通过聊天室实时与主播和其他观众交流。Twitch的订阅和打赏系统为创作者提供了收入来源,粉丝可以通过订阅频道支持喜爱的主播。平台的特色功能包括Clips(精彩片段)、Extensions(扩展应用)和Raids(主播引导观众访问其他频道)。Twitch社区文化独特,发展出专属的表情符号和术语。对于游戏玩家、内容创作者和寻求互动娱乐体验的观众来说,Twitch提供了一个实时连接和分享热情的平台。",
+ category: "娱乐",
+ tags: ["直播", "游戏", "社区"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "douyin",
+ url: "https://www.douyin.com",
+ title: "抖音",
+ description: "中国领先的短视频平台,提供多样化的创意内容",
+ detailedDescription: "抖音是中国领先的短视频平台,由字节跳动开发运营,以其算法推荐和创意短视频内容而闻名。平台支持15秒至几分钟不等的视频创作,内容涵盖舞蹈、美食、旅游、教育、搞笑等多个领域。抖音的特色在于强大的视频编辑工具,用户可以添加音乐、特效、滤镜等元素,即使是普通用户也能创作出有趣的内容。平台的"挑战"功能鼓励用户参与特定主题的创作,形成病毒式传播。抖音还整合了直播和电商功能,创作者可以通过带货和打赏获得收入。作为中国最具影响力的社交媒体之一,抖音不仅是娱乐平台,也成为品牌营销和个人表达的重要渠道。",
+ category: "娱乐",
+ tags: ["短视频", "社交", "创意"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "tiktok",
+ url: "https://www.tiktok.com",
+ title: "TikTok",
+ description: "全球流行的短视频平台,以创意内容和趋势挑战著称",
+ detailedDescription: "TikTok是抖音的国际版本,已成为全球最受欢迎的短视频平台之一,在150多个国家拥有超过10亿月活跃用户。平台以15秒至3分钟的短视频为主,内容涵盖舞蹈、挑战、教程、喜剧等多种形式。TikTok的核心是其强大的推荐算法,通过"For You Page"(推荐页)向用户展示个性化内容,使新创作者也有机会获得广泛曝光。平台的音乐库丰富,用户可以轻松使用流行歌曲创建视频。TikTok的趋势和挑战机制促进了内容的病毒式传播,经常影响流行文化和音乐排行榜。对于创作者、品牌和普通用户来说,TikTok提供了一个创意表达和发现新内容的平台,其影响力已超越娱乐领域,延伸到营销、教育和社会议题讨论。",
+ category: "娱乐",
+ tags: ["短视频", "社交", "趋势"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "iqiyi",
+ url: "https://www.iqiyi.com",
+ title: "爱奇艺",
+ description: "中国领先的视频平台,提供电影、剧集和综艺节目",
+ detailedDescription: "爱奇艺是中国领先的视频平台,提供丰富的电影、电视剧、综艺节目、动漫和纪录片等内容。平台采用免费+会员的商业模式,VIP会员可享受无广告观看、超前点播和高清画质等特权。爱奇艺以自制内容见长,推出了多部热门网剧和综艺节目,如《延禧攻略》、《青春有你》等。平台的技术优势包括高质量的视频编码和智能推荐系统,为用户提供流畅的观看体验和个性化内容。爱奇艺还开发了弹幕、评论等社交功能,增强用户互动。作为中国在线视频市场的主要参与者,爱奇艺不断探索内容创新和商业模式升级,影响着中国数字娱乐产业的发展方向。",
+ category: "娱乐",
+ tags: ["视频", "电影", "电视剧"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "steam",
+ url: "https://store.steampowered.com",
+ title: "Steam",
+ description: "全球最大的PC游戏分发平台,提供丰富的游戏内容和社区功能",
+ detailedDescription: "Steam是由Valve Corporation开发的全球最大PC游戏分发平台,拥有超过5万款游戏和1.2亿活跃用户。平台不仅是游戏商店,还提供社区功能、游戏更新管理、云存档和好友系统等服务。Steam的特色包括定期举办的大型特卖活动、用户评价系统和愿望单功能,帮助玩家发现和购买游戏。平台支持创意工坊,玩家可以创建、分享和下载游戏模组和自定义内容。Steam还提供直播、截图分享和成就系统等社交功能,增强游戏体验。对于PC游戏玩家来说,Steam已成为必备的游戏平台,不仅提供游戏购买和管理,也是连接游戏社区和获取游戏资讯的重要渠道。",
+ category: "娱乐",
+ tags: ["游戏", "数字分发", "社区"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "weibo",
+ url: "https://weibo.com",
+ title: "微博",
+ description: "中国领先的社交媒体平台,类似Twitter的信息分享服务",
+ detailedDescription: "微博是中国领先的社交媒体平台,由新浪公司开发运营,被称为"中国的Twitter"。平台允许用户发布短文本、图片、视频和直播内容,关注感兴趣的用户和话题。微博的特色是话题标签系统(#话题#),用户可以参与热门话题讨论,实时了解社会热点。平台汇集了大量名人、媒体和官方机构账号,成为信息传播和公共讨论的重要渠道。微博的超级话题和粉丝群功能强化了社区建设,用户可以围绕共同兴趣形成社群。平台还整合了电商、支付等功能,拓展了社交媒体的应用场景。作为中国互联网生态的重要组成部分,微博不仅是社交和娱乐平台,也是新闻传播、舆论形成和社会互动的重要空间。",
+ category: "娱乐",
+ tags: ["社交媒体", "资讯", "话题"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ }
+];
\ No newline at end of file
diff --git a/src/client/data/import-code.js b/src/client/data/import-code.js
new file mode 100644
index 0000000..a4da6d7
--- /dev/null
+++ b/src/client/data/import-code.js
@@ -0,0 +1,9 @@
+
+// 一网一匠网站数据 - 自动爬取于 2025-08-12
+// 将以下代码添加到 featuredSites.ts 文件中
+
+// 一网一匠网站数据
+export const ywyjSites: FeaturedSite[] = [];
+
+// 将以下代码添加到 featuredSites 数组中:
+// ...ywyjSites,
diff --git a/src/client/data/toolSites.ts b/src/client/data/toolSites.ts
new file mode 100644
index 0000000..320fc09
--- /dev/null
+++ b/src/client/data/toolSites.ts
@@ -0,0 +1,125 @@
+// 工具类网站数据
+import { FeaturedSite } from '../utils/featuredSites';
+
+export const toolSites: FeaturedSite[] = [
+ {
+ id: "figma",
+ url: "https://www.figma.com",
+ title: "Figma",
+ description: "基于浏览器的协作式界面设计工具",
+ detailedDescription: "Figma是一个基于浏览器的协作式设计工具,专门用于UI/UX设计。它支持实时协作,多个设计师可以同时编辑同一个设计文件,大大提高了团队协作效率。Figma提供强大的设计系统功能,支持组件库、样式管理和设计规范。它的原型功能允许设计师创建交互式原型,进行用户测试和演示。Figma还提供丰富的插件生态系统,扩展了工具的功能。对于设计团队来说,Figma已经成为行业标准,特别是在远程协作和设计系统管理方面表现出色。",
+ category: "工具",
+ tags: ["设计", "协作", "UI/UX"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "notion",
+ url: "https://www.notion.so",
+ title: "Notion",
+ description: "全能的工作空间,集笔记、数据库、项目管理于一体",
+ detailedDescription: "Notion是一个全能的工作空间平台,将笔记、数据库、项目管理、知识库等功能整合在一个应用中。它采用块级编辑系统,用户可以自由组合各种内容块,创建个性化的页面和数据库。Notion支持团队协作,多人可以同时编辑和评论。它提供丰富的模板库,涵盖个人管理、团队协作、项目管理等各种场景。Notion的数据库功能强大,支持多种视图和关联关系,可以构建复杂的信息管理系统。对于个人用户和团队来说,Notion是一个灵活且强大的生产力工具。",
+ category: "工具",
+ tags: ["笔记", "协作", "生产力"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "airtable",
+ url: "https://airtable.com",
+ title: "Airtable",
+ description: "灵活的数据库和电子表格混合工具,适合团队协作",
+ detailedDescription: "Airtable是一个结合了数据库功能和电子表格界面的协作平台,让用户可以轻松创建和共享关系型数据库。它提供直观的界面,无需编程知识即可构建复杂的数据模型。Airtable支持多种视图模式,包括网格视图、看板视图、日历视图等,满足不同场景的需求。它的自动化功能可以设置触发器和操作,减少重复工作。Airtable还提供丰富的集成选项,可以与Slack、Google Drive等工具连接。对于项目管理、内容规划、客户关系管理等场景,Airtable提供了灵活且强大的解决方案。",
+ category: "工具",
+ tags: ["数据库", "协作", "项目管理"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "miro",
+ url: "https://miro.com",
+ title: "Miro",
+ description: "在线协作白板平台,适合远程团队头脑风暴和规划",
+ detailedDescription: "Miro是一个无限画布的在线协作白板平台,为远程团队提供视觉协作空间。它支持实时多人协作,团队成员可以同时在白板上添加内容、评论和互动。Miro提供丰富的模板,涵盖头脑风暴、用户故事地图、敏捷规划等多种场景。它的集成功能强大,可以与Jira、Asana、Slack等工具无缝连接。Miro支持多种内容类型,包括便签、图形、文本、图片、PDF等,满足不同的表达需求。对于分布式团队和远程工作者来说,Miro是进行创意思考、项目规划和视觉协作的理想平台。",
+ category: "工具",
+ tags: ["白板", "协作", "远程工作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "linear",
+ url: "https://linear.app",
+ title: "Linear",
+ description: "现代化的项目管理工具,专为软件开发团队设计",
+ detailedDescription: "Linear是一个为软件开发团队设计的现代化项目管理工具,以其简洁的界面和高效的工作流程著称。它提供直观的问题跟踪系统,支持敏捷开发方法,包括冲刺规划、迭代周期和团队目标。Linear的性能极为出色,操作流畅快速,支持键盘快捷键,提高工作效率。它的路线图功能允许团队规划长期项目和目标。Linear与GitHub、Figma等工具集成,实现开发流程的无缝衔接。对于追求效率和简洁的软件开发团队来说,Linear提供了专注于核心功能的项目管理解决方案。",
+ category: "工具",
+ tags: ["项目管理", "软件开发", "敏捷"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "obsidian",
+ url: "https://obsidian.md",
+ title: "Obsidian",
+ description: "基于本地Markdown文件的知识库工具,支持双向链接",
+ detailedDescription: "Obsidian是一个强大的知识管理工具,基于本地存储的Markdown文件,确保数据隐私和所有权。它的核心特性是双向链接,允许用户在笔记之间创建连接,形成知识网络。Obsidian支持图形视图,可视化笔记之间的关系,帮助发现新的联系。它提供丰富的插件系统,用户可以扩展功能,如日历、看板、模板等。Obsidian的工作区概念允许用户为不同项目创建独立的环境。对于研究人员、作家、学生和知识工作者来说,Obsidian是构建个人知识库和思考工具的理想选择。",
+ category: "工具",
+ tags: ["笔记", "知识管理", "Markdown"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "framer",
+ url: "https://www.framer.com",
+ title: "Framer",
+ description: "高级原型设计和网站构建工具,无需编码",
+ detailedDescription: "Framer是一个结合了设计和开发功能的工具,允许设计师创建高保真交互原型和功能性网站。它提供直观的可视化界面,同时支持代码自定义,满足不同技能水平的用户需求。Framer的智能组件系统让设计师可以创建响应式设计,适应不同屏幕尺寸。它的动画工具非常强大,可以创建流畅的过渡和微交互。Framer还提供网站托管服务,用户可以直接发布设计作品。对于UI/UX设计师和希望快速构建网站原型的团队来说,Framer提供了从概念到实现的完整解决方案。",
+ category: "工具",
+ tags: ["设计", "原型", "网站构建"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "webflow",
+ url: "https://webflow.com",
+ title: "Webflow",
+ description: "可视化网站设计和开发平台,无需编码",
+ detailedDescription: "Webflow是一个可视化网站设计和开发平台,让设计师可以在不编写代码的情况下创建专业级网站。它提供直观的拖放界面,同时生成干净的HTML、CSS和JavaScript代码。Webflow的CMS功能强大,支持动态内容和数据库集合,适合构建博客、电子商务和内容网站。它的响应式设计工具让用户可以为不同设备优化网站布局。Webflow还提供托管服务,包括CDN、SSL证书和表单处理。对于设计师、自由职业者和小型团队来说,Webflow是一个从设计到发布的完整网站解决方案。",
+ category: "工具",
+ tags: ["网站设计", "CMS", "无代码"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "loom",
+ url: "https://www.loom.com",
+ title: "Loom",
+ description: "屏幕录制和视频消息工具,简化远程沟通",
+ detailedDescription: "Loom是一个屏幕录制和视频消息工具,让用户可以快速录制屏幕、摄像头或两者结合的视频,并立即分享链接。它特别适合异步沟通,用户可以在自己方便的时间录制和观看视频。Loom提供浏览器扩展和桌面应用,使录制过程简单快捷。它的视频管理系统允许组织和分类录像,创建团队工作空间。Loom还提供互动功能,如评论、表情反应和时间戳。对于远程团队、教育工作者和客户支持人员来说,Loom是传达复杂信息和演示的有效工具,减少了会议需求和沟通障碍。",
+ category: "工具",
+ tags: ["视频", "远程工作", "沟通"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "coda",
+ url: "https://coda.io",
+ title: "Coda",
+ description: "集文档、电子表格和应用于一体的协作平台",
+ detailedDescription: "Coda是一个创新的文档平台,将文字处理、电子表格和数据库功能融为一体。它允许用户在一个文档中创建动态表格、自动化工作流程和交互式应用。Coda的公式系统强大而灵活,可以构建复杂的逻辑和计算。它支持团队协作,多人可以同时编辑和评论。Coda提供丰富的模板和构建块,帮助用户快速启动项目。它还支持与Slack、GitHub等工具的集成,实现工作流程的自动化。对于希望减少工具切换、统一信息管理的团队来说,Coda提供了一个灵活且强大的协作平台。",
+ category: "工具",
+ tags: ["文档", "协作", "工作流程"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ }
+];
\ No newline at end of file
diff --git a/src/client/data/ywyj-sites.json b/src/client/data/ywyj-sites.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/src/client/data/ywyj-sites.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/src/client/lib/axiosConfig.ts b/src/client/lib/axiosConfig.ts
new file mode 100644
index 0000000..162528e
--- /dev/null
+++ b/src/client/lib/axiosConfig.ts
@@ -0,0 +1,151 @@
+import axios from 'axios'
+import { toast } from 'vue-sonner'
+import Cookies from 'js-cookie'
+
+const service = axios.create({
+ baseURL: '/api',
+ timeout: 10000,
+})
+
+// 创建非响应式的 toast 配置
+const createToastConfig = (title: string, description: string, variant: 'destructive' | 'default' = 'destructive', duration?: number) => {
+ return {
+ variant,
+ title,
+ description,
+ ...(duration && { duration })
+ }
+}
+
+// 请求拦截器 - 添加日志
+service.interceptors.request.use(
+ config => {
+ const token = Cookies.get('token');
+ if (token) {
+ config.headers['Authorization'] = `Bearer ${token}`;
+ }
+
+ // 记录请求日志
+ console.log(`🚀 API Request: ${config.method?.toUpperCase()} ${config.url}`, {
+ params: config.params,
+ data: config.data,
+ headers: {
+ 'Content-Type': config.headers['Content-Type'],
+ 'Authorization': config.headers['Authorization'] ? 'Bearer ***' : 'None'
+ }
+ });
+
+ return config;
+ },
+ error => {
+ console.error('❌ 请求错误:', error);
+ toast.error('请求发送失败', {
+ description: error.message || "网络连接异常,请检查网络设置"
+ });
+ return Promise.reject(error);
+ }
+);
+
+// 响应拦截器 - 添加日志和错误处理
+service.interceptors.response.use(
+ response => {
+ // 记录成功响应日志
+ console.log(`✅ API Response: ${response.config.method?.toUpperCase()} ${response.config.url}`, {
+ status: response.status,
+ statusText: response.statusText,
+ data: response.data
+ });
+
+ // 检查业务逻辑错误
+ if (response.data && response.data.status === 'error') {
+ console.error(`❌ Business Error: ${response.config.url}`, response.data);
+ toast.error('操作失败', {
+ description: response.data.message || "服务器返回错误"
+ });
+ }
+
+ return response;
+ },
+ error => {
+ // 记录错误响应日志
+ console.error(`❌ API Error: ${error.config?.method?.toUpperCase()} ${error.config?.url}`, {
+ status: error.response?.status,
+ statusText: error.response?.statusText,
+ data: error.response?.data,
+ message: error.message
+ });
+
+ let errorMessage = '网络连接失败';
+ let errorTitle = '请求失败';
+
+ if (error.response) {
+ const status = error.response.status;
+ const data = error.response.data;
+
+ switch (status) {
+ case 400:
+ errorTitle = '请求参数错误';
+ errorMessage = data?.message || '请求参数不完整或格式错误';
+ break;
+ case 401:
+ errorTitle = '身份验证失败';
+ errorMessage = data?.message || '请重新登录';
+ // 清除用户信息并跳转到登录页
+ Cookies.remove('token');
+ Cookies.remove('userId');
+ Cookies.remove('userInfo');
+ window.location.href = '/login';
+ break;
+ case 403:
+ errorTitle = '访问被拒绝';
+ errorMessage = data?.message || '您没有权限执行此操作';
+ break;
+ case 404:
+ errorTitle = '资源不存在';
+ errorMessage = data?.message || '请求的资源不存在';
+ break;
+ case 429:
+ errorTitle = '请求过于频繁';
+ errorMessage = data?.message || '请稍后再试';
+ break;
+ case 500:
+ errorTitle = '服务器内部错误';
+ errorMessage = data?.message || '服务器出现异常,请联系管理员';
+ break;
+ case 502:
+ errorTitle = '网关错误';
+ errorMessage = data?.message || '服务器网关异常';
+ break;
+ case 503:
+ errorTitle = '服务不可用';
+ errorMessage = data?.message || '服务暂时不可用,请稍后重试';
+ break;
+ default:
+ errorTitle = '请求失败';
+ errorMessage = data?.message || `HTTP ${status} 错误`;
+ }
+ } else if (error.code === 'ECONNABORTED') {
+ errorTitle = '请求超时';
+ errorMessage = '请求超时,请检查网络连接';
+ } else if (error.code === 'NETWORK_ERROR') {
+ errorTitle = '网络错误';
+ errorMessage = '网络连接异常,请检查网络设置';
+ }
+
+ // 显示破坏性 toast
+ toast.error(errorTitle, {
+ description: errorMessage,
+ duration: 5000
+ });
+
+ return {
+ status: error.response?.status || 500,
+ data: error.response?.data || { message: errorMessage },
+ headers: error.response?.headers,
+ config: error.config,
+ request: error.request,
+ };
+ }
+);
+
+export default service
\ No newline at end of file
diff --git a/src/client/lib/utils.ts b/src/client/lib/utils.ts
new file mode 100644
index 0000000..a8a12bc
--- /dev/null
+++ b/src/client/lib/utils.ts
@@ -0,0 +1,30 @@
+import { type ClassValue, clsx } from 'clsx'
+import { twMerge } from 'tailwind-merge'
+// @ts-expect-error: blueimp-md5 没有类型声明
+import md5 from 'blueimp-md5'
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
+
+/**
+ * 根据邮箱地址获取 Gravatar 头像 URL
+ * @param email 邮箱地址
+ * @param size 头像尺寸,默认 200
+ * @param defaultImage 默认头像类型,可选 'mp', 'identicon', 'monsterid', 'wavatar', 'retro', 'robohash', 'blank'
+ * @returns Gravatar 头像 URL
+ */
+export function getGravatarURL(email: string, size: number = 200, defaultImage: string = 'identicon'): string {
+ if (!email) {
+ return `https://gravatar.com/avatar/?d=${defaultImage}&s=${size}`
+ }
+
+ // 去除首尾空格并转换为小写
+ const address = email.trim().toLowerCase()
+
+ // 创建 MD5 哈希(Gravatar 使用 MD5)
+ const hash = md5(address)
+
+ // 返回 Gravatar URL
+ return `https://gravatar.com/avatar/${hash}?d=${defaultImage}&s=${size}`
+}
diff --git a/src/client/main.ts b/src/client/main.ts
new file mode 100644
index 0000000..8530498
--- /dev/null
+++ b/src/client/main.ts
@@ -0,0 +1,74 @@
+import "./style.css";
+
+import { createApp } from "vue";
+import App from "./App.vue";
+import { createPinia } from 'pinia'
+import { createRouter, createWebHistory } from 'vue-router'
+import routes from './routes'
+import { useUserStore } from './store/user'
+import { useThemeStore } from './store/theme'
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes,
+})
+
+router.beforeEach((to, from, next) => {
+ const userStore = useUserStore()
+ const isLoggedIn = !!userStore.token
+ const isAdmin = !!userStore.userInfo?.is_admin
+
+ // 如果用户已登录且被封禁,且不在封禁页面,则跳转到封禁页面
+ if (isLoggedIn && userStore.userInfo?.is_banned && to.path !== '/banned') {
+ const banReason = userStore.userInfo?.ban_reason || '违反社区规定'
+ const banExpiration = userStore.userInfo?.ban_expiration || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
+
+ next({
+ path: '/banned',
+ query: {
+ banReason: banReason,
+ banExpiration: banExpiration
+ }
+ })
+ return
+ }
+
+ // 管理员权限校验
+ if (to.meta.requiresAdmin && !isAdmin) {
+ // 可自定义跳转页面或弹窗提示
+ next({ path: '/' }) // 跳转到首页
+ return
+ }
+ // 需要登录的页面,未登录则跳转到登录页
+ if (to.meta.requiresAuth && !isLoggedIn) {
+ next({ path: '/login' })
+ // 已登录访问登录/注册页,自动跳转到仪表盘
+ } else if ((to.path === '/login' || to.path === '/register') && isLoggedIn) {
+ next({ path: '/dashboard' }) // 或 '/dashboard',根据你的仪表盘路由
+ } else {
+ next()
+ }
+})
+
+const app = createApp(App)
+const pinia = createPinia()
+app.use(pinia)
+app.use(router)
+
+// 初始化主题
+const themeStore = useThemeStore(pinia)
+themeStore.initTheme()
+
+
+router.afterEach((to) => {
+ // 首页特殊标题
+ if (to.path === '/' || to.name === 'Home') {
+ document.title = 'Space | 搜索'
+ } else {
+ // 取 meta.title 或 name 或自定义标题
+ const pageTitle = to.meta?.title || to.meta?.pageTitle || to.name || '页面';
+ document.title = `${pageTitle} | Space`
+ }
+})
+
+app.mount('#app')
diff --git a/src/client/public/gt4.js b/src/client/public/gt4.js
new file mode 100644
index 0000000..772e096
--- /dev/null
+++ b/src/client/public/gt4.js
@@ -0,0 +1,487 @@
+"v4.2.0 Geetest Inc.";
+
+(function (window) {
+ "use strict";
+ if (typeof window === 'undefined') {
+ throw new Error('Geetest requires browser environment');
+ }
+
+var document = window.document;
+var Math = window.Math;
+var head = document.getElementsByTagName("head")[0];
+var TIMEOUT = 10000;
+
+function _Object(obj) {
+ this._obj = obj;
+}
+
+_Object.prototype = {
+ _each: function (process) {
+ var _obj = this._obj;
+ for (var k in _obj) {
+ if (_obj.hasOwnProperty(k)) {
+ process(k, _obj[k]);
+ }
+ }
+ return this;
+ },
+ _extend: function (obj){
+ var self = this;
+ new _Object(obj)._each(function (key, value){
+ self._obj[key] = value;
+ })
+ }
+};
+
+var uuid = function () {
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+ var r = Math.random() * 16 | 0;
+ var v = c === 'x' ? r : (r & 0x3 | 0x8);
+ return v.toString(16);
+ });
+ };
+
+function Config(config) {
+ var self = this;
+ new _Object(config)._each(function (key, value) {
+ self[key] = value;
+ });
+}
+
+Config.prototype = {
+ apiServers: ['gcaptcha4.geetest.com','gcaptcha4.geevisit.com','gcaptcha4.gsensebot.com'],
+ staticServers: ["static.geetest.com",'static.geevisit.com'],
+ protocol: 'http://',
+ typePath: '/load',
+ fallback_config: {
+ bypass: {
+ staticServers: ["static.geetest.com",'static.geevisit.com'],
+ type: 'bypass',
+ bypass: '/v4/bypass.js'
+ }
+ },
+ _get_fallback_config: function () {
+ var self = this;
+ if (isString(self.type)) {
+ return self.fallback_config[self.type];
+ } else {
+ return self.fallback_config.bypass;
+ }
+ },
+ _extend: function (obj) {
+ var self = this;
+ new _Object(obj)._each(function (key, value) {
+ self[key] = value;
+ })
+ }
+};
+var isNumber = function (value) {
+ return (typeof value === 'number');
+};
+var isString = function (value) {
+ return (typeof value === 'string');
+};
+var isBoolean = function (value) {
+ return (typeof value === 'boolean');
+};
+var isObject = function (value) {
+ return (typeof value === 'object' && value !== null);
+};
+var isFunction = function (value) {
+ return (typeof value === 'function');
+};
+var MOBILE = /Mobi/i.test(navigator.userAgent);
+
+var callbacks = {};
+var status = {};
+
+var random = function () {
+ return parseInt(Math.random() * 10000) + (new Date()).valueOf();
+};
+
+// bind 函数polify, 不带new功能的bind
+
+var bind = function(target,context){
+ if(typeof target !== 'function'){
+ return;
+ }
+ var args = Array.prototype.slice.call(arguments,2);
+
+ if(Function.prototype.bind){
+ return target.bind(context, args);
+ }else {
+ return function(){
+ var _args = Array.prototype.slice.call(arguments);
+ return target.apply(context,args.concat(_args));
+ }
+ }
+}
+
+
+
+var toString = Object.prototype.toString;
+
+var _isFunction = function(obj) {
+ return typeof(obj) === 'function';
+};
+var _isObject = function(obj) {
+ return obj === Object(obj);
+};
+var _isArray = function(obj) {
+ return toString.call(obj) == '[object Array]';
+};
+var _isDate = function(obj) {
+ return toString.call(obj) == '[object Date]';
+};
+var _isRegExp = function(obj) {
+ return toString.call(obj) == '[object RegExp]';
+};
+var _isBoolean = function(obj) {
+ return toString.call(obj) == '[object Boolean]';
+};
+
+
+function resolveKey(input){
+ return input.replace(/(\S)(_([a-zA-Z]))/g, function(match, $1, $2, $3){
+ return $1 + $3.toUpperCase() || "";
+ })
+}
+
+function camelizeKeys(input, convert){
+ if(!_isObject(input) || _isDate(input) || _isRegExp(input) || _isBoolean(input) || _isFunction(input)){
+ return convert ? resolveKey(input) : input;
+ }
+
+ if(_isArray(input)){
+ var temp = [];
+ for(var i = 0; i < input.length; i++){
+ temp.push(camelizeKeys(input[i]));
+ }
+
+ }else {
+ var temp = {};
+ for(var prop in input){
+ if(input.hasOwnProperty(prop)){
+ temp[camelizeKeys(prop, true)] = camelizeKeys(input[prop]);
+ }
+ }
+ }
+ return temp;
+}
+
+var loadScript = function (url, cb, timeout) {
+ var script = document.createElement("script");
+ script.charset = "UTF-8";
+ script.async = true;
+
+ // 对geetest的静态资源添加 crossOrigin
+ if ( /static\.geetest\.com/g.test(url)) {
+ script.crossOrigin = "anonymous";
+ }
+
+ script.onerror = function () {
+ cb(true);
+ // 错误触发了,超时逻辑就不用了
+ loaded = true;
+ };
+ var loaded = false;
+ script.onload = script.onreadystatechange = function () {
+ if (!loaded &&
+ (!script.readyState ||
+ "loaded" === script.readyState ||
+ "complete" === script.readyState)) {
+
+ loaded = true;
+ setTimeout(function () {
+ cb(false);
+ }, 0);
+ }
+ };
+ script.src = url;
+ head.appendChild(script);
+
+ setTimeout(function () {
+ if (!loaded) {
+ script.onerror = script.onload = null;
+ script.remove && script.remove();
+ cb(true);
+ }
+ }, timeout || TIMEOUT);
+};
+
+var normalizeDomain = function (domain) {
+ // special domain: uems.sysu.edu.cn/jwxt/geetest/
+ // return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
+ return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
+};
+var normalizePath = function (path) {
+
+ path = path && path.replace(/\/+/g, '/');
+ if (path.indexOf('/') !== 0) {
+ path = '/' + path;
+ }
+ return path;
+};
+var normalizeQuery = function (query) {
+ if (!query) {
+ return '';
+ }
+ var q = '?';
+ new _Object(query)._each(function (key, value) {
+ if (isString(value) || isNumber(value) || isBoolean(value)) {
+ q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
+ }
+ });
+ if (q === '?') {
+ q = '';
+ }
+ return q.replace(/&$/, '');
+};
+var makeURL = function (protocol, domain, path, query) {
+ domain = normalizeDomain(domain);
+
+ var url = normalizePath(path) + normalizeQuery(query);
+ if (domain) {
+ url = protocol + domain + url;
+ }
+
+ return url;
+};
+
+var load = function (config, protocol, domains, path, query, cb, handleCb) {
+ var tryRequest = function (at) {
+ // 处理jsonp回调,这里为了保证每个不同jsonp都有唯一的回调函数
+ if(handleCb){
+ var cbName = "geetest_" + random();
+ // 需要与预先定义好cbname参数,删除对象
+ window[cbName] = bind(handleCb, null, cbName);
+ query.callback = cbName;
+ }
+ var url = makeURL(protocol, domains[at], path, query);
+ loadScript(url, function (err) {
+ if (err) {
+ // 超时或者出错的时候 移除回调
+ if(cbName){
+ try {
+ window[cbName] = function(){
+ window[cbName] = null;
+ }
+ } catch (e) {}
+ }
+
+ if (at >= domains.length - 1) {
+ cb(true);
+ // report gettype error
+ } else {
+ tryRequest(at + 1);
+ }
+ } else {
+ cb(false);
+ }
+ }, config.timeout);
+ };
+ tryRequest(0);
+};
+
+
+var jsonp = function (domains, path, config, callback) {
+
+ var handleCb = function (cbName, data) {
+
+ // 保证只执行一次,全部超时的情况下不会再触发;
+
+ if (data.status == 'success') {
+ callback(data.data);
+ } else if (!data.status) {
+ callback(data);
+ } else {
+ //接口有返回,但是返回了错误状态,进入报错逻辑
+ callback(data);
+ }
+ window[cbName] = undefined;
+ try {
+ delete window[cbName];
+ } catch (e) {
+ }
+ };
+ load(config, config.protocol, domains, path, {
+ callback: '',
+ captcha_id: config.captchaId,
+ challenge: config.challenge || uuid(),
+ client_type: config.clientType ? config.clientType : (MOBILE? 'h5':'web'),
+ risk_type: config.riskType,
+ user_info: config.userInfo,
+ call_type: config.callType,
+ lang: config.language? config.language : navigator.appName === 'Netscape' ? navigator.language.toLowerCase() : navigator.userLanguage.toLowerCase()
+ }, function (err) {
+ // 网络问题接口没有返回,直接使用本地验证码,走宕机模式
+ // 这里可以添加用户的逻辑
+ if(err && typeof config.offlineCb === 'function'){
+ // 执行自己的宕机
+ config.offlineCb();
+ return;
+ }
+ if(err){
+ callback(config._get_fallback_config());
+ }
+ }, handleCb);
+};
+
+var reportError = function (config, url) {
+ load(config, config.protocol, ['monitor.geetest.com'], '/monitor/send', {
+ time: Date.now().getTime(),
+ captcha_id: config.gt,
+ challenge: config.challenge,
+ exception_url: url,
+ error_code: config.error_code
+ }, function (err) {})
+}
+
+var throwError = function (errorType, config, errObj) {
+ var errors = {
+ networkError: '网络错误',
+ gtTypeError: 'gt字段不是字符串类型'
+ };
+ if (typeof config.onError === 'function') {
+ config.onError({
+ desc: errObj.desc,
+ msg: errObj.msg,
+ code: errObj.code
+ });
+ } else {
+ throw new Error(errors[errorType]);
+ }
+};
+
+var detect = function () {
+ return window.Geetest || document.getElementById("gt_lib");
+};
+
+if (detect()) {
+ status.slide = "loaded";
+}
+var GeetestIsLoad = function (fname) {
+ var GeetestIsLoad = false;
+ var tags = { js: 'script', css: 'link' };
+ var tagname = fname && tags[fname.split('.').pop()];
+ if (tagname !== undefined) {
+ var elts = document.getElementsByTagName(tagname);
+ for (var i in elts) {
+ if ((elts[i].href && elts[i].href.toString().indexOf(fname) > 0)
+ || (elts[i].src && elts[i].src.toString().indexOf(fname) > 0)) {
+ GeetestIsLoad = true;
+ }
+ }
+ }
+ return GeetestIsLoad;
+};
+window.initGeetest4 = function (userConfig,callback) {
+
+ var config = new Config(userConfig);
+ if (userConfig.https) {
+ config.protocol = 'https://';
+ } else if (!userConfig.protocol) {
+ config.protocol = window.location.protocol + '//';
+ }
+
+
+ if (isObject(userConfig.getType)) {
+ config._extend(userConfig.getType);
+ }
+
+ jsonp(config.apiServers , config.typePath, config, function (newConfig) {
+ //错误捕获,第一个load请求可能直接报错
+ var newConfig = camelizeKeys(newConfig);
+
+ if(newConfig.status === 'error'){
+ return throwError('networkError', config, newConfig);
+ }
+
+ var type = newConfig.type;
+ if(config.debug){
+ new _Object(newConfig)._extend(config.debug)
+ }
+ var init = function () {
+ config._extend(newConfig);
+ callback(new window.Geetest4(config));
+ };
+
+ callbacks[type] = callbacks[type] || [];
+
+ var s = status[type] || 'init';
+ if (s === 'init') {
+ status[type] = 'loading';
+
+ callbacks[type].push(init);
+
+ if(newConfig.gctPath){
+ load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers , newConfig.gctPath, null, function (err){
+ if(err){
+ throwError('networkError', config, {
+ code: '60205',
+ msg: 'Network failure',
+ desc: {
+ detail: 'gct resource load timeout'
+ }
+ });
+ }
+ })
+ }
+
+ load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers, newConfig.bypass || (newConfig.staticPath + newConfig.js), null, function (err) {
+ if (err) {
+ status[type] = 'fail';
+ throwError('networkError', config, {
+ code: '60204',
+ msg: 'Network failure',
+ desc: {
+ detail: 'js resource load timeout'
+ }
+ });
+ } else {
+
+ status[type] = 'loaded';
+ var cbs = callbacks[type];
+ for (var i = 0, len = cbs.length; i < len; i = i + 1) {
+ var cb = cbs[i];
+ if (isFunction(cb)) {
+ cb();
+ }
+ }
+ callbacks[type] = [];
+ status[type] = 'init';
+ }
+ });
+ } else if (s === "loaded") {
+ // 判断gct是否需要重新加载
+ if(newConfig.gctPath && !GeetestIsLoad(newConfig.gctPath)){
+ load(config, config.protocol, Object.hasOwnProperty.call(config, 'staticServers') ? config.staticServers : newConfig.staticServers || config.staticServers , newConfig.gctPath, null, function (err){
+ if(err){
+ throwError('networkError', config, {
+ code: '60205',
+ msg: 'Network failure',
+ desc: {
+ detail: 'gct resource load timeout'
+ }
+ });
+ }
+ })
+ }
+ return init();
+ } else if (s === "fail") {
+ throwError('networkError', config, {
+ code: '60204',
+ msg: 'Network failure',
+ desc: {
+ detail: 'js resource load timeout'
+ }
+ });
+ } else if (s === "loading") {
+ callbacks[type].push(init);
+ }
+ });
+
+};
+
+
+})(window);
diff --git a/src/client/routes.ts b/src/client/routes.ts
new file mode 100644
index 0000000..63df819
--- /dev/null
+++ b/src/client/routes.ts
@@ -0,0 +1,148 @@
+import LoginForm from './components/LoginForm.vue'
+import RegisterForm from './components/RegisterForm.vue'
+
+export default [
+ {
+ path: '/',
+ name: 'Home',
+ component: () => import('./views/Home.vue'),
+ meta: { title: 'Aoteam | Lunova Studio' }
+ },
+ {
+ path: '/login',
+ name: 'Login',
+ component: LoginForm,
+ meta: { title: '登录' }
+ },
+ {
+ path: '/register',
+ name: 'Register',
+ component: RegisterForm,
+ meta: { title: '注册' }
+ },
+ {
+ path: '/banned',
+ name: 'Banned',
+ component: () => import('./views/BannedPage.vue'),
+ meta: { title: '账户被封禁' }
+ },
+ {
+ path: '/settings',
+ name: 'Settings',
+ component: () => import('./views/Settings.vue'),
+ meta: { title: '设置' }
+ },
+ {
+ path: '/privacy-policy',
+ name: 'PrivacyPolicy',
+ component: () => import('./views/PrivacyPolicy.vue'),
+ meta: { title: '隐私政策' }
+ },
+ {
+ path: '/terms-of-service',
+ name: 'TermsOfService',
+ component: () => import('./views/TermsOfService.vue'),
+ meta: { title: '使用条款' }
+ },
+ {
+ path: '/about-us',
+ name: 'AboutUs',
+ component: () => import('./views/AboutUs.vue'),
+ meta: { title: '关于我们' }
+ },
+ {
+ path: '/about-version',
+ name: 'AboutVersion',
+ component: () => import('./views/AboutVersion.vue'),
+ meta: { title: '版本信息' }
+ },
+ {
+ path: '/setup',
+ name: 'Setup',
+ component: () => import('./views/Setup.vue'),
+ meta: { title: '欢迎' }
+ },
+ {
+ path: '/quality-content',
+ name: 'QualityContent',
+ component: () => import('./views/QualityContent.vue'),
+ meta: { title: 'Today' }
+ },
+ {
+ path: '/quality-content/:id',
+ name: 'SiteDetail',
+ component: () => import('./views/SiteDetail.vue'),
+ meta: { title: '网站详情' }
+ },
+ {
+ path: '/bilibili-hot',
+ name: 'BilibiliHot',
+ component: () => import('./views/BilibiliHot.vue'),
+ meta: { title: 'B站热门榜' }
+ },
+ {
+ path: '/github-hot',
+ name: 'GithubHot',
+ component: () => import('./views/GithubHot.vue'),
+ meta: { title: 'GitHub热门榜' }
+ },
+ {
+ path: '/hot',
+ name: 'Hot',
+ component: () => import('./views/Hot.vue'),
+ meta: { title: '热门榜单' }
+ },
+ {
+ path: '/dashboard',
+ component: () => import('./views/DashboardLayout.vue'),
+ children: [
+ {
+ path: '',
+ name: 'Dashboard',
+ component: () => import('./views/Dashboard.vue'),
+ meta: { title: '管理仪表盘' }
+ },
+ {
+ path: 'settings',
+ name: 'DashboardSettings',
+ component: () => import('./views/Settings.vue'),
+ meta: { title: '设置'}
+ },
+ {
+ path: 'profile',
+ name: 'UserProfile',
+ component: () => import('./views/UserProfile.vue'),
+ meta: { title: '用户设置' }
+ },
+ {
+ path: 'users',
+ name: 'UserManagement',
+ component: () => import('./views/UserManagement.vue'),
+ meta: { title: '用户管理' , requiresAdmin: true }
+ },
+ {
+ path: 'permissions',
+ name: 'PermissionManagement',
+ component: () => import('./views/PermissionManagement.vue'),
+ meta: { title: '权限管理' , requiresAdmin: true }
+ },
+ {
+ path: 'audit',
+ name: 'AuditManagement',
+ component: () => import('./views/AuditManagement.vue'),
+ meta: { title: '审核管理' , requiresAdmin: true }
+ },
+ {
+ path: 'about',
+ name: 'AboutIndex',
+ component: () => import('./views/About-d.vue'),
+ meta: { title: '关于' }
+ }
+ ]
+ },
+ // 添加一个通配符路由,将所有未匹配的路径重定向到首页
+ {
+ path: '/:pathMatch(.*)*',
+ redirect: '/'
+ }
+]
\ No newline at end of file
diff --git a/src/client/store/theme.ts b/src/client/store/theme.ts
new file mode 100644
index 0000000..fd41f98
--- /dev/null
+++ b/src/client/store/theme.ts
@@ -0,0 +1,77 @@
+import { defineStore } from 'pinia'
+import { ref, watch } from 'vue'
+
+export type Theme = 'light' | 'dark' | 'auto'
+
+export const useThemeStore = defineStore('theme', () => {
+ const theme = ref('auto')
+
+ // 从localStorage初始化主题
+ const initTheme = () => {
+ const savedTheme = localStorage.getItem('theme') as Theme
+ if (savedTheme && ['light', 'dark', 'auto'].includes(savedTheme)) {
+ theme.value = savedTheme
+ }
+ applyTheme()
+ }
+
+ // 应用主题
+ const applyTheme = () => {
+ const root = document.documentElement
+ const currentTheme = getCurrentTheme()
+
+ if (currentTheme === 'dark') {
+ root.classList.add('dark')
+ } else {
+ root.classList.remove('dark')
+ }
+ }
+
+ // 获取当前实际主题
+ const getCurrentTheme = (): 'light' | 'dark' => {
+ if (theme.value === 'auto') {
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
+ }
+ return theme.value
+ }
+
+ // 设置主题
+ const setTheme = (newTheme: Theme) => {
+ theme.value = newTheme
+ localStorage.setItem('theme', newTheme)
+ applyTheme()
+ }
+
+ // 切换主题
+ const toggleTheme = () => {
+ const themes: Theme[] = ['light', 'dark', 'auto']
+ const currentIndex = themes.indexOf(theme.value)
+ const nextIndex = (currentIndex + 1) % themes.length
+ setTheme(themes[nextIndex])
+ }
+
+ // 监听系统主题变化
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
+ const handleMediaChange = () => {
+ if (theme.value === 'auto') {
+ applyTheme()
+ }
+ }
+
+ mediaQuery.addEventListener('change', handleMediaChange)
+
+ // 监听主题变化并保存到localStorage
+ watch(theme, (newTheme) => {
+ localStorage.setItem('theme', newTheme)
+ applyTheme()
+ })
+
+ return {
+ theme,
+ initTheme,
+ setTheme,
+ toggleTheme,
+ getCurrentTheme,
+ applyTheme
+ }
+})
\ No newline at end of file
diff --git a/src/client/store/user.ts b/src/client/store/user.ts
new file mode 100644
index 0000000..c1cf724
--- /dev/null
+++ b/src/client/store/user.ts
@@ -0,0 +1,149 @@
+import { defineStore } from 'pinia'
+import Cookies from 'js-cookie'
+import request from '@/lib/axiosConfig'
+
+export const useUserStore = defineStore('user', {
+ state: () => ({
+ token: '',
+ userId: '',
+ userInfo: {
+ id: '',
+ username: '',
+ email: '',
+ avatar: '',
+ is_banned: false,
+ ban_reason: '',
+ ban_expiration: '',
+ is_admin: false,
+ qq_num: ''
+ },
+ }),
+ actions: {
+ setUser(token: string, userId: string, userInfo?: {
+ id: string,
+ username: string,
+ email: string,
+ avatar?: string,
+ is_banned?: boolean,
+ ban_reason?: string,
+ ban_expiration?: string,
+ is_admin?: boolean,
+ qq_num?: string
+ }) {
+ console.log('👤 设置用户数据:', {
+ hasToken: !!token,
+ userId,
+ username: userInfo?.username
+ })
+
+ this.token = token
+ this.userId = userId
+ if (userInfo) this.userInfo = {
+ id: userInfo.id,
+ username: userInfo.username,
+ email: userInfo.email,
+ avatar: userInfo.avatar || '',
+ is_banned: userInfo.is_banned || false,
+ ban_reason: userInfo.ban_reason || '',
+ ban_expiration: userInfo.ban_expiration || '',
+ is_admin: userInfo.is_admin || false,
+ qq_num: userInfo.qq_num || ''
+ }
+ // 存储到 cookie,设置72小时过期时间
+ Cookies.set('token', token, { expires: 3 }) // 3天 = 72小时
+ Cookies.set('userId', userId, { expires: 3 })
+ Cookies.set('userInfo', JSON.stringify(this.userInfo), { expires: 3 })
+
+ console.log('✅ 用户数据设置成功')
+ },
+ logout() {
+ console.log('🚪 用户登出')
+ this.token = ''
+ this.userId = ''
+ this.userInfo = {
+ id: '',
+ username: '',
+ email: '',
+ avatar: '',
+ is_banned: false,
+ ban_reason: '',
+ ban_expiration: '',
+ is_admin: false,
+ qq_num: ''
+ }
+ // 清除 cookie
+ Cookies.remove('token')
+ Cookies.remove('userId')
+ Cookies.remove('userInfo')
+
+ console.log('✅ 用户登出完成')
+ },
+ loadFromCookie() {
+ console.log('🍪 从 cookie 加载用户数据...')
+ const token = Cookies.get('token')
+ const userId = Cookies.get('userId')
+ const userInfoStr = Cookies.get('userInfo')
+
+ if (token && userId && userInfoStr) {
+ try {
+ const userInfo = JSON.parse(userInfoStr)
+ this.token = token
+ this.userId = userId
+ this.userInfo = userInfo
+ console.log('✅ 从 cookie 加载用户数据:', {
+ username: userInfo.username,
+ is_admin: userInfo.is_admin
+ })
+ } catch (error) {
+ console.error('❌ 从 cookie 解析用户信息失败:', error)
+ this.logout()
+ }
+ } else {
+ console.log('ℹ️ 没有找到用户数据')
+ }
+ },
+ async getUserInfo() {
+ if (!this.token || !this.userId) {
+ console.warn('⚠️ 无法获取用户信息: 缺少 token 或 userId')
+ return
+ }
+
+ console.log('🔄 从服务器获取用户信息...')
+ try {
+ const res = await request.get(`/user/info/${this.userId}`)
+ console.log('✅ 用户信息响应接收:', {
+ code: res.data?.code,
+ hasData: !!res.data?.data
+ })
+
+ if (res.data && res.data.code === 200 && res.data.data) {
+ const userData = res.data.data
+ console.log('📊 用户数据接收:', {
+ username: userData.username,
+ is_admin: userData.is_admin,
+ is_banned: userData.is_banned
+ })
+
+ this.userInfo = {
+ id: userData.id,
+ username: userData.username,
+ email: userData.email,
+ avatar: userData.avatar || '',
+ is_banned: userData.is_banned || false,
+ ban_reason: userData.ban_reason || '',
+ ban_expiration: userData.ban_expiration || '',
+ is_admin: userData.is_admin || false,
+ qq_num: userData.qq_num || ''
+ }
+ Cookies.set('userInfo', JSON.stringify(this.userInfo), { expires: 3 })
+ console.log('✅ 用户信息更新成功')
+ } else {
+ console.error('❌ 获取用户信息失败:', res.data)
+ }
+ } catch (e) {
+ console.error('❌ 获取用户信息异常:', e)
+ // 可选:处理错误
+ }
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/client/style.css b/src/client/style.css
new file mode 100644
index 0000000..ff07b93
--- /dev/null
+++ b/src/client/style.css
@@ -0,0 +1,202 @@
+@import "tailwindcss";
+@import "tw-animate-css";
+
+@custom-variant dark (&:is(.dark *));
+
+/* 隐藏浏览器默认滚动条 */
+html {
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
+}
+
+html::-webkit-scrollbar {
+ display: none; /* Chrome, Safari, Opera */
+}
+
+body {
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch; /* 平滑滚动 */
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-popover: var(--popover);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-destructive: var(--destructive);
+ --color-destructive-foreground: var(--destructive-foreground);
+ --color-border: var(--border);
+ --color-input: var(--input);
+ --color-ring: var(--ring);
+ --color-chart-1: var(--chart-1);
+ --color-chart-2: var(--chart-2);
+ --color-chart-3: var(--chart-3);
+ --color-chart-4: var(--chart-4);
+ --color-chart-5: var(--chart-5);
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+ --color-sidebar: var(--sidebar);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-ring: var(--sidebar-ring);
+}
+
+:root {
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.141 0.005 285.823);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.141 0.005 285.823);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.141 0.005 285.823);
+ --primary: oklch(0.21 0.006 285.885);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.967 0.001 286.375);
+ --secondary-foreground: oklch(0.21 0.006 285.885);
+ --muted: oklch(0.967 0.001 286.375);
+ --muted-foreground: oklch(0.552 0.016 285.938);
+ --accent: oklch(0.967 0.001 286.375);
+ --accent-foreground: oklch(0.21 0.006 285.885);
+ --destructive: oklch(0.577 0.245 27.325);
+ --destructive-foreground: oklch(0.577 0.245 27.325);
+ --border: oklch(0.92 0.004 286.32);
+ --input: oklch(0.92 0.004 286.32);
+ --ring: oklch(0.705 0.015 286.067);
+ --chart-1: oklch(0.646 0.222 41.116);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --radius: 0.625rem;
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
+ --sidebar-primary: oklch(0.21 0.006 285.885);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.967 0.001 286.375);
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
+ --sidebar-border: oklch(0.92 0.004 286.32);
+ --sidebar-ring: oklch(0.705 0.015 286.067);
+}
+
+.dark {
+ --background: oklch(0.141 0.005 285.823);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.141 0.005 285.823);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.141 0.005 285.823);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.985 0 0);
+ --primary-foreground: oklch(0.21 0.006 285.885);
+ --secondary: oklch(0.274 0.006 286.033);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.274 0.006 286.033);
+ --muted-foreground: oklch(0.705 0.015 286.067);
+ --accent: oklch(0.274 0.006 286.033);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.396 0.141 25.723);
+ --destructive-foreground: oklch(0.637 0.237 25.331);
+ --border: oklch(0.274 0.006 286.033);
+ --input: oklch(0.274 0.006 286.033);
+ --ring: oklch(0.442 0.017 285.786);
+ --chart-1: oklch(0.488 0.243 264.376);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.21 0.006 285.885);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.274 0.006 286.033);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(0.274 0.006 286.033);
+ --sidebar-ring: oklch(0.442 0.017 285.786);
+}
+
+@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+
+ --primary: 221.2 83.2% 53.3%;
+ --primary-foreground: 210 40% 98%;
+
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+
+ --border:214.3 31.8% 91.4%;
+ --input:214.3 31.8% 91.4%;
+ --ring:221.2 83.2% 53.3%;
+ --radius: 1rem;
+ }
+
+ .dark {
+ --background:222.2 84% 4.9%;
+ --foreground:210 40% 98%;
+
+ --card:222.2 84% 4.9%;
+ --card-foreground:210 40% 98%;
+
+ --popover:222.2 84% 4.9%;
+ --popover-foreground:210 40% 98%;
+
+ --primary:217.2 91.2% 59.8%;
+ --primary-foreground:222.2 47.4% 11.2%;
+
+ --secondary:217.2 32.6% 17.5%;
+ --secondary-foreground:210 40% 98%;
+
+ --muted:217.2 32.6% 17.5%;
+ --muted-foreground:215 20.2% 65.1%;
+
+ --accent:217.2 32.6% 17.5%;
+ --accent-foreground:210 40% 98%;
+
+ --destructive:0 62.8% 30.6%;
+ --destructive-foreground:210 40% 98%;
+
+ --border:217.2 32.6% 17.5%;
+ --input:217.2 32.6% 17.5%;
+ --ring:224.3 76.3% 48%;
+ }
+}
diff --git a/src/client/types/barba.d.ts b/src/client/types/barba.d.ts
new file mode 100644
index 0000000..f22c662
--- /dev/null
+++ b/src/client/types/barba.d.ts
@@ -0,0 +1,47 @@
+declare module '@barba/core' {
+ interface IBarbaOptions {
+ debug?: boolean;
+ transitions?: Array<{
+ name?: string;
+ sync?: boolean;
+ from?: { namespace?: string[] };
+ to?: { namespace?: string[] };
+ leave?: (data: any) => Promise | any;
+ enter?: (data: any) => Promise | any;
+ after?: (data: any) => void;
+ }>;
+ views?: Array<{
+ namespace?: string;
+ beforeEnter?: () => void;
+ }>;
+ }
+
+ interface IBarbaHooks {
+ before: (callback: () => void) => void;
+ after: (callback: () => void) => void;
+ }
+
+ interface IBarba {
+ init: (options: IBarbaOptions) => void;
+ destroy: () => void;
+ hooks: IBarbaHooks;
+ }
+
+ const barba: IBarba;
+ export default barba;
+}
+
+declare module 'gsap' {
+ interface IGSAPTweenVars {
+ [key: string]: any;
+ }
+
+ interface IGSAP {
+ to: (target: any, vars: IGSAPTweenVars) => any;
+ from: (target: any, vars: IGSAPTweenVars) => any;
+ set: (target: any, vars: IGSAPTweenVars) => any;
+ }
+
+ const gsap: IGSAP;
+ export default gsap;
+}
\ No newline at end of file
diff --git a/src/client/types/gsap.d.ts b/src/client/types/gsap.d.ts
new file mode 100644
index 0000000..b5c8ca3
--- /dev/null
+++ b/src/client/types/gsap.d.ts
@@ -0,0 +1,14 @@
+declare module 'gsap' {
+ interface IGSAPTweenVars {
+ [key: string]: any;
+ }
+
+ interface IGSAP {
+ to: (target: any, vars: IGSAPTweenVars) => any;
+ from: (target: any, vars: IGSAPTweenVars) => any;
+ set: (target: any, vars: IGSAPTweenVars) => any;
+ }
+
+ const gsap: IGSAP;
+ export default gsap;
+}
\ No newline at end of file
diff --git a/src/client/utils/bilibiliHotUtils.ts b/src/client/utils/bilibiliHotUtils.ts
new file mode 100644
index 0000000..fc5c298
--- /dev/null
+++ b/src/client/utils/bilibiliHotUtils.ts
@@ -0,0 +1,81 @@
+import { ref, Ref } from 'vue'
+
+export interface BilibiliHotItem {
+ id: string
+ title: string
+ desc: string
+ cover: string
+ author: string
+ timestamp: number
+ hot: number
+ url: string
+ mobileUrl: string
+}
+
+export interface BilibiliHotResponse {
+ code: number
+ name: string
+ title: string
+ type: string
+ description: string
+ params: {
+ type: {
+ name: string
+ type: Record
+ }
+ }
+ link: string
+ total: number
+ updateTime: string
+ fromCache: boolean
+ data: BilibiliHotItem[]
+}
+
+const hotData: Ref = ref(null)
+const loading = ref(false)
+const error = ref(null)
+
+export const useBilibiliHot = () => {
+ const fetchHotData = async (type: string = '0') => {
+ loading.value = true
+ error.value = null
+
+ try {
+ const response = await fetch(`https://real-hot.lingke.ink/bilibili?type=${type}`)
+ if (!response.ok) {
+ throw new Error(`请求失败: ${response.status}`)
+ }
+
+ const data = await response.json()
+ hotData.value = data
+ } catch (err) {
+ console.error('获取B站热门数据失败:', err)
+ error.value = err instanceof Error ? err.message : '获取数据失败'
+ } finally {
+ loading.value = false
+ }
+ }
+
+ // 格式化热度数字
+ const formatHot = (hot: number): string => {
+ if (hot >= 10000) {
+ return (hot / 10000).toFixed(1) + '万'
+ }
+ return hot.toString()
+ }
+
+ // 格式化时间戳
+ const formatTimestamp = (timestamp: number): string => {
+ const date = new Date(timestamp)
+ return `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
+ }
+
+ return {
+ hotData,
+ loading,
+ error,
+ fetchHotData,
+ formatHot,
+ formatTimestamp
+ }
+}
\ No newline at end of file
diff --git a/src/client/utils/categoriesUtils.ts b/src/client/utils/categoriesUtils.ts
new file mode 100644
index 0000000..10200ea
--- /dev/null
+++ b/src/client/utils/categoriesUtils.ts
@@ -0,0 +1,32 @@
+import {
+ Search,
+ Home,
+ Gamepad2,
+ Bot,
+ Star,
+ Wrench,
+ Code
+} from 'lucide-vue-next'
+
+// 分类数据
+export const categories = [
+ { value: '搜索', label: '搜索', icon: Search },
+ { value: '娱乐', label: '娱乐', icon: Gamepad2 },
+ { value: 'AI', label: 'AI', icon: Bot },
+ { value: '评价', label: '评价', icon: Star },
+ { value: '工具', label: '工具', icon: Wrench },
+ { value: '开发', label: '开发', icon: Code }
+]
+
+// 获取当前分类标题
+export const getCategoryTitle = (selectedCategory: string, searchQuery: string) => {
+ if (searchQuery) {
+ return `搜索结果: "${searchQuery}"`
+ }
+
+ if (selectedCategory === 'all') {
+ return '所有内容'
+ }
+
+ return selectedCategory
+}
\ No newline at end of file
diff --git a/src/client/utils/contentViewUtils.ts b/src/client/utils/contentViewUtils.ts
new file mode 100644
index 0000000..eefe674
--- /dev/null
+++ b/src/client/utils/contentViewUtils.ts
@@ -0,0 +1,70 @@
+import { ref } from 'vue'
+
+// 视图模式
+export const useViewMode = () => {
+ const viewMode = ref('grid')
+
+ const setGridView = () => {
+ viewMode.value = 'grid'
+ }
+
+ const setListView = () => {
+ viewMode.value = 'list'
+ }
+
+ return {
+ viewMode,
+ setGridView,
+ setListView
+ }
+}
+
+// 搜索和过滤
+export const useContentFilters = () => {
+ const selectedCategory = ref('all')
+ const searchQuery = ref('')
+
+ // 搜索过滤
+ const filterSites = () => {
+ // 如果搜索框有内容,自动切换到"全部"分类以显示所有匹配结果
+ if (searchQuery.value.trim()) {
+ selectedCategory.value = 'all'
+ }
+ }
+
+ // 重置筛选条件
+ const resetFilters = () => {
+ searchQuery.value = ''
+ selectedCategory.value = 'all'
+ }
+
+ return {
+ selectedCategory,
+ searchQuery,
+ filterSites,
+ resetFilters
+ }
+}
+
+// 移动端触摸滚动增强
+export const enhanceTouchScrolling = () => {
+ // 增强移动端触摸滚动体验
+ const setupTouchScrolling = () => {
+ const scrollContainer = document.querySelector('.category-scroll-container')
+ if (scrollContainer) {
+ // 防止触摸事件冲突
+ scrollContainer.addEventListener('touchstart', (e) => {
+ e.stopPropagation()
+ }, { passive: true })
+
+ // 确保触摸滚动正常工作
+ scrollContainer.addEventListener('touchmove', (e) => {
+ e.stopPropagation()
+ }, { passive: true })
+ }
+ }
+
+ return {
+ setupTouchScrolling
+ }
+}
\ No newline at end of file
diff --git a/src/client/utils/featuredContentUtils.ts b/src/client/utils/featuredContentUtils.ts
new file mode 100644
index 0000000..55aa5ca
--- /dev/null
+++ b/src/client/utils/featuredContentUtils.ts
@@ -0,0 +1,41 @@
+import { featuredSites, getSiteIcon } from './featuredSites'
+
+// 获取推荐网站
+export const getRecommendedSites = () => {
+ const recommendedIds = ['steam', 'docker', 'github']
+ return featuredSites
+ .filter(site => recommendedIds.includes(site.id))
+ .sort((a, b) => b.rating - a.rating)
+}
+
+// 根据搜索和分类过滤网站
+export const getFilteredSites = (selectedCategory: string, searchQuery: string) => {
+ let sites = [...featuredSites]
+
+ // 按分类过滤
+ if (selectedCategory !== 'all') {
+ sites = sites.filter(site => site.category === selectedCategory)
+ }
+
+ // 按搜索关键词过滤
+ if (searchQuery.trim()) {
+ const query = searchQuery.toLowerCase()
+ sites = sites.filter(site =>
+ site.title.toLowerCase().includes(query) ||
+ site.description.toLowerCase().includes(query) ||
+ site.tags.some(tag => tag.toLowerCase().includes(query))
+ )
+ }
+
+ // 优先显示可在中国大陆访问的网站
+ const chinaAccessible = sites.filter(site => !site.unchina)
+ const chinaRestricted = sites.filter(site => site.unchina)
+
+ return [...chinaAccessible, ...chinaRestricted]
+}
+
+// 获取网站图标
+export { getSiteIcon }
+
+// 导出原始数据以供参考
+export { featuredSites }
\ No newline at end of file
diff --git a/src/client/utils/featuredSites.ts b/src/client/utils/featuredSites.ts
new file mode 100644
index 0000000..d1608f9
--- /dev/null
+++ b/src/client/utils/featuredSites.ts
@@ -0,0 +1,1893 @@
+// 精选网站数据模型
+export interface FeaturedSite {
+ id: string; // 唯一标识符
+ url: string; // 网站URL
+ title: string; // 网站标题
+ description: string; // 网站描述
+ icon?: string; // 网站图标URL(可选,优先使用Favicon.im)
+ category: string; // 分类
+ tags: string[]; // 标签
+ rating: number; // 评分 (1-5)
+ featured: boolean; // 是否为特别推荐
+ addedDate: string; // 添加日期
+ screenshots?: string[]; // 网站截图
+ detailedDescription?: string; // 详细描述
+ unchina?: boolean; // 是否可能无法在中国大陆使用
+}
+
+// 工具函数:从URL生成Favicon.im图标URL
+export function getFaviconUrl(url: string, larger: boolean = false): string {
+ try {
+ const domain = new URL(url).hostname
+ return `https://favicon.im/${domain}${larger ? '?larger=true' : ''}`
+ } catch (error) {
+ // 如果URL解析失败,返回默认图标
+ return 'https://favicon.im/example.com'
+ }
+}
+
+// 工具函数:获取网站的图标URL(优先使用Favicon.im)
+export function getSiteIcon(site: FeaturedSite, larger: boolean = false): string {
+ // 如果网站有自定义图标,优先使用
+ if (site.icon) {
+ return site.icon
+ }
+ // 否则使用Favicon.im服务
+ return getFaviconUrl(site.url, larger)
+}
+
+// 网站分类常量
+export const CATEGORIES = {
+ ALL: "全部",
+ SEARCH: "搜索",
+ ENTERTAINMENT: "娱乐",
+ AI: "AI",
+ REVIEW: "评价",
+ TOOLS: "工具",
+ DEVELOPMENT: "开发"
+} as const;
+
+// 精选网站数据
+export const featuredSites: FeaturedSite[] = [
+ // 工具类网站
+ {
+ id: "figma",
+ url: "https://www.figma.com",
+ title: "Figma",
+ description: "基于浏览器的协作式界面设计工具",
+ detailedDescription: "Figma是一个基于浏览器的协作式设计工具,专门用于UI/UX设计。它支持实时协作,多个设计师可以同时编辑同一个设计文件,大大提高了团队协作效率。Figma提供强大的设计系统功能,支持组件库、样式管理和设计规范。它的原型功能允许设计师创建交互式原型,进行用户测试和演示。Figma还提供丰富的插件生态系统,扩展了工具的功能。对于设计团队来说,Figma已经成为行业标准,特别是在远程协作和设计系统管理方面表现出色。",
+ category: "工具",
+ tags: ["设计", "协作", "UI/UX"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "notion",
+ url: "https://www.notion.so",
+ title: "Notion",
+ description: "全能的工作空间,集笔记、数据库、项目管理于一体",
+ detailedDescription: "Notion是一个全能的工作空间平台,将笔记、数据库、项目管理、知识库等功能整合在一个应用中。它采用块级编辑系统,用户可以自由组合各种内容块,创建个性化的页面和数据库。Notion支持团队协作,多人可以同时编辑和评论。它提供丰富的模板库,涵盖个人管理、团队协作、项目管理等各种场景。Notion的数据库功能强大,支持多种视图和关联关系,可以构建复杂的信息管理系统。对于个人用户和团队来说,Notion是一个灵活且强大的生产力工具。",
+ category: "工具",
+ tags: ["笔记", "协作", "生产力"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "airtable",
+ url: "https://airtable.com",
+ title: "Airtable",
+ description: "灵活的数据库和电子表格混合工具,适合团队协作",
+ detailedDescription: "Airtable是一个结合了数据库功能和电子表格界面的协作平台,让用户可以轻松创建和共享关系型数据库。它提供直观的界面,无需编程知识即可构建复杂的数据模型。Airtable支持多种视图模式,包括网格视图、看板视图、日历视图等,满足不同场景的需求。它的自动化功能可以设置触发器和操作,减少重复工作。Airtable还提供丰富的集成选项,可以与Slack、Google Drive等工具连接。对于项目管理、内容规划、客户关系管理等场景,Airtable提供了灵活且强大的解决方案。",
+ category: "工具",
+ tags: ["数据库", "协作", "项目管理"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "miro",
+ url: "https://miro.com",
+ title: "Miro",
+ description: "在线协作白板平台,适合远程团队头脑风暴和规划",
+ detailedDescription: "Miro是一个无限画布的在线协作白板平台,为远程团队提供视觉协作空间。它支持实时多人协作,团队成员可以同时在白板上添加内容、评论和互动。Miro提供丰富的模板,涵盖头脑风暴、用户故事地图、敏捷规划等多种场景。它的集成功能强大,可以与Jira、Asana、Slack等工具无缝连接。Miro支持多种内容类型,包括便签、图形、文本、图片、PDF等,满足不同的表达需求。对于分布式团队和远程工作者来说,Miro是进行创意思考、项目规划和视觉协作的理想平台。",
+ category: "工具",
+ tags: ["白板", "协作", "远程工作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "linear",
+ url: "https://linear.app",
+ title: "Linear",
+ description: "现代化的项目管理工具,专为软件开发团队设计",
+ detailedDescription: "Linear是一个为软件开发团队设计的现代化项目管理工具,以其简洁的界面和高效的工作流程著称。它提供直观的问题跟踪系统,支持敏捷开发方法,包括冲刺规划、迭代周期和团队目标。Linear的性能极为出色,操作流畅快速,支持键盘快捷键,提高工作效率。它的路线图功能允许团队规划长期项目和目标。Linear与GitHub、Figma等工具集成,实现开发流程的无缝衔接。对于追求效率和简洁的软件开发团队来说,Linear提供了专注于核心功能的项目管理解决方案。",
+ category: "工具",
+ tags: ["项目管理", "软件开发", "敏捷"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "obsidian",
+ url: "https://obsidian.md",
+ title: "Obsidian",
+ description: "基于本地Markdown文件的知识库工具,支持双向链接",
+ detailedDescription: "Obsidian是一个强大的知识管理工具,基于本地存储的Markdown文件,确保数据隐私和所有权。它的核心特性是双向链接,允许用户在笔记之间创建连接,形成知识网络。Obsidian支持图形视图,可视化笔记之间的关系,帮助发现新的联系。它提供丰富的插件系统,用户可以扩展功能,如日历、看板、模板等。Obsidian的工作区概念允许用户为不同项目创建独立的环境。对于研究人员、作家、学生和知识工作者来说,Obsidian是构建个人知识库和思考工具的理想选择。",
+ category: "工具",
+ tags: ["笔记", "知识管理", "Markdown"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "framer",
+ url: "https://www.framer.com",
+ title: "Framer",
+ description: "高级原型设计和网站构建工具,无需编码",
+ detailedDescription: "Framer是一个结合了设计和开发功能的工具,允许设计师创建高保真交互原型和功能性网站。它提供直观的可视化界面,同时支持代码自定义,满足不同技能水平的用户需求。Framer的智能组件系统让设计师可以创建响应式设计,适应不同屏幕尺寸。它的动画工具非常强大,可以创建流畅的过渡和微交互。Framer还提供网站托管服务,用户可以直接发布设计作品。对于UI/UX设计师和希望快速构建网站原型的团队来说,Framer提供了从概念到实现的完整解决方案。",
+ category: "工具",
+ tags: ["设计", "原型", "网站构建"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "webflow",
+ url: "https://webflow.com",
+ title: "Webflow",
+ description: "可视化网站设计和开发平台,无需编码",
+ detailedDescription: "Webflow是一个可视化网站设计和开发平台,让设计师可以在不编写代码的情况下创建专业级网站。它提供直观的拖放界面,同时生成干净的HTML、CSS和JavaScript代码。Webflow的CMS功能强大,支持动态内容和数据库集合,适合构建博客、电子商务和内容网站。它的响应式设计工具让用户可以为不同设备优化网站布局。Webflow还提供托管服务,包括CDN、SSL证书和表单处理。对于设计师、自由职业者和小型团队来说,Webflow是一个从设计到发布的完整网站解决方案。",
+ category: "工具",
+ tags: ["网站设计", "CMS", "无代码"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "loom",
+ url: "https://www.loom.com",
+ title: "Loom",
+ description: "屏幕录制和视频消息工具,简化远程沟通",
+ detailedDescription: "Loom是一个屏幕录制和视频消息工具,让用户可以快速录制屏幕、摄像头或两者结合的视频,并立即分享链接。它特别适合异步沟通,用户可以在自己方便的时间录制和观看视频。Loom提供浏览器扩展和桌面应用,使录制过程简单快捷。它的视频管理系统允许组织和分类录像,创建团队工作空间。Loom还提供互动功能,如评论、表情反应和时间戳。对于远程团队、教育工作者和客户支持人员来说,Loom是传达复杂信息和演示的有效工具,减少了会议需求和沟通障碍。",
+ category: "工具",
+ tags: ["视频", "远程工作", "沟通"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "coda",
+ url: "https://coda.io",
+ title: "Coda",
+ description: "集文档、电子表格和应用于一体的协作平台",
+ detailedDescription: "Coda是一个创新的文档平台,将文字处理、电子表格和数据库功能融为一体。它允许用户在一个文档中创建动态表格、自动化工作流程和交互式应用。Coda的公式系统强大而灵活,可以构建复杂的逻辑和计算。它支持团队协作,多人可以同时编辑和评论。Coda提供丰富的模板和构建块,帮助用户快速启动项目。它还支持与Slack、GitHub等工具的集成,实现工作流程的自动化。对于希望减少工具切换、统一信息管理的团队来说,Coda提供了一个灵活且强大的协作平台。",
+ category: "工具",
+ tags: ["文档", "协作", "工作流程"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+
+ // AI类网站
+ {
+ id: "chatgpt",
+ url: "https://chat.openai.com",
+ title: "ChatGPT",
+ description: "OpenAI开发的对话式AI助手,可回答问题和提供帮助",
+ detailedDescription: "ChatGPT是由OpenAI开发的先进对话式AI助手,基于GPT(生成式预训练变换器)模型。它能够理解和生成自然语言,回答问题,提供解释,创作内容,辅助编程等。ChatGPT可以记住对话上下文,进行连贯的交流,适应用户的需求和风格。它在教育、创意写作、编程辅助、信息查询等方面有广泛应用。ChatGPT不断通过更新迭代提升能力,如GPT-4模型带来了更强的理解力和创造力。对于需要智能对话助手的个人和专业用户来说,ChatGPT提供了强大而灵活的AI支持。",
+ category: "AI",
+ tags: ["AI助手", "对话", "GPT"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "midjourney",
+ url: "https://www.midjourney.com",
+ title: "Midjourney",
+ description: "AI图像生成工具,通过文本描述创建艺术作品",
+ detailedDescription: "Midjourney是一个强大的AI图像生成工具,允许用户通过文本提示创建高质量的艺术作品。它使用深度学习技术将文字描述转化为视觉图像,风格多样,从写实到抽象都能呈现。Midjourney的操作基于Discord平台,用户通过简单的命令与AI交互。它支持参数调整,用户可以控制图像的风格、比例、细节程度等。Midjourney定期更新模型,不断提升图像质量和多样性。对于设计师、艺术家、创意工作者和内容创作者来说,Midjourney是一个激发创意、辅助设计的强大工具。",
+ category: "AI",
+ tags: ["图像生成", "艺术", "创意"],
+ rating: 4.8,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "claude",
+ url: "https://claude.ai",
+ title: "Claude",
+ description: "Anthropic开发的AI助手,专注于有用、无害和诚实的对话",
+ detailedDescription: "Claude是由Anthropic开发的AI助手,以其自然的对话能力和负责任的回应而著称。它被设计为有用、无害和诚实,能够理解复杂指令并提供深思熟虑的回答。Claude擅长长文本处理,可以分析和总结大量内容,适合文档审阅和研究辅助。它支持多种语言,并能理解上下文,进行连贯的对话。Claude特别注重安全性和减少有害输出,适合需要可靠AI助手的专业环境。对于寻求高质量对话体验和内容创作支持的用户来说,Claude提供了平衡的智能和责任感。",
+ category: "AI",
+ tags: ["AI助手", "对话", "内容创作"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "perplexity",
+ url: "https://www.perplexity.ai",
+ title: "Perplexity AI",
+ description: "AI驱动的搜索引擎,提供有来源的答案和实时信息",
+ detailedDescription: "Perplexity AI是一个创新的AI搜索引擎,结合了大语言模型和网络搜索功能,为用户提供有来源依据的答案。它不仅回答问题,还提供相关信息的链接和引用,增强可信度和透明度。Perplexity支持实时信息查询,能够获取最新的新闻和数据。它的对话式界面允许用户进行后续提问和澄清,实现连贯的信息探索体验。Perplexity还提供专业模式,针对学术研究和深度分析提供更详细的回答。对于需要快速获取可靠信息和深入研究特定主题的用户来说,Perplexity AI是传统搜索引擎的强大替代品。",
+ category: "AI",
+ tags: ["搜索", "信息", "研究"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "huggingface",
+ url: "https://huggingface.co",
+ title: "Hugging Face",
+ description: "AI社区和模型库,提供开源机器学习工具和资源",
+ detailedDescription: "Hugging Face是机器学习和人工智能领域的中心平台,提供开源模型、数据集和工具。它的模型中心包含数万个预训练模型,涵盖自然语言处理、计算机视觉、音频处理等多个领域。Hugging Face的Transformers库是NLP领域的标准工具,支持多种深度学习框架。平台提供Spaces功能,允许用户部署和分享AI应用演示。它还有活跃的社区和论坛,促进知识分享和协作。对于AI研究人员、开发者和学习者来说,Hugging Face是获取资源、分享成果和学习最新技术的重要平台。",
+ category: "AI",
+ tags: ["机器学习", "开源", "模型"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "stability",
+ url: "https://stability.ai",
+ title: "Stability AI",
+ description: "开源AI图像生成技术的领导者,Stable Diffusion的创建者",
+ detailedDescription: "Stability AI是开源人工智能的领导者,以开发Stable Diffusion模型而闻名,这是一个强大的文本到图像生成系统。公司致力于推动AI民主化,将先进技术提供给广大开发者和创作者。Stability AI的产品包括DreamStudio(用户友好的图像生成平台)和各种专业API服务。他们的模型支持多种创意应用,从艺术创作到产品设计。Stability AI积极与研究机构合作,推动AI技术的边界。对于希望利用最新图像生成技术的创意专业人士和开发者来说,Stability AI提供了强大而灵活的工具和资源。",
+ category: "AI",
+ tags: ["图像生成", "开源", "创意"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "runway",
+ url: "https://runwayml.com",
+ title: "Runway",
+ description: "创意AI工具套件,用于视频编辑、生成和特效",
+ detailedDescription: "Runway是一个面向创意专业人士的AI平台,提供视频编辑、生成和特效工具。它的Gen-2模型可以从文本或图像生成视频,创造前所未有的视觉内容。Runway的Magic Tools套件包括背景移除、物体擦除、画面扩展等功能,简化了复杂的视频编辑任务。它支持协作工作流程,团队成员可以共享项目和资源。Runway定期更新其AI模型,不断提升生成内容的质量和多样性。对于电影制作者、视频编辑、动画师和数字艺术家来说,Runway提供了革命性的创意工具,大幅提高工作效率和创作可能性。",
+ category: "AI",
+ tags: ["视频", "创意", "编辑"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "synthesia",
+ url: "https://www.synthesia.io",
+ title: "Synthesia",
+ description: "AI视频生成平台,通过文本创建专业视频和虚拟人物",
+ detailedDescription: "Synthesia是一个AI视频生成平台,允许用户通过简单的文本输入创建专业质量的视频内容。它提供多种虚拟人物(AI演示者),能够用自然的语音和表情传达信息。Synthesia支持多种语言和口音,使内容本地化变得简单。平台提供模板库和场景定制,用户可以根据需求调整视频风格和布局。Synthesia的企业版支持品牌定制和高级编辑功能。对于培训视频、营销内容、产品演示和教育材料的创作者来说,Synthesia大大降低了视频制作的成本和复杂度,同时保持专业质量。",
+ category: "AI",
+ tags: ["视频生成", "虚拟人物", "内容创作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "elevenlabs",
+ url: "https://elevenlabs.io",
+ title: "ElevenLabs",
+ description: "AI语音生成和克隆平台,创建自然逼真的语音内容",
+ detailedDescription: "ElevenLabs是一个先进的AI语音技术平台,提供超逼真的文本转语音和语音克隆功能。它的语音合成技术能够生成带有自然情感和语调变化的语音,几乎无法与人类语音区分。ElevenLabs支持多种语言和口音,适合全球内容创作。平台提供语音库,用户可以选择不同风格和特点的预设声音。它的语音克隆功能允许用户从短音频样本创建个性化语音模型。ElevenLabs适用于有声读物、播客、视频配音、游戏角色和虚拟助手等多种应用场景。对于需要高质量语音内容的创作者和企业来说,ElevenLabs提供了突破性的AI语音解决方案。",
+ category: "AI",
+ tags: ["语音", "配音", "文本转语音"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "anthropic",
+ url: "https://www.anthropic.com",
+ title: "Anthropic",
+ description: "开发Claude AI助手的公司,专注于AI安全和研究",
+ detailedDescription: "Anthropic是一家AI研究公司,致力于开发可靠、可解释和可控的AI系统。公司由前OpenAI研究人员创立,专注于AI安全和负责任的AI发展。Anthropic的旗舰产品是Claude AI助手,以其自然对话能力和安全性能而著称。公司采用'宪法AI'方法,通过人类反馈和明确的价值观指导AI行为。Anthropic积极参与AI安全研究,发表关于AI风险评估和缓解的学术论文。对于关注AI发展方向和寻求负责任AI解决方案的组织和个人来说,Anthropic代表了以安全和人类价值为中心的AI创新方向。",
+ category: "AI",
+ tags: ["AI研究", "安全", "对话系统"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+
+ // 开发类网站
+ {
+ id: "github",
+ url: "https://github.com",
+ title: "GitHub",
+ description: "全球最大的代码托管平台,支持Git版本控制和协作开发",
+ detailedDescription: "GitHub是全球最大的代码托管和协作开发平台,为超过8300万开发者提供服务。它基于Git版本控制系统,提供代码仓库托管、分支管理、代码审查和项目管理功能。GitHub的核心特性包括Issues(问题跟踪)、Pull Requests(代码审查和合并)、Actions(自动化工作流)和Pages(静态网站托管)。平台支持开源社区发展,同时为企业提供私有仓库和团队协作工具。GitHub已成为现代软件开发的中心枢纽,不仅用于代码管理,还用于文档共享、项目规划和社区建设。对于开发者来说,GitHub既是技术工具,也是展示作品和参与开源项目的社交平台。",
+ category: "开发",
+ tags: ["代码托管", "Git", "协作"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "stackoverflow",
+ url: "https://stackoverflow.com",
+ title: "Stack Overflow",
+ description: "程序员问答社区,解决编程问题的最大资源库",
+ detailedDescription: "Stack Overflow是全球最大的程序员问答社区,每月有超过1亿访问者。平台采用问答形式,用户可以提问、回答问题,并通过投票系统突出最有价值的内容。Stack Overflow的声誉系统鼓励高质量的贡献,用户通过有用的回答和问题获得声誉点数和徽章。网站涵盖几乎所有编程语言、框架和技术问题,成为开发者解决技术难题的首选资源。除了技术问答,Stack Overflow还提供开发者调查、工作板块和团队协作版本。对于从初学者到专业开发者的所有技术人员来说,Stack Overflow是学习、解决问题和分享知识的重要平台。",
+ category: "开发",
+ tags: ["问答", "社区", "编程"],
+ rating: 4.9,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "vercel",
+ url: "https://vercel.com",
+ title: "Vercel",
+ description: "前端开发和部署平台,专为现代Web应用优化",
+ detailedDescription: "Vercel是一个为前端开发者设计的云平台,专注于提供无缝的开发、预览和部署体验。它为Next.js、React、Vue等现代框架提供优化的托管环境,自动处理构建过程和全球CDN分发。Vercel的预览部署功能为每个Git提交自动生成唯一URL,便于团队审查和测试变更。平台提供无服务器函数支持,允许开发者创建API端点而无需管理服务器。Vercel的分析工具提供性能监控和用户体验数据。对于追求高性能、现代化开发流程的前端团队来说,Vercel提供了从开发到部署的完整解决方案,大大简化了工作流程。",
+ category: "开发",
+ tags: ["前端", "部署", "托管"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "netlify",
+ url: "https://www.netlify.com",
+ title: "Netlify",
+ description: "现代网站开发和托管平台,自动化部署流程",
+ detailedDescription: "Netlify是一个面向现代web项目的开发和托管平台,提供从本地开发到全球部署的完整工作流。它与Git仓库直接集成,实现持续部署,每次代码推送后自动构建和发布网站。Netlify的边缘网络确保全球快速加载,内置CDN和自动HTTPS加密保障性能和安全。平台提供丰富的功能,包括表单处理、身份验证、无服务器函数和大型媒体优化。Netlify还支持分支部署和预览URL,便于团队协作和测试。对于静态网站、JAMstack应用和前端项目,Netlify提供了简单而强大的托管解决方案,使开发者能够专注于代码而非基础设施。",
+ category: "开发",
+ tags: ["托管", "静态网站", "JAMstack"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "codepen",
+ url: "https://codepen.io",
+ title: "CodePen",
+ description: "在线代码编辑器和社区,专注于前端开发",
+ detailedDescription: "CodePen是一个面向前端开发者的在线代码编辑器和社交开发环境。它允许用户在浏览器中编写HTML、CSS和JavaScript代码,并实时预览结果。CodePen的核心功能是Pen(代码片段),用户可以创建、保存和分享自己的前端作品。平台支持预处理器(如Sass、LESS、TypeScript)和流行框架(如React、Vue、Angular)。CodePen的社区功能丰富,用户可以关注他人、收藏作品、参与挑战和探索精选内容。对于前端开发者来说,CodePen既是学习和实验的工具,也是展示创意和寻找灵感的平台,特别适合原型设计、概念验证和技术分享。",
+ category: "开发",
+ tags: ["编辑器", "前端", "社区"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "replit",
+ url: "https://replit.com",
+ title: "Replit",
+ description: "在线IDE和协作编码平台,支持多种编程语言",
+ detailedDescription: "Replit是一个功能强大的在线集成开发环境(IDE),支持超过50种编程语言,从Python、JavaScript到C++、Java等。它提供完整的开发环境,包括代码编辑器、文件系统、终端和版本控制,全部在浏览器中运行,无需本地安装。Replit的实时协作功能允许多人同时编辑同一个项目,类似Google Docs的体验。平台内置了部署功能,用户可以一键托管web应用和API。Replit还提供教育工具,支持课堂管理和作业分发。对于学生、教师、远程团队和希望快速原型开发的程序员来说,Replit提供了便捷、强大且无需配置的编码环境。",
+ category: "开发",
+ tags: ["IDE", "协作", "云开发"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "devto",
+ url: "https://dev.to",
+ title: "DEV Community",
+ description: "开发者社区平台,分享文章、讨论和最佳实践",
+ detailedDescription: "DEV Community (dev.to) 是一个开放、包容的开发者社区平台,专注于技术内容分享和讨论。它采用博客风格,允许开发者发布文章、教程、经验分享和职业建议。平台支持Markdown编写,集成了代码高亮和嵌入功能。DEV的互动功能包括评论、点赞、收藏和关注,促进社区参与。它的标签系统帮助用户发现特定技术领域的内容,如JavaScript、Python、Web开发等。DEV强调积极、支持性的社区文化,欢迎各级开发者参与。对于希望分享知识、学习新技术或与同行交流的开发者来说,DEV提供了一个友好、专业的在线社区空间。",
+ category: "开发",
+ tags: ["社区", "博客", "技术文章"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "digitalocean",
+ url: "https://www.digitalocean.com",
+ title: "DigitalOcean",
+ description: "简单易用的云服务平台,专为开发者设计",
+ detailedDescription: "DigitalOcean是一个面向开发者的云计算平台,以其简单性和可预测的定价而著称。它提供虚拟服务器(Droplets)、托管Kubernetes、托管数据库、对象存储和负载均衡器等核心云服务。DigitalOcean的控制面板直观易用,新用户可以在几分钟内启动服务器和部署应用。平台的文档和教程库(Community)是业内最全面的技术资源之一,涵盖各种开发和系统管理主题。DigitalOcean特别适合中小型企业、初创公司和个人开发者,提供了AWS和Azure等大型云服务的简化替代方案。对于需要可靠、易于管理且价格透明的云基础设施的开发团队来说,DigitalOcean是一个理想的选择。",
+ category: "开发",
+ tags: ["云服务", "服务器", "基础设施"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "hashnode",
+ url: "https://hashnode.com",
+ title: "Hashnode",
+ description: "开发者博客平台,支持个人技术博客和社区互动",
+ detailedDescription: "Hashnode是一个为开发者设计的现代博客平台,允许用户创建个人技术博客,同时保持对内容的完全所有权。它支持自定义域名,博客内容可以映射到用户自己的域名下,而不是子域名。Hashnode的编辑器支持Markdown、代码高亮和嵌入媒体,提供流畅的写作体验。平台内置了开发者社区,文章可以在个人博客和社区feed中同时展示,增加曝光度。Hashnode的货币化功能允许作者通过赞助和会员订阅获得收入。它还提供分析工具,帮助博主了解读者行为和文章表现。对于希望建立个人品牌、分享技术知识并与同行交流的开发者来说,Hashnode提供了一个专业、无广告的博客解决方案。",
+ category: "开发",
+ tags: ["博客", "写作", "社区"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "glitch",
+ url: "https://glitch.com",
+ title: "Glitch",
+ description: "创意应用开发和托管平台,强调简单性和协作",
+ detailedDescription: "Glitch是一个创新的web应用开发和托管平台,专注于简化创建过程和促进协作。它提供浏览器内编码环境,支持HTML、CSS、JavaScript和Node.js等技术。Glitch的特色是即时部署,代码更改会立即生效并在线可访问,无需手动部署步骤。平台支持实时协作,多人可以同时编辑项目,类似Google Docs的体验。Glitch鼓励代码重用和学习,用户可以查看、复制和修改公开项目。它还提供简单的数据库集成、环境变量管理和HTTPS支持。对于原型开发、教学演示、API实验和小型web应用,Glitch提供了一个低门槛、高效率的开发环境,特别适合初学者和快速创意实现。",
+ category: "开发",
+ tags: ["web开发", "协作", "托管"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+
+ // 娱乐类网站
+ {
+ id: "bilibili",
+ url: "https://www.bilibili.com",
+ title: "哔哩哔哩",
+ description: "中国领先的视频平台,涵盖动画、游戏、科技等多种内容",
+ detailedDescription: "哔哩哔哩(B站)是中国领先的视频平台,最初以ACG(动画、漫画、游戏)内容为主,现已发展为涵盖多元内容的综合性平台。B站特色的弹幕系统让观众可以在视频上方发送评论,创造独特的互动体验。平台拥有丰富的UP主生态,创作者涵盖知识分享、生活记录、娱乐、游戏等多个领域。B站的社区文化浓厚,用户粘性高,形成了独特的次元文化和社区氛围。平台还提供直播、番剧、影视、专栏等多种内容形式。对于年轻用户群体,B站不仅是娱乐平台,也是学习交流和文化社区的重要场所。",
+ category: "娱乐",
+ tags: ["视频", "弹幕", "社区"],
+ rating: 4.8,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "youtube",
+ url: "https://www.youtube.com",
+ title: "YouTube",
+ description: "全球最大的视频分享平台,内容覆盖几乎所有领域",
+ detailedDescription: "YouTube是全球最大的视频分享平台,每月活跃用户超过20亿。平台内容极其丰富,涵盖教育、娱乐、音乐、游戏、科技、生活方式等几乎所有领域。YouTube的创作者生态系统完善,从个人博主到大型媒体公司都在平台上发布内容。它的推荐算法根据用户观看历史和偏好提供个性化内容。YouTube提供多种互动方式,包括评论、点赞、分享和频道订阅。平台还有YouTube Premium订阅服务,提供无广告观看、背景播放和YouTube Music等增值功能。对于内容创作者,YouTube提供了广告分成、频道会员、超级感谢等多种变现途径。作为视频领域的主导平台,YouTube既是娱乐来源,也是学习工具和文化影响力的重要渠道。",
+ category: "娱乐",
+ tags: ["视频", "创作者", "直播"],
+ rating: 4.9,
+ featured: true,
+ addedDate: "2025-08-05",
+ unchina: true
+ },
+ {
+ id: "netflix",
+ url: "https://www.netflix.com",
+ title: "Netflix",
+ description: "全球领先的流媒体服务,提供电影、剧集和原创内容",
+ detailedDescription: "Netflix是全球领先的流媒体娱乐服务,提供丰富的电影、电视剧、纪录片和原创内容。平台以订阅制运营,用户支付月费即可无限观看所有内容,无广告干扰。Netflix以其高质量的原创内容著称,如《怪奇物语》、《纸牌屋》和《王冠》等,多次获得艾美奖和奥斯卡提名。平台采用先进的推荐算法,根据用户观看历史提供个性化内容建议。Netflix支持多设备访问,允许下载部分内容供离线观看。它的界面简洁直观,支持多种语言和字幕选项。作为娱乐产业的重要参与者,Netflix不仅改变了人们消费视频内容的方式,也对全球影视制作产生了深远影响。",
+ category: "娱乐",
+ tags: ["流媒体", "电影", "剧集"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "spotify",
+ url: "https://www.spotify.com",
+ title: "Spotify",
+ description: "全球最大的音乐流媒体服务,提供数百万首歌曲和播客",
+ detailedDescription: "Spotify是全球领先的音乐流媒体服务,拥有超过8000万首歌曲和400万个播客节目。平台提供免费(含广告)和付费订阅两种模式,付费用户可享受无广告、更高音质和离线下载等特权。Spotify的个性化推荐系统是其核心优势,通过'发现周刊'和'每日混音'等功能,根据用户听歌习惯推荐新内容。平台支持创建和分享播放列表,关注艺人和好友的活动。Spotify还大力投资播客领域,成为音频内容的综合平台。它的社交功能允许用户分享正在收听的内容,查看好友的音乐品味。对于音乐爱好者来说,Spotify不仅是听歌工具,也是发现新音乐和连接音乐社区的平台。",
+ category: "娱乐",
+ tags: ["音乐", "播客", "流媒体"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "twitch",
+ url: "https://www.twitch.tv",
+ title: "Twitch",
+ description: "领先的直播平台,主要面向游戏、创意和生活内容",
+ detailedDescription: "Twitch是全球最大的直播流媒体平台,最初专注于游戏直播,现已扩展到创意艺术、音乐、聊天节目和现实生活内容。平台的互动性极强,观众可以通过聊天室实时与主播和其他观众交流。Twitch的订阅和打赏系统为创作者提供了收入来源,粉丝可以通过订阅频道支持喜爱的主播。平台的特色功能包括Clips(精彩片段)、Extensions(扩展应用)和Raids(主播引导观众访问其他频道)。Twitch社区文化独特,发展出专属的表情符号和术语。对于游戏玩家、内容创作者和寻求互动娱乐体验的观众来说,Twitch提供了一个实时连接和分享热情的平台。",
+ category: "娱乐",
+ tags: ["直播", "游戏", "社区"],
+ rating: 4.6,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "douyin",
+ url: "https://www.douyin.com",
+ title: "抖音",
+ description: "中国领先的短视频平台,提供多样化的创意内容",
+ detailedDescription: "抖音是中国领先的短视频平台,由字节跳动开发运营,以其算法推荐和创意短视频内容而闻名。平台支持15秒至几分钟不等的视频创作,内容涵盖舞蹈、美食、旅游、教育、搞笑等多个领域。抖音的特色在于强大的视频编辑工具,用户可以添加音乐、特效、滤镜等元素,即使是普通用户也能创作出有趣的内容。平台的'挑战'功能鼓励用户参与特定主题的创作,形成病毒式传播。抖音还整合了直播和电商功能,创作者可以通过带货和打赏获得收入。作为中国最具影响力的社交媒体之一,抖音不仅是娱乐平台,也成为品牌营销和个人表达的重要渠道。",
+ category: "娱乐",
+ tags: ["短视频", "社交", "创意"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "tiktok",
+ url: "https://www.tiktok.com",
+ title: "TikTok",
+ description: "全球流行的短视频平台,以创意内容和趋势挑战著称",
+ detailedDescription: "TikTok是抖音的国际版本,已成为全球最受欢迎的短视频平台之一,在150多个国家拥有超过10亿月活跃用户。平台以15秒至3分钟的短视频为主,内容涵盖舞蹈、挑战、教程、喜剧等多种形式。TikTok的核心是其强大的推荐算法,通过'For You Page'(推荐页)向用户展示个性化内容,使新创作者也有机会获得广泛曝光。平台的音乐库丰富,用户可以轻松使用流行歌曲创建视频。TikTok的趋势和挑战机制促进了内容的病毒式传播,经常影响流行文化和音乐排行榜。对于创作者、品牌和普通用户来说,TikTok提供了一个创意表达和发现新内容的平台,其影响力已超越娱乐领域,延伸到营销、教育和社会议题讨论。",
+ category: "娱乐",
+ tags: ["短视频", "社交", "趋势"],
+ rating: 4.7,
+ featured: false,
+ addedDate: "2025-08-12",
+ unchina: true
+ },
+ {
+ id: "iqiyi",
+ url: "https://www.iqiyi.com",
+ title: "爱奇艺",
+ description: "中国领先的视频平台,提供电影、剧集和综艺节目",
+ detailedDescription: "爱奇艺是中国领先的视频平台,提供丰富的电影、电视剧、综艺节目、动漫和纪录片等内容。平台采用免费+会员的商业模式,VIP会员可享受无广告观看、超前点播和高清画质等特权。爱奇艺以自制内容见长,推出了多部热门网剧和综艺节目,如《延禧攻略》、《青春有你》等。平台的技术优势包括高质量的视频编码和智能推荐系统,为用户提供流畅的观看体验和个性化内容。爱奇艺还开发了弹幕、评论等社交功能,增强用户互动。作为中国在线视频市场的主要参与者,爱奇艺不断探索内容创新和商业模式升级,影响着中国数字娱乐产业的发展方向。",
+ category: "娱乐",
+ tags: ["视频", "电影", "电视剧"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "steam",
+ url: "https://store.steampowered.com",
+ title: "Steam",
+ description: "全球最大的PC游戏分发平台,提供丰富的游戏内容和社区功能",
+ detailedDescription: "Steam是由Valve Corporation开发的全球最大PC游戏分发平台,拥有超过5万款游戏和1.2亿活跃用户。平台不仅是游戏商店,还提供社区功能、游戏更新管理、云存档和好友系统等服务。Steam的特色包括定期举办的大型特卖活动、用户评价系统和愿望单功能,帮助玩家发现和购买游戏。平台支持创意工坊,玩家可以创建、分享和下载游戏模组和自定义内容。Steam还提供直播、截图分享和成就系统等社交功能,增强游戏体验。对于PC游戏玩家来说,Steam已成为必备的游戏平台,不仅提供游戏购买和管理,也是连接游戏社区和获取游戏资讯的重要渠道。",
+ category: "娱乐",
+ tags: ["游戏", "数字分发", "社区"],
+ rating: 4.8,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+ {
+ id: "weibo",
+ url: "https://weibo.com",
+ title: "微博",
+ description: "中国领先的社交媒体平台,类似Twitter的信息分享服务",
+ detailedDescription: "微博是中国领先的社交媒体平台,由新浪公司开发运营,被称为'中国的Twitter'。平台允许用户发布短文本、图片、视频和直播内容,关注感兴趣的用户和话题。微博的特色是话题标签系统(#话题#),用户可以参与热门话题讨论,实时了解社会热点。平台汇集了大量名人、媒体和官方机构账号,成为信息传播和公共讨论的重要渠道。微博的超级话题和粉丝群功能强化了社区建设,用户可以围绕共同兴趣形成社群。平台还整合了电商、支付等功能,拓展了社交媒体的应用场景。作为中国互联网生态的重要组成部分,微博不仅是社交和娱乐平台,也是新闻传播、舆论形成和社会互动的重要空间。",
+ category: "娱乐",
+ tags: ["社交媒体", "资讯", "话题"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-12"
+ },
+
+ // 搜索类网站
+ {
+ id: "google",
+ url: "https://www.google.com",
+ title: "Google",
+ description: "全球最大的搜索引擎,提供网页、图片、视频等多种搜索服务",
+ detailedDescription: "Google是全球最受欢迎的搜索引擎,拥有超过90%的市场份额。它提供智能搜索算法,能够理解用户意图并提供最相关的搜索结果。除了基本的网页搜索,Google还提供图片搜索、视频搜索、新闻搜索、学术搜索等多种专业搜索服务。Google搜索具有强大的AI能力,支持语音搜索、图像识别搜索等先进功能。其搜索结果的准确性和相关性在业界处于领先地位,是互联网用户获取信息的主要入口。",
+ category: "搜索",
+ tags: ["搜索", "工具", "必备"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-01",
+ unchina: true
+ },
+ {
+ id: "bing",
+ url: "https://www.bing.com",
+ title: "Bing",
+ description: "微软推出的搜索引擎,提供网页、图片、视频搜索",
+ detailedDescription: "Bing是微软开发的搜索引擎,是Google搜索的主要竞争对手。Bing以其独特的视觉设计和丰富的功能著称,包括每日更新的精美背景图片。在图片搜索方面,Bing表现尤为出色,提供高质量的图片搜索结果和智能图片识别功能。Bing还集成了Microsoft的AI技术,提供智能搜索建议和个性化搜索结果。对于Microsoft生态系统用户来说,Bing与Windows、Office等产品深度集成,提供无缝的搜索体验。",
+ category: "搜索",
+ tags: ["搜索", "微软", "图片"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-01"
+ },
+ {
+ id: "duckduckgo",
+ url: "https://duckduckgo.com",
+ title: "DuckDuckGo",
+ description: "注重隐私保护的搜索引擎,不追踪用户数据",
+ detailedDescription: "DuckDuckGo是一个以隐私保护为核心的搜索引擎,承诺不收集、不存储、不分享用户的个人信息。它不会追踪用户的搜索历史,不会创建用户档案,也不会根据个人数据提供个性化搜索结果。DuckDuckGo通过聚合多个搜索引擎的结果来提供搜索服务,同时保持用户隐私。它支持即时答案、图片搜索、视频搜索等功能,界面简洁易用。对于重视隐私的用户来说,DuckDuckGo是一个理想的选择,它证明了搜索引擎可以在保护用户隐私的同时提供高质量的搜索服务。",
+ category: "搜索",
+ tags: ["搜索", "隐私", "安全"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-01"
+ },
+ {
+ id: "yandex",
+ url: "https://yandex.com",
+ title: "Yandex",
+ description: "俄罗斯最大的搜索引擎,提供多语言搜索服务",
+ detailedDescription: "Yandex是俄罗斯最大的搜索引擎,在俄语市场占据主导地位。它提供强大的俄语搜索能力,对俄语语法和语义有深入理解。Yandex不仅是一个搜索引擎,还是一个综合性的互联网服务平台,提供地图、翻译、新闻、邮箱等多种服务。在机器学习和技术创新方面,Yandex投入巨大,开发了先进的搜索算法和AI技术。对于需要俄语内容搜索或了解俄罗斯互联网生态的用户来说,Yandex是不可或缺的工具。",
+ category: "搜索",
+ tags: ["搜索", "俄语", "国际"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-01"
+ },
+ {
+ id: "searx",
+ url: "https://searx.org",
+ title: "SearX",
+ description: "开源的元搜索引擎,聚合多个搜索引擎结果",
+ detailedDescription: "SearX是一个开源的元搜索引擎,它聚合了来自Google、Bing、DuckDuckGo等多个搜索引擎的结果,为用户提供更全面的搜索结果。SearX注重隐私保护,不会追踪用户行为,也不会存储个人数据。用户可以自定义搜索源,选择特定的搜索引擎进行查询。SearX支持多种搜索类型,包括网页、图片、新闻、文件等。由于其开源特性,用户可以自己部署SearX实例,完全控制搜索环境和隐私保护级别。对于技术爱好者和隐私意识强的用户来说,SearX是一个理想的选择。",
+ category: "搜索",
+ tags: ["搜索", "开源", "隐私"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-01"
+ },
+
+ // 娱乐类网站
+ {
+ id: "bilibili",
+ url: "https://www.bilibili.com",
+ title: "哔哩哔哩",
+ description: "中国年轻人喜爱的视频弹幕网站,提供动画、游戏、音乐等内容",
+ detailedDescription: "哔哩哔哩(B站)是中国领先的视频平台,以弹幕文化著称。它最初以动漫内容起家,现已发展成为涵盖游戏、音乐、科技、生活、教育等多元内容的综合性视频平台。B站的弹幕系统让观众可以实时互动,创造了独特的观看体验。平台拥有大量优质UP主,生产高质量的内容,涵盖从娱乐到教育的各个领域。B站还提供直播、会员服务、电商等多种功能,形成了完整的生态系统。对于中国年轻一代来说,B站不仅是娱乐平台,更是文化社区和学习平台。",
+ category: "娱乐",
+ tags: ["视频", "弹幕", "二次元"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-02"
+ },
+ {
+ id: "youtube",
+ url: "https://www.youtube.com",
+ title: "YouTube",
+ description: "全球最大的视频分享平台,用户可以上传、观看和分享视频",
+ detailedDescription: "YouTube是全球最大的视频分享平台,拥有超过20亿月活跃用户。它允许任何人上传、观看和分享视频内容,从个人vlog到专业制作的内容应有尽有。YouTube的推荐算法非常强大,能够根据用户兴趣提供个性化内容推荐。平台支持多种视频格式,包括4K、HDR等高质量内容。YouTube还提供YouTube Premium订阅服务,去除广告并提供离线观看功能。对于内容创作者来说,YouTube提供了完善的创作工具和盈利机制,是全球最重要的内容创作平台之一。",
+ category: "娱乐",
+ tags: ["视频", "分享", "全球"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-02",
+ unchina: true
+ },
+ {
+ id: "netflix",
+ url: "https://www.netflix.com",
+ title: "Netflix",
+ description: "全球领先的流媒体服务平台,提供电影、电视剧等内容",
+ detailedDescription: "Netflix是全球领先的流媒体服务平台,彻底改变了人们观看电影和电视剧的方式。它提供大量原创内容和授权内容,涵盖各种类型和语言。Netflix的推荐系统基于用户观看历史和偏好,提供个性化的内容推荐。平台支持多种设备,包括智能电视、手机、平板等,并提供离线下载功能。Netflix的原创内容制作精良,包括《纸牌屋》、《怪奇物语》等热门剧集,在影视行业产生了重大影响。对于追求高质量娱乐内容的用户来说,Netflix是不可或缺的选择。",
+ category: "娱乐",
+ tags: ["流媒体", "电影", "电视剧"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-02",
+ unchina: true
+ },
+ {
+ id: "spotify",
+ url: "https://www.spotify.com",
+ title: "Spotify",
+ description: "全球最大的音乐流媒体平台,提供海量音乐资源",
+ detailedDescription: "Spotify是全球最大的音乐流媒体平台,拥有超过8000万首歌曲和播客内容。它提供免费和付费两种服务模式,付费用户享受无广告、离线下载、高音质等特权。Spotify的推荐算法非常精准,能够根据用户听歌习惯推荐相似音乐。平台支持创建和分享播放列表,用户可以发现新的音乐和艺术家。Spotify还提供播客服务,涵盖各种主题和内容。对于音乐爱好者来说,Spotify提供了便捷的音乐发现和聆听体验,是数字音乐时代的代表性平台。",
+ category: "娱乐",
+ tags: ["音乐", "流媒体", "播放列表"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-02",
+ unchina: true
+ },
+ {
+ id: "twitch",
+ url: "https://www.twitch.tv",
+ title: "Twitch",
+ description: "全球最大的游戏直播平台,观看和直播游戏内容",
+ detailedDescription: "Twitch是全球最大的游戏直播平台,专注于游戏内容的直播和观看。它允许游戏玩家实时直播他们的游戏过程,观众可以观看、聊天和互动。Twitch不仅限于游戏,还涵盖创意内容、音乐、聊天等多元化内容。平台提供强大的互动功能,包括实时聊天、表情符号、订阅、捐赠等。Twitch的合作伙伴计划为优秀主播提供收入分成和额外功能。对于游戏玩家和内容创作者来说,Twitch是展示技能、建立社区、获得收入的重要平台。它已经成为游戏文化的重要组成部分,推动了电子竞技和游戏直播行业的发展。",
+ category: "娱乐",
+ tags: ["直播", "游戏", "互动"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-02",
+ unchina: true
+ },
+ {
+ id: "tiktok",
+ url: "https://www.tiktok.com",
+ title: "TikTok",
+ description: "短视频社交平台,用户可以创作和分享短视频内容",
+ detailedDescription: "TikTok是全球最受欢迎的短视频社交平台,专注于15秒到3分钟的短视频内容。它提供丰富的音乐库、滤镜、特效和编辑工具,让用户可以轻松创作有趣的短视频。TikTok的推荐算法非常精准,能够根据用户兴趣推送相关内容。平台涵盖各种内容类型,包括娱乐、教育、美食、旅行、舞蹈等。TikTok的挑战和趋势功能让内容更容易传播,用户可以参与热门话题和挑战。对于内容创作者来说,TikTok提供了快速获得关注和粉丝的机会。它已经成为年轻一代表达创意、分享生活、发现新内容的重要平台。",
+ category: "娱乐",
+ tags: ["短视频", "社交", "创作"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-02",
+ unchina: true
+ },
+ {
+ id: "douyin",
+ url: "https://www.douyin.com",
+ title: "抖音",
+ description: "中国最受欢迎的短视频平台,提供丰富的短视频内容",
+ category: "娱乐",
+ tags: ["短视频", "中国", "娱乐"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-02"
+ },
+ {
+ id: "iqiyi",
+ url: "https://www.iqiyi.com",
+ title: "爱奇艺",
+ description: "中国领先的在线视频平台,提供电影、电视剧、综艺等内容",
+
+ category: "娱乐",
+ tags: ["视频", "中国", "影视"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-02"
+ },
+ {
+ id: "youku",
+ url: "https://www.youku.com",
+ title: "优酷",
+ description: "中国知名视频网站,提供电影、电视剧、动漫等视频内容",
+
+ category: "娱乐",
+ tags: ["视频", "中国", "综合"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-02"
+ },
+ {
+ id: "steam",
+ url: "https://store.steampowered.com",
+ title: "Steam",
+ description: "全球最大的PC游戏数字发行平台",
+ detailedDescription: "Steam是Valve公司开发的全球最大PC游戏数字发行平台,拥有超过3万款游戏。它提供游戏购买、下载、更新、社交等一站式服务。Steam的社区功能强大,用户可以评价游戏、分享截图、参与讨论。平台经常举办大型促销活动,如夏季特卖、冬季特卖等,提供大幅折扣。Steam还支持云存档、成就系统、好友系统等功能,为玩家提供完整的游戏体验。对于PC游戏玩家来说,Steam是购买和管理游戏的主要平台,几乎涵盖了所有主流PC游戏。",
+ category: "娱乐",
+ tags: ["游戏", "PC", "数字发行"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-02"
+ },
+
+ // AI类网站
+ {
+ id: "chatgpt",
+ url: "https://chat.openai.com",
+ title: "ChatGPT",
+ description: "OpenAI开发的AI聊天机器人,能够进行自然语言对话",
+ detailedDescription: "ChatGPT是由OpenAI开发的大型语言模型,基于GPT(生成式预训练变换器)技术。它能够进行自然语言对话,回答各种问题,协助写作、编程、分析等任务。ChatGPT具有强大的语言理解能力,能够处理复杂的对话上下文,提供连贯和有意义的回答。它支持多种语言,可以用于学习、工作、创作等多种场景。ChatGPT的免费版本已经足够强大,而付费的Plus版本提供更快的响应速度和更高级的功能。作为AI领域的代表性产品,ChatGPT推动了AI技术的普及和应用。",
+ category: "AI",
+ tags: ["AI", "聊天", "OpenAI"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "claude",
+ url: "https://claude.ai",
+ title: "Claude",
+ description: "Anthropic开发的AI助手,擅长对话和文本处理",
+ detailedDescription: "Claude是由Anthropic开发的AI助手,基于Constitutional AI技术,注重安全性和有用性。Claude在文本处理、分析、写作等方面表现出色,能够处理长文档和复杂任务。它特别擅长学术写作、代码分析、文档总结等专业任务。Claude的设计理念强调AI的安全性,通过宪法训练确保AI行为符合人类价值观。它提供免费和付费版本,付费版本支持更长的对话和更高级的功能。对于需要高质量文本处理和分析的用户来说,Claude是一个可靠的选择。",
+ category: "AI",
+ tags: ["AI", "助手", "Anthropic"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "midjourney",
+ url: "https://www.midjourney.com",
+ title: "Midjourney",
+ description: "AI图像生成工具,通过文本描述生成艺术作品",
+ detailedDescription: "Midjourney是一个基于AI的图像生成工具,通过文本描述创建高质量的艺术作品。它使用先进的扩散模型技术,能够生成具有艺术感的图像,特别擅长艺术风格、概念设计和创意表达。Midjourney通过Discord机器人提供服务,用户可以在Discord频道中与机器人交互来生成图像。它支持多种艺术风格,从写实到抽象,从古典到现代。Midjourney在艺术创作、设计、概念可视化等领域广受欢迎,为创作者提供了强大的视觉表达工具。",
+ category: "AI",
+ tags: ["AI", "图像生成", "艺术"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "stable-diffusion",
+ url: "https://stability.ai",
+ title: "Stable Diffusion",
+ description: "开源的AI图像生成模型,可以根据文本生成图像",
+ detailedDescription: "Stable Diffusion是一个开源的AI图像生成模型,由Stability AI开发。它使用扩散模型技术,能够根据文本描述生成高质量的图像。Stable Diffusion的独特之处在于其开源特性,任何人都可以下载、修改和部署自己的版本。它支持多种图像生成模式,包括文本到图像、图像到图像、图像修复等。Stable Diffusion在艺术创作、设计、概念可视化等领域广受欢迎。由于其开源性质,社区开发了大量插件和工具,扩展了其功能。对于开发者、艺术家和研究人员来说,Stable Diffusion提供了探索AI图像生成技术的理想平台。",
+ category: "AI",
+ tags: ["AI", "图像生成", "开源"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "copilot",
+ url: "https://copilot.microsoft.com",
+ title: "Microsoft Copilot",
+ description: "微软的AI助手,集成在多个微软产品中",
+
+ category: "AI",
+ tags: ["AI", "微软", "助手"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-03"
+ },
+ {
+ id: "bard",
+ url: "https://bard.google.com",
+ title: "Google Bard",
+ description: "Google开发的AI聊天机器人,基于LaMDA技术",
+
+ category: "AI",
+ tags: ["AI", "Google", "聊天"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "huggingface",
+ url: "https://huggingface.co",
+ title: "Hugging Face",
+ description: "AI模型和数据集的开源社区平台",
+
+ category: "AI",
+ tags: ["AI", "开源", "模型"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "runway",
+ url: "https://runwayml.com",
+ title: "Runway",
+ description: "AI视频和图像编辑工具,提供多种创意AI功能",
+
+ category: "AI",
+ tags: ["AI", "视频编辑", "创意"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "character-ai",
+ url: "https://character.ai",
+ title: "Character.AI",
+ description: "与AI角色对话的平台,可以创建和聊天各种AI角色",
+
+ category: "AI",
+ tags: ["AI", "角色", "对话"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+ {
+ id: "perplexity",
+ url: "https://www.perplexity.ai",
+ title: "Perplexity",
+ description: "AI搜索引擎,提供准确的答案和引用来源",
+
+ category: "AI",
+ tags: ["AI", "搜索", "问答"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-03",
+ unchina: true
+ },
+
+ // 评价类网站
+ {
+ id: "douban",
+ url: "https://www.douban.com",
+ title: "豆瓣",
+ description: "中国最大的文化社区网站,提供图书、电影、音乐等评论和推荐",
+ detailedDescription: "豆瓣是中国最大的文化社区网站,专注于图书、电影、音乐等文化内容的评价和推荐。它采用用户评分和评论系统,为每个作品提供详细的用户反馈。豆瓣的推荐算法基于用户兴趣和评分历史,能够提供个性化的内容推荐。平台还提供丰富的文化资讯、书单、影单等内容,帮助用户发现新的文化作品。豆瓣的社区功能强大,用户可以加入各种兴趣小组,参与文化讨论。对于中国文化爱好者来说,豆瓣是发现和评价文化作品的重要平台,拥有高质量的用户群体和内容。",
+ category: "评价",
+ tags: ["评论", "文化", "社区"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-03"
+ },
+ {
+ id: "imdb",
+ url: "https://www.imdb.com",
+ title: "IMDb",
+ description: "全球最大的电影数据库,提供电影评分和详细信息",
+ detailedDescription: "IMDb(Internet Movie Database)是全球最大的电影数据库,拥有超过800万部电影、电视剧和视频游戏的详细信息。它提供演员、导演、编剧等电影从业者的完整资料,以及电影的制作信息、票房数据、获奖记录等。IMDb的用户评分系统非常权威,被广泛用作电影质量的参考标准。平台还提供电影新闻、预告片、用户评论等内容。IMDb的搜索和筛选功能强大,用户可以按类型、年份、评分等条件查找电影。对于电影爱好者来说,IMDb是了解电影信息、发现新作品、参与电影讨论的重要平台。",
+ category: "评价",
+ tags: ["电影", "评分", "数据库"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-04",
+ unchina: true
+ },
+ {
+ id: "rottentomatoes",
+ url: "https://www.rottentomatoes.com",
+ title: "Rotten Tomatoes",
+ description: "知名电影评论聚合网站,提供专业和观众评分",
+
+ category: "评价",
+ tags: ["电影", "评论", "评分"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-04",
+ unchina: true
+ },
+ {
+ id: "metacritic",
+ url: "https://www.metacritic.com",
+ title: "Metacritic",
+ description: "综合评分网站,涵盖电影、游戏、音乐等多个领域",
+
+ category: "评价",
+ tags: ["评分", "综合", "媒体"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-04",
+ unchina: true
+ },
+ {
+ id: "goodreads",
+ url: "https://www.goodreads.com",
+ title: "Goodreads",
+ description: "全球最大的图书社区,用户可以评价和推荐图书",
+
+ category: "评价",
+ tags: ["图书", "阅读", "社区"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-04",
+ unchina: true
+ },
+ {
+ id: "yelp",
+ url: "https://www.yelp.com",
+ title: "Yelp",
+ description: "本地商家评价平台,用户可以查看和发布商家评论",
+
+ category: "评价",
+ tags: ["商家", "本地", "评论"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-04",
+ unchina: true
+ },
+ {
+ id: "dianping",
+ url: "https://www.dianping.com",
+ title: "大众点评",
+ description: "中国领先的本地生活信息及交易平台",
+
+ category: "评价",
+ tags: ["本地", "生活", "评价"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-04"
+ },
+ {
+ id: "tripadvisor",
+ url: "https://www.tripadvisor.com",
+ title: "TripAdvisor",
+ description: "全球最大的旅游评价网站,提供酒店、景点等评价",
+
+ category: "评价",
+ tags: ["旅游", "酒店", "评价"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-04"
+ },
+ {
+ id: "glassdoor",
+ url: "https://www.glassdoor.com",
+ title: "Glassdoor",
+ description: "职场评价网站,员工可以匿名评价公司和薪资",
+
+ category: "评价",
+ tags: ["职场", "公司", "薪资"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-04"
+ },
+ {
+ id: "zhipin",
+ url: "https://www.zhipin.com",
+ title: "BOSS直聘",
+ description: "中国知名招聘网站,提供职位信息和公司评价",
+
+ category: "评价",
+ tags: ["招聘", "职场", "中国"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-04"
+ },
+
+ // 工具类网站
+ {
+ id: "notion",
+ url: "https://www.notion.so",
+ title: "Notion",
+ description: "全能的工作空间,集笔记、数据库、项目管理于一体",
+ detailedDescription: "Notion是一个全能的工作空间平台,将笔记、数据库、项目管理、知识库等功能整合在一个应用中。它采用块级编辑系统,用户可以自由组合各种内容块,创建个性化的页面和数据库。Notion支持团队协作,多人可以同时编辑和评论。它提供丰富的模板库,涵盖个人管理、团队协作、项目管理等各种场景。Notion的数据库功能强大,支持多种视图和关联关系,可以构建复杂的信息管理系统。对于个人用户和团队来说,Notion是一个灵活且强大的生产力工具。",
+ category: "工具",
+ tags: ["笔记", "协作", "生产力"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "figma",
+ url: "https://www.figma.com",
+ title: "Figma",
+ description: "基于浏览器的协作式界面设计工具",
+ detailedDescription: "Figma是一个基于浏览器的协作式设计工具,专门用于UI/UX设计。它支持实时协作,多个设计师可以同时编辑同一个设计文件,大大提高了团队协作效率。Figma提供强大的设计系统功能,支持组件库、样式管理和设计规范。它的原型功能允许设计师创建交互式原型,进行用户测试和演示。Figma还提供丰富的插件生态系统,扩展了工具的功能。对于设计团队来说,Figma已经成为行业标准,特别是在远程协作和设计系统管理方面表现出色。",
+ category: "工具",
+ tags: ["设计", "协作", "UI/UX"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "canva",
+ url: "https://www.canva.com",
+ title: "Canva",
+ description: "在线图形设计平台,提供丰富的设计模板",
+ detailedDescription: "Canva是一个在线图形设计平台,让任何人都能轻松创建专业级的设计作品。它提供数万个精美的模板,涵盖社交媒体、海报、名片、演示文稿等各种设计需求。Canva的拖拽式编辑器非常直观,即使没有设计经验的用户也能快速上手。它支持团队协作,多人可以共同编辑设计项目。Canva还提供丰富的素材库,包括图片、图标、字体等。对于小型企业、营销人员和内容创作者来说,Canva是一个经济实惠且功能强大的设计工具。",
+ category: "工具",
+ tags: ["设计", "模板", "图形"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "photopea",
+ url: "https://www.photopea.com",
+ title: "Photopea",
+ description: "免费的在线图像编辑器,支持PSD等多种格式",
+ detailedDescription: "Photopea是一个功能强大的免费在线图像编辑器,界面和功能与Adobe Photoshop非常相似。它支持多种文件格式,包括PSD、AI、XD、Sketch等专业设计文件格式,让用户可以在浏览器中直接编辑这些文件。Photopea提供图层、蒙版、滤镜、调整等专业图像编辑功能,支持智能对象、矢量图形等高级特性。它完全免费使用,无需注册,所有功能都可以直接使用。对于需要专业图像编辑功能但不想购买昂贵软件的用户来说,Photopea是一个理想的选择。",
+ category: "工具",
+ tags: ["图像编辑", "免费", "在线"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "remove-bg",
+ url: "https://www.remove.bg",
+ title: "Remove.bg",
+ description: "AI驱动的背景移除工具,一键去除图片背景",
+ detailedDescription: "Remove.bg是一个基于AI技术的在线背景移除工具,能够自动识别前景对象并移除背景。它使用先进的机器学习算法,可以处理各种复杂的图像,包括人物、产品、动物等。Remove.bg的操作非常简单,用户只需上传图片,AI就会自动处理并生成透明背景的图片。它支持多种输出格式,包括PNG、JPG等。免费版本有一些限制,付费版本提供更高分辨率和批量处理功能。对于电商、设计师、摄影师等需要处理产品图片的用户来说,Remove.bg大大提高了工作效率。",
+ category: "工具",
+ tags: ["图像处理", "AI", "背景移除"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "tinypng",
+ url: "https://tinypng.com",
+ title: "TinyPNG",
+ description: "在线图片压缩工具,减小文件大小同时保持质量",
+ detailedDescription: "TinyPNG是一个专业的在线图片压缩工具,专门用于压缩PNG和JPEG格式的图片。它使用智能的压缩算法,能够显著减小图片文件大小,同时保持视觉质量。TinyPNG支持批量上传,一次可以处理多张图片,大大提高了工作效率。它提供拖拽上传功能,界面简洁易用。TinyPNG的压缩是无损的,不会影响图片的透明度和其他重要特性。对于网站开发者、设计师和内容创作者来说,TinyPNG是优化网站性能、减少加载时间的重要工具。它特别适合需要大量图片处理的场景。",
+ category: "工具",
+ tags: ["图片压缩", "优化", "在线"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "convertio",
+ url: "https://convertio.co",
+ title: "Convertio",
+ description: "在线文件转换工具,支持300多种文件格式",
+
+ category: "工具",
+ tags: ["文件转换", "格式", "在线"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "wetransfer",
+ url: "https://wetransfer.com",
+ title: "WeTransfer",
+ description: "简单易用的大文件传输服务",
+
+ category: "工具",
+ tags: ["文件传输", "分享", "大文件"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "speedtest",
+ url: "https://www.speedtest.net",
+ title: "Speedtest",
+ description: "测试网络速度的权威工具",
+
+ category: "工具",
+ tags: ["网速测试", "网络", "诊断"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "downdetector",
+ url: "https://downdetector.com",
+ title: "Downdetector",
+ description: "实时监控网站和服务的运行状态",
+
+ category: "工具",
+ tags: ["监控", "状态", "服务"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "qrcode-generator",
+ url: "https://www.qr-code-generator.com",
+ title: "QR Code Generator",
+ description: "免费的二维码生成工具",
+
+ category: "工具",
+ tags: ["二维码", "生成", "免费"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "temp-mail",
+ url: "https://temp-mail.org",
+ title: "Temp Mail",
+ description: "临时邮箱服务,保护隐私的一次性邮箱",
+
+ category: "工具",
+ tags: ["临时邮箱", "隐私", "一次性"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "have-i-been-pwned",
+ url: "https://haveibeenpwned.com",
+ title: "Have I Been Pwned",
+ description: "检查邮箱是否在数据泄露事件中被泄露",
+
+ category: "工具",
+ tags: ["安全", "数据泄露", "检查"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "password-generator",
+ url: "https://passwordsgenerator.net",
+ title: "Password Generator",
+ description: "生成强密码的在线工具",
+
+ category: "工具",
+ tags: ["密码", "生成", "安全"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "regex101",
+ url: "https://regex101.com",
+ title: "Regex101",
+ description: "在线正则表达式测试和调试工具",
+
+ category: "工具",
+ tags: ["正则表达式", "测试", "调试"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "json-formatter",
+ url: "https://jsonformatter.curiousconcept.com",
+ title: "JSON Formatter",
+ description: "在线JSON格式化和验证工具",
+
+ category: "工具",
+ tags: ["JSON", "格式化", "验证"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "codebeautify",
+ url: "https://codebeautify.org",
+ title: "Code Beautify",
+ description: "代码格式化和各种在线工具集合",
+
+ category: "工具",
+ tags: ["代码格式化", "工具集", "开发"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "carbon",
+ url: "https://carbon.now.sh",
+ title: "Carbon",
+ description: "创建美观的代码截图分享工具",
+ detailedDescription: "Carbon是一个专门用于创建美观代码截图的在线工具,让开发者可以轻松制作专业的代码分享图片。它支持多种编程语言的语法高亮,提供丰富的主题和自定义选项。用户可以调整背景、字体、边距、阴影等视觉效果,创建个性化的代码截图。Carbon支持多种导出格式,包括PNG、SVG等,也可以直接分享链接。它提供拖拽上传功能,支持从文件或URL导入代码。对于开发者、技术博主和文档编写者来说,Carbon是创建高质量代码展示图片的理想工具,特别适合社交媒体分享和技术文档。",
+ category: "工具",
+ tags: ["代码截图", "分享", "美化"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "excalidraw",
+ url: "https://excalidraw.com",
+ title: "Excalidraw",
+ description: "开源的手绘风格图表绘制工具",
+ detailedDescription: "Excalidraw是一个开源的在线绘图工具,采用手绘风格的设计,让图表看起来更加自然和友好。它支持多种图表类型,包括流程图、思维导图、线框图、架构图等。Excalidraw提供实时协作功能,多人可以同时编辑同一个图表。它支持导出为PNG、SVG等格式,也可以嵌入到其他应用中。Excalidraw的界面简洁直观,提供丰富的绘图工具和模板。由于其开源特性,用户可以自己部署或修改。对于产品经理、设计师、开发者和教育工作者来说,Excalidraw是创建和分享图表的理想工具。",
+ category: "工具",
+ tags: ["绘图", "图表", "手绘风格"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+ {
+ id: "draw-io",
+ url: "https://app.diagrams.net",
+ title: "Draw.io",
+ description: "免费的在线图表绘制工具",
+
+ category: "工具",
+ tags: ["图表", "流程图", "免费"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-05"
+ },
+
+ // 开发类网站
+ {
+ id: "github",
+ url: "https://github.com",
+ title: "GitHub",
+ description: "全球最大的代码托管平台,为开发者提供Git仓库托管服务",
+ detailedDescription: "GitHub是全球最大的代码托管平台,拥有超过1亿个代码仓库。它基于Git版本控制系统,为开发者提供代码托管、协作开发、项目管理等服务。GitHub支持公开和私有仓库,免费用户可以使用无限量的公开仓库。平台提供强大的协作功能,包括Pull Request、Issue跟踪、代码审查等。GitHub还集成了CI/CD、代码扫描、依赖管理等开发工具。对于开源项目,GitHub提供了完整的项目管理解决方案,包括Wiki、Projects、Discussions等。GitHub已经成为开发者社区的中心,是学习和参与开源项目的重要平台。",
+ category: "开发",
+ tags: ["代码", "开源", "Git"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-01"
+ },
+ {
+ id: "gitlab",
+ url: "https://gitlab.com",
+ title: "GitLab",
+ description: "DevOps平台,提供Git仓库管理和CI/CD功能",
+
+ category: "开发",
+ tags: ["Git", "DevOps", "CI/CD"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "bitbucket",
+ url: "https://bitbucket.org",
+ title: "Bitbucket",
+ description: "Atlassian的Git代码管理解决方案",
+
+ category: "开发",
+ tags: ["Git", "Atlassian", "代码管理"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "stackoverflow",
+ url: "https://stackoverflow.com",
+ title: "Stack Overflow",
+ description: "全球最大的程序员问答社区",
+ detailedDescription: "Stack Overflow是全球最大的程序员问答社区,拥有超过2000万注册用户和数千万个问题。它采用问答模式,开发者可以提出问题并获得来自全球社区的解答。Stack Overflow的声誉系统鼓励高质量的回答,用户可以通过投票来评价问题和答案的质量。平台涵盖几乎所有编程语言和技术栈,从基础的编程概念到高级的技术问题都有覆盖。Stack Overflow还提供职业发展平台,包括工作机会、技能评估等。对于程序员来说,Stack Overflow是解决技术问题、学习新知识、展示专业能力的重要平台。",
+ category: "开发",
+ tags: ["问答", "编程", "社区"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "codepen",
+ url: "https://codepen.io",
+ title: "CodePen",
+ description: "前端代码在线编辑和分享平台",
+ detailedDescription: "CodePen是一个专门为前端开发者设计的在线代码编辑和分享平台。它支持HTML、CSS、JavaScript的实时编辑和预览,让开发者可以快速创建和测试前端代码片段。CodePen的界面简洁直观,支持多种预处理器和框架。用户可以创建公开或私有的代码片段,分享给其他开发者。平台还提供丰富的代码示例和模板,是学习前端技术的优秀资源。CodePen的社区功能强大,用户可以关注其他开发者、收藏喜欢的作品、参与讨论。对于前端开发者来说,CodePen是展示技能、学习新技术、快速原型开发的重要工具。",
+ category: "开发",
+ tags: ["前端", "在线编辑", "分享"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "jsfiddle",
+ url: "https://jsfiddle.net",
+ title: "JSFiddle",
+ description: "在线JavaScript、HTML、CSS代码测试工具",
+
+ category: "开发",
+ tags: ["JavaScript", "在线测试", "前端"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "codesandbox",
+ url: "https://codesandbox.io",
+ title: "CodeSandbox",
+ description: "在线代码编辑器,支持多种框架和库",
+
+ category: "开发",
+ tags: ["在线编辑", "框架", "协作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "replit",
+ url: "https://replit.com",
+ title: "Replit",
+ description: "在线编程环境,支持多种编程语言",
+
+ category: "开发",
+ tags: ["在线编程", "多语言", "协作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "glitch",
+ url: "https://glitch.com",
+ title: "Glitch",
+ description: "创建和部署Web应用的友好平台",
+
+ category: "开发",
+ tags: ["Web应用", "部署", "协作"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "netlify",
+ url: "https://www.netlify.com",
+ title: "Netlify",
+ description: "现代Web项目的构建、部署和托管平台",
+
+ category: "开发",
+ tags: ["部署", "托管", "JAMstack"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "vercel",
+ url: "https://vercel.com",
+ title: "Vercel",
+ description: "前端框架和静态站点的部署平台",
+ detailedDescription: "Vercel是一个专门为前端开发者设计的部署平台,特别适合React、Vue、Next.js等现代前端框架。它提供零配置的部署体验,只需连接Git仓库即可自动部署。Vercel的全球CDN网络确保网站的高速访问,支持自动HTTPS和自定义域名。它提供预览部署功能,每次提交都会生成一个预览链接,方便团队协作和测试。Vercel还支持无服务器函数,可以部署API端点。对于前端开发者和团队来说,Vercel简化了部署流程,提供了优秀的性能和开发体验,是现代前端开发的首选部署平台。",
+ category: "开发",
+ tags: ["部署", "前端", "静态站点"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "heroku",
+ url: "https://www.heroku.com",
+ title: "Heroku",
+ description: "云应用平台,支持多种编程语言",
+
+ category: "开发",
+ tags: ["云平台", "部署", "多语言"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "aws",
+ url: "https://aws.amazon.com",
+ title: "Amazon Web Services",
+ description: "亚马逊的云计算服务平台",
+ detailedDescription: "Amazon Web Services(AWS)是全球领先的云计算服务平台,提供200多种云服务,涵盖计算、存储、数据库、网络、安全、人工智能等领域。AWS的弹性计算云(EC2)允许用户租用虚拟服务器,简单存储服务(S3)提供可扩展的对象存储,Lambda支持无服务器计算。AWS在全球拥有多个数据中心,提供高可用性和低延迟的服务。它采用按需付费模式,用户只需为实际使用的资源付费。AWS还提供丰富的开发工具和SDK,支持多种编程语言。对于企业和开发者来说,AWS是构建和部署云应用的首选平台。",
+ category: "开发",
+ tags: ["云计算", "AWS", "基础设施"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "azure",
+ url: "https://azure.microsoft.com",
+ title: "Microsoft Azure",
+ description: "微软的云计算平台和服务",
+
+ category: "开发",
+ tags: ["云计算", "微软", "企业"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "gcp",
+ url: "https://cloud.google.com",
+ title: "Google Cloud Platform",
+ description: "Google的云计算服务平台",
+
+ category: "开发",
+ tags: ["云计算", "Google", "机器学习"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "docker",
+ url: "https://www.docker.com",
+ title: "Docker",
+ description: "容器化平台,简化应用部署和管理",
+ detailedDescription: "Docker是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包到轻量级的容器中。Docker容器可以在任何支持Docker的环境中运行,确保应用在不同环境中的一致性。Docker使用镜像来创建容器,镜像包含了运行应用所需的所有文件和配置。它提供Docker Hub镜像仓库,用户可以分享和下载预构建的镜像。Docker Compose允许用户定义和运行多容器应用。对于开发者和运维团队来说,Docker简化了应用的开发、测试和部署流程,是现代软件开发和DevOps实践的重要工具。",
+ category: "开发",
+ tags: ["容器", "部署", "DevOps"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "kubernetes",
+ url: "https://kubernetes.io",
+ title: "Kubernetes",
+ description: "开源的容器编排平台",
+
+ category: "开发",
+ tags: ["容器编排", "开源", "云原生"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "npm",
+ url: "https://www.npmjs.com",
+ title: "npm",
+ description: "Node.js包管理器和JavaScript包仓库",
+
+ category: "开发",
+ tags: ["包管理", "JavaScript", "Node.js"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "pypi",
+ url: "https://pypi.org",
+ title: "PyPI",
+ description: "Python包索引,Python软件包的官方仓库",
+
+ category: "开发",
+ tags: ["Python", "包管理", "仓库"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "maven",
+ url: "https://mvnrepository.com",
+ title: "Maven Repository",
+ description: "Java项目的依赖管理和构建工具仓库",
+
+ category: "开发",
+ tags: ["Java", "Maven", "依赖管理"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "mdn",
+ url: "https://developer.mozilla.org",
+ title: "MDN Web Docs",
+ description: "Mozilla开发者网络,Web技术文档和学习资源",
+ detailedDescription: "MDN Web Docs(Mozilla Developer Network)是Web开发领域最权威的文档和学习资源平台。它提供HTML、CSS、JavaScript等Web技术的详细文档,包括API参考、教程、示例代码等。MDN的内容由Mozilla维护,质量高且更新及时,被全球开发者广泛使用。平台还提供Web开发的学习路径、最佳实践、兼容性信息等实用内容。MDN支持多语言,包括中文版本,方便不同地区的开发者使用。对于Web开发者来说,MDN是学习和查阅Web技术文档的首选平台,被誉为Web开发的圣经。",
+ category: "开发",
+ tags: ["文档", "Web技术", "学习"],
+ rating: 5,
+ featured: true,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "w3schools",
+ url: "https://www.w3schools.com",
+ title: "W3Schools",
+ description: "Web开发教程和参考资料网站",
+
+ category: "开发",
+ tags: ["教程", "Web开发", "学习"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "devdocs",
+ url: "https://devdocs.io",
+ title: "DevDocs",
+ description: "多种编程语言和框架的API文档集合",
+
+ category: "开发",
+ tags: ["API文档", "多语言", "参考"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "postman",
+ url: "https://www.postman.com",
+ title: "Postman",
+ description: "API开发和测试平台",
+ detailedDescription: "Postman是一个功能强大的API开发和测试平台,被全球数百万开发者使用。它提供直观的界面来创建、测试和文档化API。Postman支持各种HTTP方法,包括GET、POST、PUT、DELETE等,并提供丰富的请求参数设置选项。它支持环境变量和集合,让用户可以管理不同环境的API配置。Postman的自动化测试功能允许用户创建测试脚本,验证API响应的正确性。它还提供团队协作功能,支持API集合的共享和版本控制。对于前端开发者、后端开发者和测试工程师来说,Postman是API开发和测试的必备工具。",
+ category: "开发",
+ tags: ["API", "测试", "开发工具"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "insomnia",
+ url: "https://insomnia.rest",
+ title: "Insomnia",
+ description: "现代化的REST客户端和API设计工具",
+
+ category: "开发",
+ tags: ["REST", "API", "客户端"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "swagger",
+ url: "https://swagger.io",
+ title: "Swagger",
+ description: "API设计、构建、文档化和测试工具",
+
+ category: "开发",
+ tags: ["API", "文档", "设计"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "jenkins",
+ url: "https://www.jenkins.io",
+ title: "Jenkins",
+ description: "开源的持续集成和持续部署工具",
+
+ category: "开发",
+ tags: ["CI/CD", "自动化", "开源"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "travis-ci",
+ url: "https://travis-ci.org",
+ title: "Travis CI",
+ description: "持续集成服务,与GitHub深度集成",
+
+ category: "开发",
+ tags: ["CI/CD", "GitHub", "自动化"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "circleci",
+ url: "https://circleci.com",
+ title: "CircleCI",
+ description: "现代化的持续集成和部署平台",
+
+ category: "开发",
+ tags: ["CI/CD", "现代化", "部署"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "sentry",
+ url: "https://sentry.io",
+ title: "Sentry",
+ description: "应用性能监控和错误跟踪平台",
+
+ category: "开发",
+ tags: ["监控", "错误跟踪", "性能"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "datadog",
+ url: "https://www.datadoghq.com",
+ title: "Datadog",
+ description: "云规模的监控和安全平台",
+
+ category: "开发",
+ tags: ["监控", "安全", "云服务"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+ {
+ id: "newrelic",
+ url: "https://newrelic.com",
+ title: "New Relic",
+ description: "应用性能监控和可观测性平台",
+
+ category: "开发",
+ tags: ["APM", "监控", "可观测性"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-06"
+ },
+
+ // 一网一匠网站
+ {
+ id: "ywyj",
+ url: "https://ywyj.cn/",
+ title: "一网一匠",
+ description: "精选优质网站导航,发现互联网优质内容",
+ detailedDescription: "一网一匠是一个精心筛选的网站导航平台,致力于发现和推荐互联网上的优质内容和工具。平台按照不同领域和功能对网站进行分类,包括设计、开发、学习、工具、资源等多个类别。每个推荐的网站都经过严格筛选,确保内容的质量和实用性。一网一匠不仅提供网站链接,还附有简洁的描述和评价,帮助用户快速了解网站的核心功能和特点。对于希望发现高质量网络资源的用户来说,一网一匠是一个理想的起点,能够节省大量搜索时间,直达优质内容。",
+ category: "工具",
+ tags: ["导航", "资源", "精选"],
+ rating: 4.5,
+ featured: true,
+ addedDate: "2025-08-10"
+ },
+
+ // 更多工具类网站
+ {
+ id: "trello",
+ url: "https://trello.com",
+ title: "Trello",
+ description: "基于看板的项目管理工具",
+ detailedDescription: "Trello是一个基于看板(Kanban)方法的项目管理工具,使用卡片和列表来组织任务和工作流程。它的界面直观易用,用户可以创建看板、列表和卡片来管理项目进度。Trello支持团队协作,多人可以同时编辑看板,添加评论、附件和标签。它提供丰富的集成功能,可以与Slack、Google Drive、GitHub等工具连接。Trello还支持自动化功能,可以设置规则来自动移动卡片或发送通知。对于小型团队和个人项目来说,Trello是一个灵活且高效的项目管理工具,特别适合敏捷开发和任务跟踪。",
+ category: "工具",
+ tags: ["项目管理", "看板", "协作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-07"
+ },
+ {
+ id: "asana",
+ url: "https://asana.com",
+ title: "Asana",
+ description: "团队协作和项目管理平台",
+
+ category: "工具",
+ tags: ["项目管理", "团队协作", "任务"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-07"
+ },
+ {
+ id: "slack",
+ url: "https://slack.com",
+ title: "Slack",
+ description: "团队沟通和协作平台",
+ detailedDescription: "Slack是一个专为团队协作设计的即时通讯平台,提供频道、私聊、文件共享等功能。它支持创建主题频道,让团队成员可以按项目或话题进行讨论。Slack的集成功能非常强大,可以与GitHub、Trello、Google Drive等数百种工具连接,实现工作流程的自动化。它支持语音和视频通话,以及屏幕共享功能。Slack的搜索功能强大,可以快速找到历史消息和文件。对于远程团队和分布式办公来说,Slack是保持团队沟通和协作的重要工具,已经成为现代工作场所的标准配置。",
+ category: "工具",
+ tags: ["团队沟通", "协作", "即时消息"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-07"
+ },
+ {
+ id: "discord",
+ url: "https://discord.com",
+ title: "Discord",
+ description: "为游戏玩家和社区设计的语音、视频和文字聊天应用",
+
+ category: "工具",
+ tags: ["语音聊天", "社区", "游戏"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-07"
+ },
+ {
+ id: "zoom",
+ url: "https://zoom.us",
+ title: "Zoom",
+ description: "视频会议和在线协作平台",
+
+ category: "工具",
+ tags: ["视频会议", "在线协作", "远程"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-07"
+ },
+ {
+ id: "google-drive",
+ url: "https://drive.google.com",
+ title: "Google Drive",
+ description: "Google的云存储和文件同步服务",
+ detailedDescription: "Google Drive是Google提供的云存储和文件同步服务,与Google Workspace(原G Suite)深度集成。它提供15GB的免费存储空间,支持各种文件类型的存储和同步。Google Drive与Google Docs、Sheets、Slides等在线办公套件无缝集成,支持实时协作编辑。它提供强大的搜索功能,可以搜索文件内容、OCR识别图片中的文字。Google Drive支持文件版本控制,可以恢复之前的文件版本。对于个人用户和团队来说,Google Drive是文件存储、共享和协作的重要工具,特别适合需要在线办公和团队协作的场景。",
+ category: "工具",
+ tags: ["云存储", "文件同步", "协作"],
+ rating: 4.5,
+ featured: false,
+ addedDate: "2025-08-07"
+ },
+ {
+ id: "dropbox",
+ url: "https://www.dropbox.com",
+ title: "Dropbox",
+ description: "云存储和文件同步服务",
+
+ category: "工具",
+ tags: ["云存储", "文件同步", "备份"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-07"
+ },
+ {
+ id: "onedrive",
+ url: "https://onedrive.live.com",
+ title: "OneDrive",
+ description: "微软的云存储服务",
+
+ category: "工具",
+ tags: ["云存储", "微软", "Office"],
+ rating: 4,
+ featured: false,
+ addedDate: "2025-08-07"
+ }
+];
+
+// 根据ID获取网站详情
+export function getSiteById(id: string): FeaturedSite | undefined {
+ return featuredSites.find(site => site.id === id);
+}
+
+// 根据URL获取网站详情
+export function getSiteByUrl(url: string): FeaturedSite | undefined {
+ return featuredSites.find(site => site.url === url);
+}
+
+// 相关网站结果接口
+export interface RelatedSiteResult {
+ site: FeaturedSite;
+ score: number;
+ matchReasons: string[];
+}
+
+// 获取相关网站(使用加权算法)
+export function getRelatedSites(siteId: string, limit: number = 5): RelatedSiteResult[] {
+ const site = getSiteById(siteId);
+ if (!site) return [];
+
+ // 创建一个Map来存储网站ID和其相关性得分
+ const siteScores = new Map();
+
+ // 当前网站的标签集合
+ const currentTags = new Set(site.tags || []);
+
+ // 为每个网站计算相关性得分
+ featuredSites.forEach(s => {
+ // 跳过当前网站
+ if (s.id === site.id) return;
+
+ let score = 0;
+ const matchReasons: string[] = [];
+
+ // 1. 同类别加高分(权重最高)
+ if (s.category === site.category) {
+ score += 10;
+ matchReasons.push(`同类别: ${s.category}`);
+ }
+
+ // 2. 标签匹配度(每匹配一个标签加分)
+ if (s.tags && s.tags.length > 0) {
+ const matchingTags = s.tags.filter(tag => currentTags.has(tag));
+ if (matchingTags.length > 0) {
+ score += matchingTags.length * 3;
+ matchReasons.push(`相同标签: ${matchingTags.join(', ')}`);
+ }
+ }
+
+ // 3. 评分因素(评分越高加分越多)
+ score += s.rating;
+ if (s.rating >= 4.5) {
+ matchReasons.push('高评分网站');
+ }
+
+ // 4. 特别推荐的网站加分
+ if (s.featured) {
+ score += 2;
+ matchReasons.push('特别推荐');
+ }
+
+ // 只有得分大于0的网站才被考虑为相关网站
+ if (score > 0) {
+ siteScores.set(s.id, {
+ site: s,
+ score,
+ matchReasons
+ });
+ }
+ });
+
+ // 按得分排序并返回前limit个
+ return Array.from(siteScores.values())
+ .sort((a, b) => b.score - a.score)
+ .slice(0, limit);
+}
+
+// 获取特别推荐的网站
+export function getFeaturedSites(): FeaturedSite[] {
+ return featuredSites.filter(site => site.featured);
+}
+
+// 获取优先推荐的网站(优先显示可在中国大陆访问的)
+export function getPriorityFeaturedSites(): FeaturedSite[] {
+ const featured = featuredSites.filter(site => site.featured);
+
+ // 将网站分为两组:可在中国大陆访问的和可能无法访问的
+ const chinaAccessible = featured.filter(site => !site.unchina);
+ const chinaRestricted = featured.filter(site => site.unchina);
+
+ // 优先返回可在中国大陆访问的网站,然后是其他网站
+ return [...chinaAccessible, ...chinaRestricted];
+}
+
+// 根据分类获取网站
+export function getSitesByCategory(category: string): FeaturedSite[] {
+ if (category === CATEGORIES.ALL) {
+ return featuredSites;
+ }
+ return featuredSites.filter(site => site.category === category);
+}
+
+// 获取所有分类
+export function getAllCategories(): string[] {
+ return Object.values(CATEGORIES);
+}
+
+// 根据标签搜索网站
+
+// 获取分类统计信息
+export function getCategoryStats(): Record {
+ const stats: Record = {};
+
+ // 初始化所有分类为0
+ Object.values(CATEGORIES).forEach(category => {
+ stats[category] = 0;
+ });
+
+ // 统计每个分类的网站数量
+ featuredSites.forEach(site => {
+ if (stats[site.category] !== undefined) {
+ stats[site.category]++;
+ }
+ });
+
+ // "全部"分类显示总数
+ stats[CATEGORIES.ALL] = featuredSites.length;
+
+ return stats;
+}
+
+// 获取高评分网站
+export function getHighRatedSites(minRating: number = 4.5): FeaturedSite[] {
+ return featuredSites.filter(site => site.rating >= minRating);
+}
+
+// 获取最新添加的网站
+export function getRecentSites(limit: number = 10): FeaturedSite[] {
+ return featuredSites
+ .sort((a, b) => new Date(b.addedDate).getTime() - new Date(a.addedDate).getTime())
+ .slice(0, limit);
+}
+
+// 随机获取网站
+export function getRandomSites(limit: number = 5): FeaturedSite[] {
+ const shuffled = [...featuredSites].sort(() => 0.5 - Math.random());
+ return shuffled.slice(0, limit);
+}
+
+// 获取可能无法在中国大陆使用的网站
+export function getUnchinaSites(): FeaturedSite[] {
+ return featuredSites.filter(site => site.unchina === true);
+}
+
+// 检查网站是否可能无法在中国大陆使用
+export function isUnchinaSite(siteId: string): boolean {
+ const site = getSiteById(siteId);
+ return site?.unchina === true;
+}
diff --git a/src/client/utils/githubHotUtils.ts b/src/client/utils/githubHotUtils.ts
new file mode 100644
index 0000000..65ddf39
--- /dev/null
+++ b/src/client/utils/githubHotUtils.ts
@@ -0,0 +1,80 @@
+import { ref, Ref } from 'vue'
+
+export interface GithubHotItem {
+ id: string
+ title: string
+ desc: string
+ author: string
+ timestamp: number
+ hot: number
+ url: string
+ mobileUrl: string
+}
+
+export interface GithubHotResponse {
+ code: number
+ name: string
+ title: string
+ type: string
+ description: string
+ params: {
+ sort: {
+ name: string
+ type: Record
+ }
+ }
+ link: string
+ total: number
+ updateTime: string
+ fromCache: boolean
+ data: GithubHotItem[]
+}
+
+const hotData: Ref = ref(null)
+const loading = ref(false)
+const error = ref(null)
+
+export const useGithubHot = () => {
+ const fetchHotData = async (sort: string = 'featured') => {
+ loading.value = true
+ error.value = null
+
+ try {
+ const response = await fetch(`https://real-hot.lingke.ink/hellogithub?sort=${sort}`)
+ if (!response.ok) {
+ throw new Error(`请求失败: ${response.status}`)
+ }
+
+ const data = await response.json()
+ hotData.value = data
+ } catch (err) {
+ console.error('获取GitHub热门数据失败:', err)
+ error.value = err instanceof Error ? err.message : '获取数据失败'
+ } finally {
+ loading.value = false
+ }
+ }
+
+ // 格式化热度数字
+ const formatHot = (hot: number): string => {
+ if (hot >= 10000) {
+ return (hot / 10000).toFixed(1) + '万'
+ }
+ return hot.toString()
+ }
+
+ // 格式化时间戳
+ const formatTimestamp = (timestamp: number): string => {
+ const date = new Date(timestamp)
+ return `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
+ }
+
+ return {
+ hotData,
+ loading,
+ error,
+ fetchHotData,
+ formatHot,
+ formatTimestamp
+ }
+}
\ No newline at end of file
diff --git a/src/client/utils/location.ts b/src/client/utils/location.ts
new file mode 100644
index 0000000..0301b96
--- /dev/null
+++ b/src/client/utils/location.ts
@@ -0,0 +1,206 @@
+import { ref } from 'vue'
+import axios from 'axios'
+import Cookies from 'js-cookie'
+import { LocationData } from './weather'
+
+// 高德地图API相关函数
+export const useLocation = (AMAP_KEY: string) => {
+ // 位置数据
+ const locationData = ref({
+ latitude: 0,
+ longitude: 0,
+ city: '深圳',
+ adcode: '440304' // 默认深圳市福田区
+ })
+
+ // 获取用户位置
+ const getUserLocation = (): Promise => {
+ return new Promise((resolve, reject) => {
+ if (!navigator.geolocation) {
+ reject(new Error('您的浏览器不支持地理定位'))
+ return
+ }
+
+ console.log('请求浏览器地理定位...')
+ navigator.geolocation.getCurrentPosition(
+ (position) => {
+ console.log('浏览器地理定位成功:', {
+ latitude: position.coords.latitude,
+ longitude: position.coords.longitude,
+ accuracy: position.coords.accuracy
+ })
+ resolve(position)
+ },
+ (error) => {
+ console.error('浏览器地理定位失败:', {
+ code: error.code,
+ message: error.message
+ })
+ reject(error)
+ },
+ {
+ enableHighAccuracy: true, // 尝试获取最精确的位置
+ timeout: 10000, // 10秒超时
+ maximumAge: 0 // 不使用缓存的位置
+ }
+ )
+ })
+ }
+
+ // 通过经纬度获取城市信息(逆地理编码)
+ const getCityByLocation = async (latitude: number, longitude: number): Promise<{ city: string, adcode: string }> => {
+ try {
+ // 使用高德地图逆地理编码API获取位置信息
+ const location = `${longitude},${latitude}`
+ console.log('开始逆地理编码请求,坐标:', location)
+
+ const response = await axios.get('/amap-api/v3/geocode/regeo', {
+ params: {
+ key: AMAP_KEY,
+ location: location,
+ extensions: 'base', // 返回基本地址信息
+ radius: 1000, // 搜索半径
+ output: 'JSON'
+ }
+ })
+
+ console.log('逆地理编码响应:', response.data)
+
+ if (response.data.status === '1' && response.data.regeocode && response.data.regeocode.addressComponent) {
+ const addressComponent = response.data.regeocode.addressComponent
+ console.log('获取到地址组件:', addressComponent)
+
+ // 从addressComponent中获取城市和adcode
+ return {
+ city: addressComponent.city || addressComponent.province, // 如果city为空(直辖市),则使用province
+ adcode: addressComponent.adcode // 行政区编码
+ }
+ } else {
+ console.error('逆地理编码返回无效数据:', response.data)
+ throw new Error('获取城市信息失败')
+ }
+ } catch (error) {
+ console.error('逆地理编码失败:', error)
+ // 使用IP定位作为备选方案
+ try {
+ console.log('尝试使用IP定位作为备选方案')
+ const ipResponse = await axios.get('/amap-api/v3/ip', {
+ params: {
+ key: AMAP_KEY,
+ output: 'JSON'
+ }
+ })
+
+ console.log('IP定位响应:', ipResponse.data)
+
+ if (ipResponse.data.status === '1' && ipResponse.data.city) {
+ return {
+ city: ipResponse.data.city,
+ adcode: ipResponse.data.adcode // 使用IP定位返回的adcode
+ }
+ } else {
+ console.error('IP定位返回无效数据:', ipResponse.data)
+ throw new Error('IP定位失败')
+ }
+ } catch (ipError) {
+ console.error('IP定位失败:', ipError)
+ // 使用默认位置
+ return { city: '北京', adcode: '110101' }
+ }
+ }
+ }
+
+ // IP定位作为备选方案
+ const getLocationByIp = async (): Promise<{ city: string, adcode: string }> => {
+ try {
+ const response = await axios.get('/amap-api/v3/ip', {
+ params: {
+ key: AMAP_KEY,
+ output: 'JSON'
+ }
+ })
+
+ console.log('IP定位响应:', response.data)
+
+ if (response.data.status === '1' && response.data.city) {
+ return {
+ city: response.data.city,
+ adcode: response.data.adcode || '110101' // 如果没有adcode,使用默认值
+ }
+ } else {
+ console.error('IP定位返回无效数据:', response.data)
+ throw new Error('IP定位失败')
+ }
+ } catch (error) {
+ console.error('IP定位请求失败:', error)
+ throw error
+ }
+ }
+
+ // 保存位置信息到Cookie和localStorage
+ const saveLocationData = (data: LocationData): void => {
+ Cookies.set('locationData', JSON.stringify(data), { expires: 30 }) // 30天有效期
+ localStorage.setItem('locationData', JSON.stringify(data))
+ }
+
+ // 从Cookie或localStorage获取位置信息
+ const loadLocationData = (): LocationData | null => {
+ const cookieData = Cookies.get('locationData')
+ if (cookieData) {
+ try {
+ return JSON.parse(cookieData)
+ } catch (error) {
+ console.error('解析位置数据失败:', error)
+ }
+ }
+
+ const localData = localStorage.getItem('locationData')
+ if (localData) {
+ try {
+ return JSON.parse(localData)
+ } catch (error) {
+ console.error('解析位置数据失败:', error)
+ }
+ }
+
+ return null
+ }
+
+ return {
+ locationData,
+ getUserLocation,
+ getCityByLocation,
+ getLocationByIp,
+ saveLocationData,
+ loadLocationData
+ }
+}
+
+// 常用城市列表
+export const commonCities = [
+ { name: '北京', adcode: '110100' },
+ { name: '上海', adcode: '310100' },
+ { name: '广州', adcode: '440100' },
+ { name: '深圳', adcode: '440300' },
+ { name: '杭州', adcode: '330100' },
+ { name: '成都', adcode: '510100' },
+ { name: '武汉', adcode: '420100' },
+ { name: '西安', adcode: '610100' },
+ { name: '南京', adcode: '320100' },
+ { name: '重庆', adcode: '500100' },
+ { name: '天津', adcode: '120100' },
+ { name: '苏州', adcode: '320500' },
+ { name: '厦门', adcode: '350200' },
+ { name: '青岛', adcode: '370200' },
+ { name: '大连', adcode: '210200' },
+ { name: '郑州', adcode: '410100' },
+ { name: '长沙', adcode: '430100' },
+ { name: '东莞', adcode: '441900' },
+ { name: '佛山', adcode: '440600' },
+ { name: '宁波', adcode: '330200' },
+ { name: '合肥', adcode: '340100' },
+ { name: '济南', adcode: '370100' },
+ { name: '福州', adcode: '350100' },
+ { name: '沈阳', adcode: '210100' },
+ { name: '昆明', adcode: '530100' }
+]
\ No newline at end of file
diff --git a/src/client/utils/searchEngines.ts b/src/client/utils/searchEngines.ts
new file mode 100644
index 0000000..0bbd6cc
--- /dev/null
+++ b/src/client/utils/searchEngines.ts
@@ -0,0 +1,83 @@
+import { ref, computed } from 'vue'
+
+// 定义搜索引擎接口类型
+export interface SearchEngine {
+ id: string;
+ name: string;
+ svg: string;
+ url: string;
+}
+
+// 搜索引擎配置
+export const searchEngines: SearchEngine[] = [
+ {
+ id: 'bing',
+ name: '必应',
+ svg: ' ',
+ url: 'https://www.bing.com/search?q='
+ },
+ {
+ id: 'bilibili',
+ name: '哔哩哔哩',
+ svg: ' ',
+ url: 'https://search.bilibili.com/all?keyword='
+ },
+ {
+ id: 'google',
+ name: '谷歌',
+ svg: ' ',
+ url: 'https://www.google.com/search?q='
+ },
+ {
+ id: 'zhihu',
+ name: '知乎',
+ svg: ' ',
+ url: 'https://www.zhihu.com/search?type=content&q='
+ }
+]
+
+export const useSearchEngine = () => {
+ const inputValue = ref('')
+ const selectedEngine = ref('bing') // 默认搜索引擎为Bing
+
+ // 切换搜索引擎
+ const switchEngine = (engineId: string): void => {
+ selectedEngine.value = engineId
+ }
+
+ // 获取当前选中的搜索引擎
+ const currentEngine = computed(() => {
+ return searchEngines.find(engine => engine.id === selectedEngine.value) || searchEngines[0]
+ })
+
+ const handleSubmit = (): void => {
+ if (!inputValue.value.trim()) return
+
+ // 获取当前搜索引擎的URL
+ const engine = currentEngine.value
+ const searchUrl = engine.url + encodeURIComponent(inputValue.value)
+
+ // 在新标签页中打开搜索结果
+ window.open(searchUrl, '_blank')
+
+ console.log(`使用 ${engine.name} 搜索:`, inputValue.value)
+ }
+
+ // 处理回车键提交,但允许Shift+Enter换行
+ const handleKeydown = (e: KeyboardEvent): void => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault()
+ handleSubmit()
+ }
+ }
+
+ return {
+ inputValue,
+ selectedEngine,
+ searchEngines,
+ currentEngine,
+ switchEngine,
+ handleSubmit,
+ handleKeydown
+ }
+}
\ No newline at end of file
diff --git a/src/client/utils/storage.ts b/src/client/utils/storage.ts
new file mode 100644
index 0000000..a927b80
--- /dev/null
+++ b/src/client/utils/storage.ts
@@ -0,0 +1,19 @@
+// 安全的localStorage访问方法
+export const safeLocalStorage = {
+ getItem(key: string, defaultValue: any = null): any {
+ if (typeof window !== 'undefined' && window.localStorage) {
+ return localStorage.getItem(key) || defaultValue;
+ }
+ return defaultValue;
+ },
+ setItem(key: string, value: any): void {
+ if (typeof window !== 'undefined' && window.localStorage) {
+ localStorage.setItem(key, value);
+ }
+ },
+ removeItem(key: string): void {
+ if (typeof window !== 'undefined' && window.localStorage) {
+ localStorage.removeItem(key);
+ }
+ }
+};
\ No newline at end of file
diff --git a/src/client/utils/versionHistory.ts b/src/client/utils/versionHistory.ts
new file mode 100644
index 0000000..d853da9
--- /dev/null
+++ b/src/client/utils/versionHistory.ts
@@ -0,0 +1,137 @@
+// 版本历史记录
+export const versionHistory = [
+ {
+ version: '3.0.0',
+ date: '即将到来',
+ mode: 'coomingsoon', // beta 或 production
+ changes: [
+ 'AI搜索',
+ '广告过滤',
+ '云同步',
+ '浏览器插件',
+ '更多功能即将推出'
+ ]
+ },
+ {
+ version: '2.1.2',
+ date: '2025-08-10',
+ mode: 'beta', // beta 或 production
+ changes: [
+ '增加了全新的热点页面与功能',
+ '将部分逻辑拆解提高了访问速度',
+ '更新了首页搜索组件的逻辑提高了访问速度',
+ '新增了网站推荐功能',
+ '修复了顶栏背景问题与逻辑问题',
+ '完善了today卡片样式',
+ '新增了其他网站的描述内容',
+ '修复了Today网站详情页面标题与评分不对其问题',
+ '删除了内置引擎的基础服务组件'
+ ]
+ },
+ {
+ version: '2.1.0',
+ date: '2025-08-10',
+ mode: 'beta', // beta 或 production
+ changes: [
+ '新增了今日推荐功能',
+ '新增了网站推荐功能',
+ '修复了顶栏背景问题与逻辑问题',
+ '修复了today分类无法在手机端滑动问题',
+ '修复了today卡片会覆盖别的内容的问题',
+ '完善了today卡片样式',
+ '新增了其他网站的描述内容',
+ '新增了管理人员的仪表盘',
+ '完成了内置引擎的基础服务组件'
+ ]
+ },
+ {
+ version: '2.0.5',
+ date: '2025-08-08',
+ mode: 'beta', // beta 或 production
+ changes: [
+ '新增了初始化功能',
+ '修复了注册问题',
+ '修复已知问题'
+ ]
+ },
+ {
+ version: '2.0.2',
+ date: '2025-08-08',
+ mode: 'beta', // beta 或 production
+ changes: [
+ '新增了壁纸功能测试',
+ '开放了注册与部分账户功能',
+ '修复已知问题'
+ ]
+ },
+ {
+ version: '2.0.1',
+ date: '2025-08-07',
+ mode: 'production', // beta 或 production
+ changes: [
+ '暂时停止了自动获取定位功能,需要手动选择',
+ '新增了13个城市供选择',
+ '修复已知问题'
+ ]
+ },
+ {
+ version: '2.0.1',
+ date: '2025-08-07',
+ mode: 'beta', // beta 或 production
+ changes: [
+ '修复了设置无法保存问题',
+ '修复天气获取问题',
+ '修复了定位获取问题',
+ '修复已知问题'
+ ]
+ },
+ {
+ version: '2.0.0',
+ date: '2025-08-06',
+ mode: 'production', // beta 或 production
+ changes: [
+ '全新的用户界面设计',
+ '优化页面加载速度',
+ '修复已知问题'
+ ]
+ },
+ {
+ version: '2.0.0',
+ date: '2025-08-06',
+ mode: 'beta', // beta 或 production
+ changes: [
+ '删除了百度搜索',
+ '修正天气定位授权问题',
+ '优化页面加载速度',
+ '修复已知问题'
+ ]
+ },
+ {
+ version: '1.9.0',
+ date: '2025-08-06',
+ mode: 'beta', // beta 或 production
+ changes: [
+ '添加天气功能',
+ '优化页面加载速度',
+ '修复已知问题'
+ ]
+ },
+ {
+ version: '1.5.0',
+ date: '2025-06-15',
+ mode: 'production',
+ changes: [
+ '添加用户设置页面',
+ '改进搜索功能',
+ '优化移动端体验'
+ ]
+ },
+ {
+ version: '1.0.0',
+ date: '2025-01-01',
+ mode: 'production',
+ changes: [
+ '首次发布'
+ ]
+ }
+]
\ No newline at end of file
diff --git a/src/client/utils/wallpaper.ts b/src/client/utils/wallpaper.ts
new file mode 100644
index 0000000..1267bff
--- /dev/null
+++ b/src/client/utils/wallpaper.ts
@@ -0,0 +1,123 @@
+import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
+import { safeLocalStorage } from './storage'
+
+// 壁纸相关
+const bingDailyWallpaperUrl = ref("https://bing.img.run/uhd.php")
+const bingRandomWallpaperUrl = ref("https://bing.img.run/rand_uhd.php")
+const wallpaperLoaded = ref(false)
+const wallpaperError = ref(false)
+
+const customBgImage = ref(safeLocalStorage.getItem('customBgImage', null))
+const wallpaperOpacity = ref(parseInt(safeLocalStorage.getItem('wallpaperOpacity', '80')))
+const wallpaperBlur = ref(parseInt(safeLocalStorage.getItem('wallpaperBlur', '0')))
+
+// 当前壁纸URL的计算属性
+const currentWallpaperUrl = computed(() => {
+ // 首次访问时默认使用纯净模式
+ let wallpaperType = safeLocalStorage.getItem('wallpaperType', 'pure')
+
+ // 确保壁纸类型有效,如果无效则使用纯净模式
+ if (!wallpaperType || wallpaperType === 'null' || wallpaperType === 'undefined') {
+ wallpaperType = 'pure'
+ safeLocalStorage.setItem('wallpaperType', 'pure')
+ console.log('首次访问或壁纸类型无效,设置为纯净模式')
+ }
+
+ if (wallpaperType === 'bing-daily') {
+ return bingDailyWallpaperUrl.value
+ } else if (wallpaperType === 'bing-random') {
+ return bingRandomWallpaperUrl.value
+ } else if (wallpaperType === 'custom' && customBgImage.value) {
+ return customBgImage.value
+ }
+
+ return ''
+})
+
+// 壁纸加载处理函数
+const handleWallpaperLoaded = () => {
+ wallpaperLoaded.value = true
+ wallpaperError.value = false
+ console.log('壁纸加载成功')
+}
+
+// 壁纸加载错误处理函数
+const handleWallpaperError = () => {
+ wallpaperError.value = true
+ console.error('壁纸加载失败')
+}
+
+// 计算壁纸样式
+const wallpaperStyle = computed(() => {
+ return {
+ opacity: wallpaperOpacity.value / 100,
+ filter: `blur(${wallpaperBlur.value}px)`
+ }
+})
+
+// 定义自定义事件类型
+interface WallpaperSettingsChangedEvent extends CustomEvent {
+ detail: {
+ type: string;
+ value: number;
+ };
+}
+
+interface WallpaperRefreshEvent extends CustomEvent {
+ detail: {
+ timestamp: number;
+ };
+}
+
+// 监听壁纸变化事件
+const setupWallpaperListener = () => {
+ // 监听壁纸设置变化事件
+ window.addEventListener('wallpaper-settings-changed', ((event: Event) => {
+ const customEvent = event as WallpaperSettingsChangedEvent;
+ const { type, value } = customEvent.detail;
+ if (type === 'opacity') {
+ wallpaperOpacity.value = value;
+ } else if (type === 'blur') {
+ wallpaperBlur.value = value;
+ }
+ }) as EventListener);
+
+ // 监听壁纸刷新事件
+ window.addEventListener('wallpaper-refresh', ((event: Event) => {
+ const customEvent = event as WallpaperRefreshEvent;
+ const { timestamp } = customEvent.detail;
+ // 为URL添加时间戳参数,强制刷新
+ bingRandomWallpaperUrl.value = `https://bing.img.run/rand_uhd.php?t=${timestamp}`;
+ }) as EventListener);
+};
+
+// 清除壁纸监听器
+const clearWallpaperListeners = () => {
+ // 由于我们不能直接移除匿名函数监听器,这里只是为了示例
+ // 在实际应用中,应该保存监听器引用以便后续移除
+ window.removeEventListener('wallpaper-settings-changed', (() => {}) as EventListener);
+ window.removeEventListener('wallpaper-changed', (() => {}) as EventListener);
+ window.removeEventListener('wallpaper-refresh', (() => {}) as EventListener);
+}
+
+// 导出壁纸相关功能
+export const useWallpaper = () => {
+ onMounted(() => {
+ // 设置壁纸变化监听器
+ setupWallpaperListener();
+ })
+
+ onBeforeUnmount(() => {
+ // 移除壁纸变化监听器
+ clearWallpaperListeners();
+ })
+
+ return {
+ currentWallpaperUrl,
+ wallpaperLoaded,
+ wallpaperError,
+ wallpaperStyle,
+ handleWallpaperLoaded,
+ handleWallpaperError
+ }
+}
\ No newline at end of file
diff --git a/src/client/utils/weather.ts b/src/client/utils/weather.ts
new file mode 100644
index 0000000..dbb0c5b
--- /dev/null
+++ b/src/client/utils/weather.ts
@@ -0,0 +1,941 @@
+import { ref, onMounted, onUnmounted } from 'vue'
+import axios from 'axios'
+import Cookies from 'js-cookie'
+import { toast } from 'vue-sonner'
+
+// 定义天气接口类型
+export interface WeatherData {
+ temperature: string;
+ weather: string;
+ city: string;
+ humidity: string;
+ windDirection: string;
+ windPower: string;
+ loading: boolean;
+ error: string | null;
+}
+
+// 定义位置接口类型
+export interface LocationData {
+ latitude: number;
+ longitude: number;
+ city: string;
+ adcode: string;
+}
+
+// 天气设置默认值
+const defaultWeatherSettings = {
+ enabled: true,
+ unit: 'celsius', // 摄氏度
+ showHumidity: true,
+ showWind: true
+}
+
+// 天气图标映射
+export const weatherIcons: Record = {
+ // 晴
+ '晴': ' ',
+ // 多云
+ '多云': ' ',
+ // 阴
+ '阴': ' ',
+ // 小雨
+ '小雨': ' ',
+ // 中雨
+ '中雨': ' ',
+ // 大雨
+ '大雨': ' ',
+ // 雷阵雨
+ '雷阵雨': ' ',
+ // 默认图标
+ 'default': ' '
+}
+
+// 从Cookie中获取天气设置 - 优化加载过程
+export const loadWeatherSettings = (): { enabled: boolean, unit: string, showHumidity: boolean, showWind: boolean } => {
+ // 直接从cookie读取,确保每次都获取最新设置
+ const settings = Cookies.get('weatherSettings')
+ if (settings) {
+ try {
+ const parsedSettings = JSON.parse(settings)
+ console.log('从cookie中读取到天气设置:', parsedSettings)
+ // 更新本地缓存
+ localStorage.setItem('weatherSettingsCache', settings)
+ return parsedSettings
+ } catch (error) {
+ console.error('解析天气设置失败:', error)
+ }
+ }
+
+ // 默认设置
+ console.log('未找到天气设置cookie或解析失败,使用默认设置')
+ return { ...defaultWeatherSettings }
+}
+
+// 更新天气设置到Cookie
+export const updateWeatherSettings = (settings: { enabled: boolean, unit: string, showHumidity: boolean, showWind: boolean }): void => {
+ // 同时更新cookie和本地缓存
+ Cookies.set('weatherSettings', JSON.stringify(settings), { expires: 365 }) // 一年有效期
+ localStorage.setItem('weatherSettingsCache', JSON.stringify(settings))
+ console.log('天气设置已保存到cookie和本地缓存:', settings)
+}
+
+// 获取天气图标
+export const getWeatherIcon = (weatherDesc: string): string => {
+ return weatherIcons[weatherDesc] || weatherIcons['default']
+}
+
+// 摄氏度转华氏度
+export const convertCelsiusToFahrenheit = (celsius: number): number => {
+ return celsius * 9/5 + 32
+}
+
+// 华氏度转摄氏度
+export const convertFahrenheitToCelsius = (fahrenheit: number): number => {
+ return (fahrenheit - 32) * 5/9
+}
+
+export const useWeather = (AMAP_KEY: string) => {
+ // 天气设置 - 使用ref包装以实现响应式
+ const weatherSettings = ref(loadWeatherSettings())
+
+ // 天气数据 - 初始状态设为非加载状态,避免一开始就显示加载动画
+ const weatherData = ref({
+ temperature: '',
+ weather: '',
+ city: '深圳',
+ humidity: '',
+ windDirection: '',
+ windPower: '',
+ loading: false, // 初始设为false,等待实际请求时再设为true
+ error: null
+ })
+
+ // 位置数据
+ const locationData = ref({
+ latitude: 0,
+ longitude: 0,
+ city: '深圳',
+ adcode: '440304' // 默认深圳市福田区
+ })
+
+ // 位置授权对话框
+ const showLocationDialog = ref(false)
+ const locationAuthorized = ref(false)
+ const locationLoading = ref(false)
+ const locationError = ref(null)
+ const showCitySelector = ref(false)
+ const selectedCity = ref('')
+ const commonCities = [
+ { name: '北京', adcode: '110100' },
+ { name: '上海', adcode: '310100' },
+ { name: '广州', adcode: '440100' },
+ { name: '深圳', adcode: '440300' },
+ { name: '杭州', adcode: '330100' },
+ { name: '成都', adcode: '510100' },
+ { name: '武汉', adcode: '420100' },
+ { name: '西安', adcode: '610100' },
+ { name: '南京', adcode: '320100' },
+ { name: '重庆', adcode: '500100' },
+ { name: '天津', adcode: '120100' },
+ { name: '苏州', adcode: '320500' },
+ { name: '厦门', adcode: '350200' },
+ { name: '青岛', adcode: '370200' },
+ { name: '大连', adcode: '210200' },
+ { name: '郑州', adcode: '410100' },
+ { name: '长沙', adcode: '430100' },
+ { name: '东莞', adcode: '441900' },
+ { name: '佛山', adcode: '440600' },
+ { name: '宁波', adcode: '330200' },
+ { name: '合肥', adcode: '340100' },
+ { name: '济南', adcode: '370100' },
+ { name: '福州', adcode: '350100' },
+ { name: '沈阳', adcode: '210100' },
+ { name: '昆明', adcode: '530100' }
+ ]
+
+ // 监听cookie变化的函数
+ const checkCookieChanges = () => {
+ const newSettings = loadWeatherSettings()
+ const currentSettings = weatherSettings.value
+
+ // 检查设置是否有变化
+ if (newSettings.enabled !== currentSettings.enabled ||
+ newSettings.unit !== currentSettings.unit ||
+ newSettings.showHumidity !== currentSettings.showHumidity ||
+ newSettings.showWind !== currentSettings.showWind) {
+
+ console.log('检测到天气设置变化,更新设置:', newSettings)
+ weatherSettings.value = newSettings
+
+ // 如果温度单位发生变化,需要重新格式化温度显示
+ if (newSettings.unit !== currentSettings.unit && weatherData.value.temperature) {
+ const tempValue = parseFloat(weatherData.value.temperature)
+ if (!isNaN(tempValue)) {
+ if (newSettings.unit === 'celsius' && currentSettings.unit === 'fahrenheit') {
+ // 华氏度转摄氏度
+ weatherData.value.temperature = convertFahrenheitToCelsius(tempValue).toFixed(1)
+ } else if (newSettings.unit === 'fahrenheit' && currentSettings.unit === 'celsius') {
+ // 摄氏度转华氏度
+ weatherData.value.temperature = convertCelsiusToFahrenheit(tempValue).toFixed(1)
+ }
+ }
+ }
+
+ // 如果天气功能被禁用,不需要重新获取天气数据
+ if (newSettings.enabled && currentSettings.enabled) {
+ // 如果只是显示选项变化,不需要重新获取天气数据
+ console.log('天气设置已更新,但不需要重新获取天气数据')
+ } else if (newSettings.enabled && !currentSettings.enabled) {
+ // 如果天气功能从禁用变为启用,需要重新获取天气数据
+ console.log('天气功能已启用,重新获取天气数据')
+ fetchWeather()
+ }
+ }
+ }
+
+ // 设置定时器,定期检查cookie变化
+ let cookieCheckInterval: number | null = null
+
+ // 获取用户位置
+ const getUserLocation = (): Promise => {
+ return new Promise((resolve, reject) => {
+ if (!navigator.geolocation) {
+ reject(new Error('您的浏览器不支持地理定位'))
+ return
+ }
+
+ console.log('请求浏览器地理定位...')
+ navigator.geolocation.getCurrentPosition(
+ (position) => {
+ console.log('浏览器地理定位成功:', {
+ latitude: position.coords.latitude,
+ longitude: position.coords.longitude,
+ accuracy: position.coords.accuracy
+ })
+ resolve(position)
+ },
+ (error) => {
+ console.error('浏览器地理定位失败:', {
+ code: error.code,
+ message: error.message
+ })
+ reject(error)
+ },
+ {
+ enableHighAccuracy: true, // 尝试获取最精确的位置
+ timeout: 10000, // 10秒超时
+ maximumAge: 0 // 不使用缓存的位置
+ }
+ )
+ })
+ }
+
+ // 通过经纬度获取城市信息(逆地理编码)
+ const getCityByLocation = async (latitude: number, longitude: number): Promise<{ city: string, adcode: string }> => {
+ try {
+ // 使用高德地图逆地理编码API获取位置信息
+ const location = `${longitude},${latitude}`
+ console.log('开始逆地理编码请求,坐标:', location)
+
+ const response = await axios.get('/amap-api/v3/geocode/regeo', {
+ params: {
+ key: AMAP_KEY,
+ location: location,
+ extensions: 'base', // 返回基本地址信息
+ radius: 1000, // 搜索半径
+ output: 'JSON'
+ }
+ })
+
+ console.log('逆地理编码响应:', response.data)
+
+ if (response.data.status === '1' && response.data.regeocode && response.data.regeocode.addressComponent) {
+ const addressComponent = response.data.regeocode.addressComponent
+ console.log('获取到地址组件:', addressComponent)
+
+ // 从addressComponent中获取城市和adcode
+ return {
+ city: addressComponent.city || addressComponent.province, // 如果city为空(直辖市),则使用province
+ adcode: addressComponent.adcode // 行政区编码
+ }
+ } else {
+ console.error('逆地理编码返回无效数据:', response.data)
+ throw new Error('获取城市信息失败')
+ }
+ } catch (error) {
+ console.error('逆地理编码失败:', error)
+ // 使用IP定位作为备选方案
+ try {
+ console.log('尝试使用IP定位作为备选方案')
+ const ipResponse = await axios.get('/amap-api/v3/ip', {
+ params: {
+ key: AMAP_KEY,
+ output: 'JSON'
+ }
+ })
+
+ console.log('IP定位响应:', ipResponse.data)
+
+ if (ipResponse.data.status === '1' && ipResponse.data.city) {
+ return {
+ city: ipResponse.data.city,
+ adcode: ipResponse.data.adcode // 使用IP定位返回的adcode
+ }
+ } else {
+ console.error('IP定位返回无效数据:', ipResponse.data)
+ throw new Error('IP定位失败')
+ }
+ } catch (ipError) {
+ console.error('IP定位失败:', ipError)
+ // 使用默认位置
+ return { city: '北京', adcode: '110101' }
+ }
+ }
+ }
+
+ // 请求位置授权
+ const requestLocationPermission = async (): Promise => {
+ locationLoading.value = true
+ locationError.value = null
+
+ // 首先检查天气功能是否启用
+ const currentSettings = loadWeatherSettings()
+ if (!currentSettings.enabled) {
+ toast.error('天气功能未启用', {
+ description: '请先在设置中启用天气功能'
+ })
+ locationLoading.value = false
+ return
+ }
+
+ try {
+ const position = await getUserLocation()
+
+ // 获取城市信息
+ const cityInfo = await getCityByLocation(position.coords.latitude, position.coords.longitude)
+
+ // 更新位置数据
+ locationData.value = {
+ latitude: position.coords.latitude,
+ longitude: position.coords.longitude,
+ city: cityInfo.city,
+ adcode: cityInfo.adcode
+ }
+
+ // 保存授权状态和位置方法到Cookie和localStorage
+ Cookies.set('locationAuthorized', 'true', { expires: 30 }) // 30天有效期
+ Cookies.set('locationData', JSON.stringify(locationData.value), { expires: 30 })
+ localStorage.setItem('locationAuthorized', 'true')
+ localStorage.setItem('locationMethod', 'auto')
+ locationAuthorized.value = true
+
+ // 使用获取到的位置获取天气数据
+ await fetchWeather()
+
+ console.log('位置授权成功:', locationData.value)
+
+ // 关闭对话框
+ showLocationDialog.value = false
+ } catch (error) {
+ console.error('获取位置失败:', error)
+ locationError.value = error instanceof Error ? error.message : '获取位置失败,请检查浏览器权限设置'
+
+ // 尝试使用IP定位
+ try {
+ const ipResponse = await axios.get('/amap-api/v3/ip', {
+ params: {
+ key: AMAP_KEY,
+ output: 'JSON'
+ }
+ })
+
+ if (ipResponse.data.status === '1' && ipResponse.data.city) {
+ locationData.value = {
+ latitude: 0,
+ longitude: 0,
+ city: ipResponse.data.city,
+ adcode: ipResponse.data.adcode
+ }
+
+ // 使用IP定位获取天气数据
+ await fetchWeather()
+
+ // 关闭对话框
+ showLocationDialog.value = false
+ } else {
+ // 使用默认位置获取天气数据
+ await fetchWeather()
+ }
+ } catch (ipError) {
+ console.error('IP定位失败:', ipError)
+ // 使用默认位置获取天气数据
+ await fetchWeather()
+ }
+ } finally {
+ locationLoading.value = false
+ }
+ }
+
+ // 拒绝位置授权
+ const denyLocationPermission = async (): Promise => {
+ // 保存拒绝状态到Cookie
+ Cookies.set('locationAuthorized', 'false', { expires: 30 }) // 30天有效期
+ locationAuthorized.value = false
+ showLocationDialog.value = false
+
+ // 尝试使用IP定位
+ try {
+ const ipResponse = await axios.get('/amap-api/v3/ip', {
+ params: {
+ key: AMAP_KEY,
+ output: 'JSON'
+ }
+ })
+
+ if (ipResponse.data.status === '1' && ipResponse.data.city) {
+ locationData.value = {
+ latitude: 0,
+ longitude: 0,
+ city: ipResponse.data.city,
+ adcode: ipResponse.data.adcode
+ }
+ }
+ } catch (error) {
+ console.error('IP定位失败:', error)
+ }
+
+ // 使用当前位置获取天气数据
+ await fetchWeather()
+ }
+
+ // 选择城市
+ const selectCity = (cityName: string, adcode: string): void => {
+ selectedCity.value = cityName
+ console.log('选择城市:', cityName, adcode)
+ }
+
+ // 确认城市选择
+ const confirmCitySelection = async (): Promise => {
+ if (!selectedCity.value) return
+
+ // 找到选中的城市
+ const city = commonCities.find(c => c.name === selectedCity.value)
+ if (!city) return
+
+ console.log('确认选择城市:', city)
+
+ // 更新位置数据
+ locationData.value = {
+ latitude: 0,
+ longitude: 0,
+ city: city.name,
+ adcode: city.adcode
+ }
+
+ // 保存到Cookie
+ Cookies.set('locationAuthorized', 'true', { expires: 30 })
+ Cookies.set('locationData', JSON.stringify(locationData.value), { expires: 30 })
+ locationAuthorized.value = true
+
+ // 关闭对话框
+ showCitySelector.value = false
+
+ // 获取天气数据
+ await fetchWeather()
+ }
+
+ // 检查是否有缓存的天气数据
+ const checkCachedWeather = (): boolean => {
+ // 如果是首次加载或强制刷新,不使用缓存
+ if (window.performance) {
+ const navEntry = window.performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
+ if (navEntry && navEntry.type === 'reload') {
+ console.log('页面被刷新,不使用天气缓存');
+ localStorage.removeItem('weatherDataCache');
+ localStorage.removeItem('weatherCacheTimestamp');
+ return false;
+ }
+ }
+
+ const cachedWeather = localStorage.getItem('weatherDataCache')
+ const cachedTimestamp = localStorage.getItem('weatherCacheTimestamp')
+
+ if (cachedWeather && cachedTimestamp) {
+ try {
+ // 检查缓存是否在15分钟内(减少缓存时间)
+ const timestamp = parseInt(cachedTimestamp)
+ const now = Date.now()
+ const fifteenMinutesInMs = 15 * 60 * 1000
+
+ if (now - timestamp < fifteenMinutesInMs) {
+ // 缓存有效,使用缓存数据
+ const parsedData = JSON.parse(cachedWeather)
+ console.log('使用缓存的天气数据:',parsedData)
+
+ // 更新天气数据但保持loading为false
+ weatherData.value = {
+ ...parsedData,
+ loading: false,
+ error: null
+ }
+
+ return true
+ } else {
+ console.log('天气数据缓存已过期,需要重新获取')
+ }
+ } catch (error) {
+ console.error('解析缓存天气数据失败:', error)
+ }
+ }
+
+ return false
+ }
+
+ // 获取天气数据
+ const fetchWeather = async (): Promise => {
+ // 首先检查天气功能是否启用
+ const currentSettings = loadWeatherSettings()
+ if (!currentSettings.enabled) {
+ console.log('天气功能已禁用,不获取天气数据')
+ weatherData.value.loading = false
+ return
+ }
+
+ // 检查是否有有效的缓存数据
+ if (checkCachedWeather()) {
+ console.log('使用缓存的天气数据,不发起新请求')
+ return
+ }
+
+ // 设置加载状态
+ weatherData.value.loading = true
+ weatherData.value.error = null
+
+ try {
+ // 确保有有效的adcode
+ if (!locationData.value.adcode) {
+ console.warn('没有有效的adcode,使用默认值110101')
+ locationData.value.adcode = '110101' // 北京市东城区
+ }
+
+ console.log('开始获取天气数据,使用区域编码:', locationData.value.adcode)
+
+ // 使用代理请求高德天气API获取天气数据
+ const response = await axios.get('/amap-api/v3/weather/weatherInfo', {
+ params: {
+ key: AMAP_KEY,
+ city: locationData.value.adcode, // 高德天气API使用adcode作为city参数
+ extensions: 'base',
+ output: 'JSON'
+ }
+ })
+
+ // 解析天气数据
+ const data = response.data
+ console.log('天气API响应:', data)
+
+ if (data.status === '1' && data.lives && data.lives.length > 0) {
+ const live = data.lives[0]
+ // 重新获取最新的天气设置
+ weatherSettings.value = loadWeatherSettings()
+ // 根据设置的温度单位转换温度值
+ const tempInCelsius = parseFloat(live.temperature)
+
+ // 更新天气数据
+ const updatedWeatherData = {
+ temperature: weatherSettings.value.unit === 'celsius'
+ ? live.temperature
+ : convertCelsiusToFahrenheit(tempInCelsius).toFixed(1),
+ weather: live.weather,
+ city: live.city,
+ humidity: live.humidity,
+ windDirection: live.winddirection,
+ windPower: live.windpower,
+ loading: false,
+ error: null
+ }
+
+ // 更新响应式数据
+ weatherData.value = updatedWeatherData
+
+ // 缓存天气数据和时间戳
+ localStorage.setItem('weatherDataCache', JSON.stringify(updatedWeatherData))
+ localStorage.setItem('weatherCacheTimestamp', Date.now().toString())
+
+ console.log('天气数据获取成功并已缓存:', updatedWeatherData)
+ } else {
+ weatherData.value.error = '无法获取天气数据'
+ console.error('天气数据格式错误:', data)
+
+ // 尝试使用IP定位作为备选方案
+ await useIpLocationFallback()
+ }
+ } catch (error) {
+ weatherData.value.error = '获取天气数据失败'
+ console.error('获取天气数据失败:', error)
+
+ // 尝试使用IP定位作为备选方案
+ await useIpLocationFallback()
+ } finally {
+ weatherData.value.loading = false
+ }
+ }
+
+ // IP定位作为备选方案
+ const useIpLocationFallback = async (): Promise => {
+ console.log('使用IP定位作为备选方案')
+ try {
+ const response = await axios.get('/amap-api/v3/ip', {
+ params: {
+ key: AMAP_KEY,
+ output: 'JSON'
+ }
+ })
+
+ console.log('IP定位响应:', response.data)
+
+ // 检查是否返回了香港,如果是,可能是代理问题
+ if (response.data.status === '1' && response.data.city === '香港') {
+ console.warn('检测到IP定位返回香港,可能是代理问题,使用默认位置')
+ // 使用默认位置(北京)
+ await useDefaultLocation()
+ return
+ }
+
+ if (response.data.status === '1' && response.data.city) {
+ // 获取adcode
+ let adcode = response.data.adcode
+
+ // 如果没有adcode,尝试使用citycode
+ if (!adcode && response.data.citycode) {
+ adcode = response.data.citycode
+ }
+
+ // 如果还是没有,尝试通过城市名称获取adcode
+ if (!adcode && response.data.city) {
+ try {
+ // 使用地理编码API通过城市名称获取adcode
+ const geocodeResponse = await axios.get('/amap-api/v3/geocode/geo', {
+ params: {
+ key: AMAP_KEY,
+ address: response.data.city,
+ output: 'JSON'
+ }
+ })
+
+ console.log('地理编码响应:', geocodeResponse.data)
+
+ if (geocodeResponse.data.status === '1' &&
+ geocodeResponse.data.geocodes &&
+ geocodeResponse.data.geocodes.length > 0) {
+ adcode = geocodeResponse.data.geocodes[0].adcode
+ }
+ } catch (geocodeError) {
+ console.error('地理编码请求失败:', geocodeError)
+ }
+ }
+
+ // 如果还是没有adcode,使用默认值
+ if (!adcode) {
+ adcode = '110101' // 北京市东城区
+ }
+
+ locationData.value = {
+ latitude: 0,
+ longitude: 0,
+ city: response.data.city,
+ adcode: adcode
+ }
+
+ console.log('IP定位成功,使用adcode:', adcode)
+
+ // 使用IP定位获取天气数据
+ const weatherResponse = await axios.get('/amap-api/v3/weather/weatherInfo', {
+ params: {
+ key: AMAP_KEY,
+ city: adcode, // 使用城市代码获取天气
+ extensions: 'base',
+ output: 'JSON'
+ }
+ })
+
+ if (weatherResponse.data.status === '1' && weatherResponse.data.lives && weatherResponse.data.lives.length > 0) {
+ const live = weatherResponse.data.lives[0]
+ // 重新获取最新的天气设置
+ weatherSettings.value = loadWeatherSettings()
+ // 根据设置的温度单位转换温度值
+ const tempInCelsius = parseFloat(live.temperature)
+
+ // 更新天气数据
+ weatherData.value = {
+ temperature: weatherSettings.value.unit === 'celsius'
+ ? live.temperature
+ : convertCelsiusToFahrenheit(tempInCelsius).toFixed(1),
+ weather: live.weather,
+ city: live.city,
+ humidity: live.humidity,
+ windDirection: live.winddirection,
+ windPower: live.windpower,
+ loading: false,
+ error: null
+ }
+
+ // 缓存天气数据和时间戳
+ localStorage.setItem('weatherDataCache', JSON.stringify(weatherData.value))
+ localStorage.setItem('weatherCacheTimestamp', Date.now().toString())
+
+ console.log('IP定位天气数据获取成功:', weatherData.value)
+ } else {
+ console.error('IP定位天气数据获取失败:', weatherResponse.data)
+ await useDefaultLocation()
+ }
+ } else {
+ console.error('IP定位返回无效数据:', response.data)
+ // 使用默认位置
+ await useDefaultLocation()
+ }
+ } catch (error) {
+ console.error('IP定位请求失败:', error)
+ // 使用默认位置
+ await useDefaultLocation()
+ }
+}
+
+ // 使用默认位置(北京)
+ const useDefaultLocation = async (): Promise => {
+ console.log('使用默认位置(北京)')
+ locationData.value = {
+ latitude: 39.9042,
+ longitude: 116.4074,
+ city: '北京市',
+ adcode: '110100' // 北京市
+ }
+
+ try {
+ // 使用默认位置获取天气数据
+ const response = await axios.get('/amap-api/v3/weather/weatherInfo', {
+ params: {
+ key: AMAP_KEY,
+ city: '110100', // 北京市
+ extensions: 'base',
+ output: 'JSON'
+ }
+ })
+
+ if (response.data.status === '1' && response.data.lives && response.data.lives.length > 0) {
+ const live = response.data.lives[0]
+ // 重新获取最新的天气设置
+ weatherSettings.value = loadWeatherSettings()
+ // 根据设置的温度单位转换温度值
+ const tempInCelsius = parseFloat(live.temperature)
+
+ // 更新天气数据
+ weatherData.value = {
+ temperature: weatherSettings.value.unit === 'celsius'
+ ? live.temperature
+ : convertCelsiusToFahrenheit(tempInCelsius).toFixed(1),
+ weather: live.weather,
+ city: live.city,
+ humidity: live.humidity,
+ windDirection: live.winddirection,
+ windPower: live.windpower,
+ loading: false,
+ error: null
+ }
+
+ // 缓存天气数据和时间戳
+ localStorage.setItem('weatherDataCache', JSON.stringify(weatherData.value))
+ localStorage.setItem('weatherCacheTimestamp', Date.now().toString())
+
+ console.log('默认位置天气数据获取成功:', weatherData.value)
+ } else {
+ console.error('默认位置天气数据获取失败:', response.data)
+ weatherData.value.error = '无法获取天气数据'
+ }
+ } catch (error) {
+ console.error('默认位置天气数据获取失败:', error)
+ weatherData.value.error = '获取天气数据失败'
+ } finally {
+ weatherData.value.loading = false
+ }
+ }
+
+ // 预加载天气数据
+ const preloadWeatherData = (): void => {
+ // 尝试从缓存加载天气数据,提供即时显示
+ if (checkCachedWeather()) {
+ console.log('组件挂载时已从缓存加载天气数据')
+ } else {
+ console.log('没有可用的天气数据缓存,将通过位置权限检查流程获取')
+ }
+ }
+
+ // 检查位置授权状态
+ const checkLocationPermission = async (): Promise => {
+ console.log('检查位置授权状态')
+ const authorized = Cookies.get('locationAuthorized')
+
+ if (authorized === 'true') {
+ console.log('用户已授权位置')
+ // 用户已授权,尝试从Cookie获取位置数据
+ locationAuthorized.value = true
+ const savedLocationData = Cookies.get('locationData')
+
+ if (savedLocationData) {
+ try {
+ console.log('从Cookie获取位置数据')
+ const parsedData = JSON.parse(savedLocationData)
+
+ // 检查Cookie中的位置数据是否为香港,如果是,可能是之前的代理问题
+ if (parsedData.city === '香港') {
+ console.warn('检测到Cookie中的位置是香港,可能是之前的代理问题,尝试重新获取位置')
+ throw new Error('位置数据可能不准确')
+ }
+
+ locationData.value = parsedData
+ console.log('使用Cookie中的位置数据:', locationData.value)
+ await fetchWeather()
+ } catch (error) {
+ console.error('解析位置数据失败或位置数据不准确:', error)
+ // 尝试重新获取位置
+ try {
+ console.log('尝试重新获取位置')
+ const position = await getUserLocation()
+ const cityInfo = await getCityByLocation(position.coords.latitude, position.coords.longitude)
+
+ // 检查获取到的位置是否为香港,如果是,可能是代理问题
+ if (cityInfo.city === '香港') {
+ console.warn('检测到获取的位置是香港,可能是代理问题,使用默认位置')
+ await useDefaultLocation()
+ return
+ }
+
+ locationData.value = {
+ latitude: position.coords.latitude,
+ longitude: position.coords.longitude,
+ city: cityInfo.city,
+ adcode: cityInfo.adcode
+ }
+ console.log('重新获取位置成功:', locationData.value)
+ Cookies.set('locationData', JSON.stringify(locationData.value), { expires: 30 })
+ await fetchWeather()
+ } catch (error) {
+ console.error('重新获取位置失败:', error)
+ // 尝试使用IP定位
+ await useIpLocationFallback()
+ }
+ }
+ } else {
+ // Cookie中没有位置数据,尝试重新获取
+ try {
+ console.log('Cookie中没有位置数据,尝试获取位置')
+ const position = await getUserLocation()
+ const cityInfo = await getCityByLocation(position.coords.latitude, position.coords.longitude)
+
+ // 检查获取到的位置是否为香港,如果是,可能是代理问题
+ if (cityInfo.city === '香港') {
+ console.warn('检测到获取的位置是香港,可能是代理问题,使用默认位置')
+ await useDefaultLocation()
+ return
+ }
+
+ locationData.value = {
+ latitude: position.coords.latitude,
+ longitude: position.coords.longitude,
+ city: cityInfo.city,
+ adcode: cityInfo.adcode
+ }
+ console.log('获取位置成功:', locationData.value)
+ Cookies.set('locationData', JSON.stringify(locationData.value), { expires: 30 })
+ await fetchWeather()
+ } catch (error) {
+ console.error('获取位置失败:', error)
+ // 尝试使用IP定位
+ await useIpLocationFallback()
+ }
+ }
+ } else if (authorized === 'false') {
+ // 用户已拒绝,使用IP定位
+ console.log('用户已拒绝位置授权,使用IP定位')
+ locationAuthorized.value = false
+ await useIpLocationFallback()
+ } else {
+ // 首次访问,显示授权对话框
+ console.log('首次访问,显示授权对话框')
+ showLocationDialog.value = true
+
+ // 同时尝试使用IP定位作为备选
+ await useIpLocationFallback()
+ }
+ }
+
+ // 初始化
+ const initWeather = () => {
+ // 确保使用最新的天气设置 - 强制从cookie重新读取
+ weatherSettings.value = loadWeatherSettings()
+
+ // 首先尝试预加载天气数据,避免空白加载状态
+ preloadWeatherData()
+
+ // 然后异步检查位置授权并获取最新天气
+ setTimeout(() => {
+ checkLocationPermission()
+ }, 100)
+
+ // 设置定时器,定期检查cookie变化
+ cookieCheckInterval = window.setInterval(() => {
+ checkCookieChanges()
+ }, 10000)
+ }
+
+ // 组件挂载时初始化
+ onMounted(() => {
+ initWeather()
+ })
+
+ // 组件卸载时清除定时器
+ onUnmounted(() => {
+ if (cookieCheckInterval !== null) {
+ clearInterval(cookieCheckInterval)
+ cookieCheckInterval = null
+ }
+ })
+
+ // 重置位置信息
+ const resetLocation = async (): Promise => {
+ console.log('重置位置信息')
+ // 清除位置相关的cookie
+ Cookies.remove('locationAuthorized')
+ Cookies.remove('locationData')
+ locationAuthorized.value = false
+
+ // 重新请求位置权限
+ await requestLocationPermission()
+
+ // 重新获取天气数据
+ await fetchWeather()
+ }
+
+ return {
+ weatherData,
+ weatherSettings,
+ locationData,
+ showLocationDialog,
+ locationAuthorized,
+ locationLoading,
+ locationError,
+ showCitySelector,
+ selectedCity,
+ commonCities,
+ fetchWeather,
+ requestLocationPermission,
+ denyLocationPermission,
+ selectCity,
+ confirmCitySelection,
+ resetLocation,
+ getWeatherIcon,
+ loadWeatherSettings,
+ updateWeatherSettings
+ }
+}
diff --git a/src/client/views/About-d.vue b/src/client/views/About-d.vue
new file mode 100644
index 0000000..fbac634
--- /dev/null
+++ b/src/client/views/About-d.vue
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
关于 Space.Tab
+
+ 了解 Space.Tab 的出现与技术栈
+
+
+
+
+
+
+
+
+
+ 我们的使命
+
+
+
+
+ Space.Tab 致力于提供简洁、高效的浏览体验,让用户能够更便捷地获取信息。
+
+ 我们相信技术应该为人所用,而不是让人被技术所困扰。
+
+
+
+
+
+
+
+
+ 技术信息
+
+
+
+
+ 版本
+ {{ version }}
+
+
+ 构建日期
+ {{ buildDate }}
+
+
+ 环境
+ {{ environment }}
+
+
+
+
+
+
+
+
+ 核心功能
+
+ 我们提供的核心服务和技术特性
+
+
+
+
+
+
+
+
{{ feature.title }}
+
+
{{ feature.description }}
+
+
+
+
+
+
+
+
+ 技术栈
+
+ 我们使用的最新技术和工具
+
+
+
+
+
+ {{ tech }}
+
+
+
+
+
+
+
+
+ 团队
+
+ 感谢所有为项目做出贡献的团队成员
+
+
+
+
+
+
+
+
{{ member.name }}
+
{{ member.role }}
+
+
+
+
+
+
+
+
+
+ 联系我们
+
+ 有任何问题或建议,欢迎联系我们
+
+
+
+
+
+
+
+
邮箱
+
dream_pep@mail.lingke.ink
+
+
+
+
+
+
+
+
+
+
© 2025 Lingke Network. All rights reserved.
+
Made with for the community
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/AboutUs.vue b/src/client/views/AboutUs.vue
new file mode 100644
index 0000000..801aa69
--- /dev/null
+++ b/src/client/views/AboutUs.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
关于我们
+
+ 了解 Space.Tab 背后的团队
+
+
+
+
+
开发与设计
+
Space.Tab 由 dream_pep 开发与设计
+
感谢 Lingke Network 提供云服务计算支持
+
+
+
+
+ 赞助我们
+
+
+
+
+
+ 加入用户群
+
+
+
+ 由于云计算与天气API还有域名都是运营成本,如果您认为我们做的还不错,可以赞助一下我们哦~
+
+
+
+
+
+
我们的使命
+
+ Space.Tab 致力于提供简洁、高效的浏览体验,让用户能够更便捷地获取信息。
+
+ 我们相信技术应该为人所用,而不是让人被技术所困扰。
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/AboutVersion.vue b/src/client/views/AboutVersion.vue
new file mode 100644
index 0000000..5082aad
--- /dev/null
+++ b/src/client/views/AboutVersion.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
{{ pageTitle }}
+
+
+
+
当前版本
+
+
+
+ V{{ version }}
+
+
当前版本
+
+
+ Space.Tab 是一个现代化的启航页,致力于提供简洁、高效的用户体验。
+
+
+
+
+
+
版本历史
+
+
+
+
+
+
V{{ item.version }}
+
+
{{ item.date }}
+
+
+
+
+
+
+
+
+ © {{ currentYear }} Lingke Network. 保留所有权利
+
+ 本站源码暂不开源,后续会发布社区版本供各位魔改
+
+ 由 dream_pep 设计与开发
+
+
+
+
+
diff --git a/src/client/views/AuditManagement.vue b/src/client/views/AuditManagement.vue
new file mode 100644
index 0000000..1d54be9
--- /dev/null
+++ b/src/client/views/AuditManagement.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
审核管理
+
+
+
+
+ 审核管理
+ 管理系统审核流程
+
+
+ 审核管理功能正在开发中...
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/BannedPage.vue b/src/client/views/BannedPage.vue
new file mode 100644
index 0000000..57d16b1
--- /dev/null
+++ b/src/client/views/BannedPage.vue
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
停用原因
+
{{ banReason }}
+
+
+
+
+
+
+
倒计时
+
+
{{ timeLeft }}
+
+
+
+
+
预计解封时间:{{ new Date(banExpiration).toLocaleString('zh-CN') }}
+
+
+
+
+
+ 返回首页
+
+
+
+
+
+ 如有疑问,请联系 Lingke Network Space.Tab Team .
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/BilibiliHot.vue b/src/client/views/BilibiliHot.vue
new file mode 100644
index 0000000..c3f8572
--- /dev/null
+++ b/src/client/views/BilibiliHot.vue
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
+
+ 返回到 HotDay
+
+
+
哔哩哔哩热门榜
+
{{ hotData?.description || '你所热爱的,就是你的生活' }}
+
+
+
+
+
+
+
+
+
+
+
+
获取数据失败
+
{{ error }}
+
重试
+
+
+
+
+
+
+
热门视频
+
+ 更新于 {{ formatUpdateTime(hotData.updateTime) }}
+
+
+
+ 前往B站
+
+
+
+
+
+
+
+
+
+ No.{{ index + 1 }}
+
+
+
+ {{ formatHot(item.hot) }}
+
+
+
+ {{ item.title }}
+
+
+
+ {{ item.author }}
+
+
+
+ {{ formatTimestamp(item.timestamp) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
暂无数据
+
请点击下方按钮获取最新热门视频
+
获取热门视频
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/Dashboard.vue b/src/client/views/Dashboard.vue
new file mode 100644
index 0000000..0f5c064
--- /dev/null
+++ b/src/client/views/Dashboard.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+ {{ greeting }},{{ username }}
+
+
+ 管理员
+
+
+
+ 现在是{{ formattedTime }},今天还有什么事没有完成呢?
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/DashboardLayout.vue b/src/client/views/DashboardLayout.vue
new file mode 100644
index 0000000..238b68c
--- /dev/null
+++ b/src/client/views/DashboardLayout.vue
@@ -0,0 +1,81 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/GithubHot.vue b/src/client/views/GithubHot.vue
new file mode 100644
index 0000000..0c89411
--- /dev/null
+++ b/src/client/views/GithubHot.vue
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+ 返回到 HotDay
+
+
+
GitHub 热门榜
+
{{ hotData?.description || '分享 GitHub 上有趣、入门级的开源项目' }}
+
+
+
+
+
+
+
+
+
+
+
+
获取数据失败
+
{{ error }}
+
重试
+
+
+
+
+
+
+
热门项目
+
+ 更新于 {{ formatUpdateTime(hotData.updateTime) }}
+
+
+
+ 前往 GitHub
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ item.title }}
+
+
No.{{ index + 1 }}
+
+
+
+ {{ item.desc }}
+
+
+
+ {{ item.author }}
+
+
+
+
+ {{ formatTimestamp(item.timestamp) }}
+
+
+
+ {{ formatHot(item.hot) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
暂无数据
+
请点击下方按钮获取最新热门项目
+
获取热门项目
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/Home.vue b/src/client/views/Home.vue
new file mode 100644
index 0000000..d6c8ced
--- /dev/null
+++ b/src/client/views/Home.vue
@@ -0,0 +1,352 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 你好,{{ username }}
+
+ PRO
+
+
+
你有什么想搜的吗?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 欢迎使用 Space.Tab
+
+ 看起来您还没有完成初始化设置。初始化设置可以帮助您个性化您的浏览体验,包括主题、壁纸和天气设置等。
+
+
+
+
+
+ 您可以现在完成初始化设置,也可以稍后在设置中进行配置。
+
+
+
+
+ 立即设置
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/Hot.vue b/src/client/views/Hot.vue
new file mode 100644
index 0000000..83b78cb
--- /dev/null
+++ b/src/client/views/Hot.vue
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+
+
哔哩哔哩热门榜
+
你所热爱的,就是你的生活
+
+
+
+
+
实时更新B站最热门的视频内容,包括全站、动画、音乐、游戏等多个分区的热门视频。
+
+
+
+ 实时更新
+
+
+
+
+
+
+
+
+
GitHub 热门榜
+
分享 GitHub 上有趣、入门级的开源项目
+
+
+
+
+
展示 GitHub 上最热门的开源项目,包括精选和全部两种分类,帮助你发现优质开源资源。
+
+
+
+ 实时更新
+
+
+
+
+
+
+
+
更多热门榜单正在开发中,敬请期待...
+
热门信息获取自 Real-Hot Lingke™
+
注意:本服务仍在测试,可能会有BUG与错误,遇到请及时反馈
+
+
+ 返回首页
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/PermissionManagement.vue b/src/client/views/PermissionManagement.vue
new file mode 100644
index 0000000..1009ed2
--- /dev/null
+++ b/src/client/views/PermissionManagement.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
权限管理
+
+
+
+
+ 权限管理
+ 管理系统权限配置
+
+
+ 权限管理功能正在开发中...
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/PrivacyPolicy.vue b/src/client/views/PrivacyPolicy.vue
new file mode 100644
index 0000000..ac950c2
--- /dev/null
+++ b/src/client/views/PrivacyPolicy.vue
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
Lingke Network 隐私策略
+
+ 生效日期:2025年5月1日
+
+
+
+
+
+ 欢迎使用 Lingke Network(以下简称 "我们")的产品和服务。我们深知个人信息对您的重要性,致力于保护您的隐私和个人信息安全。本隐私策略旨在清晰说明我们在您使用我们的产品和服务过程中,如何收集、使用、存储、共享、转让、公开披露您的个人信息,以及您对自身个人信息所享有的权利和相应保护措施。
+
+
+
+ 本隐私策略适用于我们提供的所有产品和服务,无论您通过计算机、移动设备还是其他方式使用。请您在使用我们的产品和服务前,仔细阅读并充分理解本隐私策略。若您不同意本隐私策略的任何内容,请勿使用我们的产品和服务。您使用我们的产品和服务,即表示您同意我们按照本隐私策略处理您的个人信息。
+
+
+
一、我们收集的信息
+
+
(一)您主动提供的信息
+
+ 注册账号时,您需向我们提供手机号码、电子邮箱地址等信息,并设置密码。这些信息用于创建您的 Lingke Network 账号,保障您能正常登录和使用我们的服务。
+ 使用部分特定服务时,您可能需要提供姓名、地址、付款信息等。例如,购买付费服务或接收实体商品时,这些信息是完成相关服务的必要条件。
+ 您通过我们的产品和服务上传、发布、分享的文字、图片、音频、视频等内容,我们会予以收集。
+
+
+
(二)我们自动收集的信息
+
+
+ 设备信息 :当您使用我们的产品和服务时,我们会自动收集您所用设备的型号、操作系统版本、设备识别码(如 IMEI、MAC 地址、UUID 等)、硬件序列号、制造商、名称、屏幕分辨率、电池电量、网络类型等信息。这些信息有助于我们为您提供适配设备的服务,优化用户体验,保障服务安全稳定运行。
+
+
+ 位置信息 :若您开启设备位置服务并使用我们基于位置的服务(如附近资源推荐、定位功能等),我们会收集您的地理位置信息。您可随时在设备设置中关闭位置服务,停止我们对您位置信息的收集。对于外国用户,我们将严格遵守当地关于位置信息收集和使用的法律法规。
+
+
+ 日志信息 :您使用我们的产品和服务时,服务器会自动记录访问时间、访问页面、IP 地址、浏览器类型及版本、操作系统、Referrer URL、请求内容、处理结果等日志信息。这些信息用于分析服务使用情况,改进优化产品和服务,同时排查故障,保障服务稳定性和安全性。
+
+
+ 使用情况信息 :我们会收集您使用产品和服务的频率、功能使用、停留时间、按钮点击等情况。这些信息有助于我们了解您的使用习惯,为您提供更个性化的服务和推荐。
+
+
+
+
(三)从第三方获取的信息
+
+ 在法律法规允许范围内,我们可能从第三方获取您的个人信息。例如,您通过第三方账号(如社交媒体账号)登录我们的服务时,我们会根据您的授权从该第三方获取用户名、头像等相关信息。我们会将这些信息与其他收集的信息结合使用,以更好地为您提供服务。
+
+
+
二、我们如何使用您的信息
+
我们收集您的个人信息主要用于以下目的:
+
+ 为您提供产品和服务,满足您的使用需求。如使用您的账号信息让您登录服务,利用位置信息提供基于位置的服务等。
+ 改进产品和服务。通过分析您的使用情况、日志等信息,了解您的需求和意见,不断改进产品功能,优化服务体验。
+ 保障服务安全稳定。借助收集的设备信息、日志信息等,识别和防范安全风险,保护您的账号和个人信息安全,确保服务正常运行。
+ 提供个性化推荐和广告。根据您的使用习惯、兴趣爱好等信息,为您推荐可能感兴趣的内容、产品和服务,展示更符合您需求的广告。对于外国用户,我们会遵守当地关于广告推送的相关规定。
+ 与您沟通。我们可能通过您的联系方式(如手机号码、电子邮箱地址)向您发送产品服务更新、通知、优惠信息等。若您不希望接收,可按我们提供的方式取消订阅。
+ 遵守法律法规要求。在法律法规规定的情况下,我们可能需要使用您的个人信息履行法律义务,如配合司法机关调查、应对监管部门检查等。
+
+
+
三、信息的共享、转让和公开披露
+
+
(一)共享
+
+ 未经您明确同意,我们不会将您的个人信息共享给第三方。
+ 为向您提供更优质的产品和服务,我们可能与关联公司共享您的个人信息。关联公司将严格遵守本隐私策略,保护您的个人信息安全。
+ 某些情况下,我们可能与为我们提供技术支持、数据存储、支付处理、客户服务等服务的第三方服务提供商共享您的个人信息。我们会与这些提供商签订严格保密协议,要求其按我们的指示处理您的个人信息,并采取相应安全保护措施。
+ 对于外国用户的个人信息,共享时我们会严格遵守相关国家和地区关于个人信息跨境传输的法律法规,确保信息共享合法安全。
+
+
+
(二)转让
+
我们不会将您的个人信息转让给任何公司、组织或个人,除非:
+
+ 获得您的明确同意;
+ 涉及合并、收购、资产转让或类似交易时,您的个人信息可能作为交易一部分被转让。此时,我们会要求受让方继续遵守本隐私策略,保护您的个人信息安全。若受让方变更个人信息处理目的或方式,需重新获得您的同意。
+
+
+
(三)公开披露
+
我们不会公开披露您的个人信息,除非:
+
+ 获得您的明确同意;
+ 根据法律法规要求,或为维护公共利益、保护我们或他人合法权益,我们可能公开披露您的个人信息。公开披露前,除法律法规另有规定或情况紧急外,我们会尽合理努力通知您。
+
+
+
四、信息的存储和保护
+
+
(一)存储
+
+ 我们将您的个人信息存储在安全服务器上,并采取适当技术措施防止信息泄露、丢失或篡改。
+ 中国境内用户的个人信息,我们会按法律法规规定存储于中国境内。外国用户的个人信息,我们会根据相关国家和地区法律法规,在当地或其他符合规定的地点存储。
+ 我们会根据服务需要和法律法规要求,确定个人信息存储期限。存储期限届满,我们会按规定删除或匿名化处理您的个人信息。
+
+
+
(二)保护
+
+ 我们采用加密技术、访问控制、防火墙、入侵检测系统等多种安全技术和措施保护您的个人信息安全。并会定期评估和更新这些安全措施,确保其有效性。
+ 我们对员工访问您的个人信息进行严格管理和控制,仅授权员工可访问,且他们需遵守严格保密义务。
+ 尽管我们采取了上述安全措施,但请您理解,任何安全措施都无法完全避免信息泄露风险。若您发现个人信息被泄露,请立即通过本隐私策略提供的联系方式与我们联系,我们会采取相应措施尽力减少您的损失。
+
+
+
五、您的权利
+
您对自己的个人信息享有以下权利:
+
+ 访问权 :您有权访问您的个人信息,包括注册信息、使用记录等。可通过我们提供的账号设置等功能自行查询。
+ 更正权 :若发现个人信息存在错误,您有权要求我们更正。可通过相关功能或联系客服进行更正。
+ 删除权 :在某些情况下(如不再使用我们的服务、我们收集信息违反法律法规等),您有权要求我们删除您的个人信息。可通过相关功能或联系客服申请删除。
+ 撤回同意权 :您可以撤回对我们收集、使用您个人信息的同意。例如,关闭设备位置服务,撤回对我们收集位置信息的同意。请注意,撤回同意可能影响您使用部分服务。
+ 数据可携带权 :在符合法律法规规定的情况下,您有权要求我们将您的个人信息转移给您指定的其他组织或个人。
+ 注销账号权 :您有权注销您的 Lingke Network 账号。账号注销后,除法律法规另有规定外,我们会按规定删除或匿名化处理您的个人信息。
+
+
若您想行使上述权利,请通过本隐私策略提供的联系方式与我们联系。我们会在收到请求后,按法律法规规定处理,并在合理期限内答复您。
+
+
六、未成年人的隐私保护
+
+ 我们高度重视未成年人的隐私保护。若您是未满 18 周岁的未成年人,使用我们的产品和服务前,应事先获得父母或监护人同意。我们不会向未成年人收集不必要的个人信息,并会采取额外措施保护其个人信息安全。
+
+
+ 若我们发现未获得父母或监护人同意而收集了未成年人的个人信息,会尽快删除相关信息。
+
+
+
七、隐私策略的更新
+
+ 我们可能根据法律法规变化、产品服务更新等情况修订本隐私策略。当隐私策略变更时,我们会在产品和服务中发布更新后的隐私策略,并通过登录页面弹窗提示、发送邮件等适当方式通知您。
+
+
+ 请您定期查阅隐私策略更新内容。您继续使用我们的产品和服务,即表示您同意我们按更新后的隐私策略处理您的个人信息。
+
+
+
八、如何联系我们
+
+ 若您对本隐私策略有任何疑问、意见或建议,或想行使个人信息相关权利,请通过以下方式联系我们:
+
+
+ 电子邮箱:privacy-policy@mail.lingke.ink
+
+
+ 我们会在收到您的请求后尽快处理,并在法律规定期限内答复您。
+
+
+
九、适用法律
+
+ 本隐私策略的订立、执行、解释及争议解决均适用中华人民共和国法律。对于外国用户,若其所在国家或地区有更严格的隐私保护法律规定,则优先适用该国家或地区的法律。
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/views/QualityContent.vue b/src/client/views/QualityContent.vue
new file mode 100644
index 0000000..f54d343
--- /dev/null
+++ b/src/client/views/QualityContent.vue
@@ -0,0 +1,858 @@
+
+
+
+
+
Today At Space
+
发现高质量的网站和工具,提升您的网络体验
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
精选推荐
+
+ 查看全部
+
+
+
+
+
+
+
+
+
+
+
+
+ 境外
+
+
+
+
{{ site.title }}
+
{{ site.category }}
+
+
+
+ {{ site.description }}
+
+
+
+
+
+
+ {{ tag }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ getCategoryTitle(selectedCategory, searchQuery) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ site.title }}
+
{{ site.category }}
+
+
+
+ {{ site.description }}
+
+
+
+
+
+
+ {{ tag }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ site.title.charAt(0) }}
+
+
+
+
+
+
+
+
{{ site.title }}
+
+
+
+
+
{{ site.description }}
+
+
{{ site.category }}
+
+
+ {{ tag }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
没有找到匹配的内容
+
尝试使用不同的关键词或筛选条件
+
重置筛选条件
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/Settings.vue b/src/client/views/Settings.vue
new file mode 100644
index 0000000..a01d6b8
--- /dev/null
+++ b/src/client/views/Settings.vue
@@ -0,0 +1,1185 @@
+
+
+
+
+
+
+
+
+
+
设置
+
+ 管理您的账户信息、偏好设置和应用功能
+
+
+
+
+
+
+
+
+
+
+
需要登录
+
请登录后查看和修改您的个人信息
+
前往登录
+
+
+
+
+
+
+
+
+
+
+ 错误
+ {{ profileError }}
+
+
+
+
+
+ {{ userInitials }}
+
+
+
+ {{ userStore.userInfo?.username || '用户' }}
+
+ PRO · 高级账户
+
+
+
{{ userInfo.email }}
+
+
+
+
+
+
+
+ 用户名
+
+
+
+ 邮箱
+
+
+
+
QQ号
+
+
+ 填写QQ号后,头像将自动使用QQ头像
+
+
+
+
+
+
+ 编辑信息
+
+
+ 修改密码
+
+
+
+ 取消
+
+
+
+ {{ isUpdating ? '保存中...' : '保存' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ toggleWeatherEnabled(true);
+ }"
+ >
+ 启用
+
+ {
+ toggleWeatherEnabled(false);
+ }"
+ >
+ 禁用
+
+
+
+
+
+
+
+
+
+
+
+ {
+ weatherSettings.unit = 'celsius';
+ const settings = {
+ enabled: weatherSettings.enabled,
+ unit: weatherSettings.unit || 'celsius',
+ showHumidity: weatherSettings.showHumidity !== false,
+ showWind: weatherSettings.showWind !== false
+ };
+ saveWeatherSettings(settings);
+ toast.success('已切换为摄氏度 (℃)');
+ }"
+ >
+ 摄氏度 (℃)
+
+ {
+ weatherSettings.unit = 'fahrenheit';
+ const settings = {
+ enabled: weatherSettings.enabled,
+ unit: 'fahrenheit',
+ showHumidity: weatherSettings.showHumidity !== false,
+ showWind: weatherSettings.showWind !== false
+ };
+ saveWeatherSettings(settings);
+ toast.success('已切换为华氏度 (℉)');
+ }"
+ >
+ 华氏度 (℉)
+
+
+
+
+
+
+
+
+
+
+
+
显示湿度
+
是否在天气信息中显示湿度数据
+
+
+ {
+ weatherSettings.showHumidity = true;
+ const settings = {
+ enabled: weatherSettings.enabled,
+ unit: weatherSettings.unit || 'celsius',
+ showHumidity: true,
+ showWind: weatherSettings.showWind !== false
+ };
+ saveWeatherSettings(settings);
+ toast.success('已启用湿度显示');
+ }"
+ >
+ 显示
+
+ {
+ weatherSettings.showHumidity = false;
+ const settings = {
+ enabled: weatherSettings.enabled,
+ unit: weatherSettings.unit || 'celsius',
+ showHumidity: false,
+ showWind: weatherSettings.showWind !== false
+ };
+ saveWeatherSettings(settings);
+ toast.success('已隐藏湿度显示');
+ }"
+ >
+ 隐藏
+
+
+
+
+
+
+
显示风向和风力
+
是否在天气信息中显示风向和风力数据
+
+
+ {
+ weatherSettings.showWind = true;
+ const settings = {
+ enabled: weatherSettings.enabled,
+ unit: weatherSettings.unit || 'celsius',
+ showHumidity: weatherSettings.showHumidity !== false,
+ showWind: true
+ };
+ saveWeatherSettings(settings);
+ toast.success('已启用风向和风力显示');
+ }"
+ >
+ 显示
+
+ {
+ weatherSettings.showWind = false;
+ const settings = {
+ enabled: weatherSettings.enabled,
+ unit: weatherSettings.unit || 'celsius',
+ showHumidity: weatherSettings.showHumidity !== false,
+ showWind: false
+ };
+ saveWeatherSettings(settings);
+ toast.success('已禁用风向和风力显示');
+ }"
+ >
+ 隐藏
+
+
+
+
+
+
+
+
+
+
位置设置
+
配置天气信息的位置来源
+
+
+
+
+ 重置位置授权
+
+
+
+
+
+ {{ weatherSettings.showCitySelector ? '隐藏城市选择' : '手动选择城市' }}
+
+
+
+
+ {{ city.name }}
+
+
+
+
+ 当前选择的城市: {{ weatherSettings.selectedCity }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ themeMode = 'auto';
+ toast.success('已切换为自动主题', {
+ description: '将根据系统设置自动切换明暗主题'
+ });
+ }"
+ >
+
+ 自动
+
+ {
+ themeMode = 'light';
+ toast.success('已切换为明亮主题');
+ }"
+ >
+
+ 明亮
+
+ {
+ themeMode = 'dark';
+ toast.success('已切换为暗黑主题');
+ }"
+ >
+
+ 暗黑
+
+
+
+
+
+
+
+
+
+
首页徽章显示
+
是否在首页显示PRO徽章图标(您需要PRO账户才会显示)
+
+
+ toggleBadgeDisplay(true)"
+ >
+ 显示
+
+ toggleBadgeDisplay(false)"
+ >
+ 隐藏
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
数据导入导出
+
备份或恢复您的设置数据
+
+
+
+
+
+ {{ isExporting ? '导出中...' : '导出设置' }}
+
+
+
+
+
+ {{ isImporting ? '导入中...' : '导入设置' }}
+
+
+
+
+
+ 应用导入
+
+
+
+
+ 已选择文件: {{ importFile.name }}
+
+
+
+
+
+
+
+
还原设置并退出登录
+
将所有设置恢复为默认值并清除登录信息
+
+
+
+
+ {{ isResetting ? '处理中...' : '还原设置并退出登录' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 确认更新个人信息
+
+ 您确定要更新个人信息吗?此操作将修改您的账户信息。
+
+
+
+ 取消
+ 确认
+
+
+
+
+
+
+
+
+ 修改密码
+
+ 请输入您的当前密码和新密码
+
+
+
+
+
+
+
+
+ 取消
+
+
+ {{ isChangingPassword ? '提交中...' : '提交' }}
+
+
+
+
+
+
+
+
+
+ 确认还原设置并退出登录
+
+ 此操作将把所有设置恢复为默认值,清除所有Cookie并退出登录。此操作无法撤销。
+
+
+
+ 取消
+ 确认操作
+
+
+
+
+
diff --git a/src/client/views/Setup.vue b/src/client/views/Setup.vue
new file mode 100644
index 0000000..ac92ce8
--- /dev/null
+++ b/src/client/views/Setup.vue
@@ -0,0 +1,1144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
欢迎使用 Space.Tab
+
您的个性化浏览体验从这里开始
+
+
+ 开始设置
+
+
+
+
+
+
当前版本 V{{ currentVersion.version }}
+
了解最新版本的更新内容
+
+
+
+
+
+ V{{ currentVersion.version }}
+
+
{{ currentVersion.date }}
+
+
+
+
+
+
+ 上一步
+ 下一步
+
+
+
+
+
+
用户协议
+
请阅读以下协议
+
+
+
+ 隐私策略
+ Lingke Network 隐私策略
+
+
+
+ 使用条款
+ Lingke Network 产品使用条款
+
+
+
+
+ 点击"同意并继续",表示您同意并遵守 Lingke Network 隐私策略和产品使用条款。
+
+
+
+ 上一步
+ 同意并继续
+
+
+
+
+
+
选择您的城市
+
选择您所在的城市以获得更准确的天气信息
+
+
+
+
+ 上一步
+ 下一步
+
+
+
+
+
+
选择您的语言
+
其实你不论怎么设置都只有中文( ͡• ͜ʖ ͡• )
+
+
+
+
+ {{ lang.name }}
+ ✓
+
+
+
+
+
+ 上一步
+ 下一步
+
+
+
+
+
+
选择您喜欢的主题
+
选择一个适合您的显示模式
+
+
+
+
+
+
+
自动
+
根据系统设置自动切换
+
+
+
+
+
+
+
+
+ 上一步
+ 下一步
+
+
+
+
+
+
选择您的壁纸
+
个性化您的起始页背景
+
+
+
+
完成初始化后可以前往设置自定义壁纸
+
+
+ 上一步
+ 下一步
+
+
+
+
+
+
+
加入用户群
+
获取最新消息与提出意见建议
+
+
+
+
Space.Tab 用户交流群
+
点击下方按钮加入
+
+
+
+
+ 立即加入
+ 暂不加入
+
+
+
+
+
+
+
设置完成
+
您的 Space.Tab 已准备就绪
+
+
+ 开始使用
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/SiteDetail.vue b/src/client/views/SiteDetail.vue
new file mode 100644
index 0000000..59d0418
--- /dev/null
+++ b/src/client/views/SiteDetail.vue
@@ -0,0 +1,754 @@
+
+
+
+
+
+
+
+
+ 返回到 Today
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ site.detailedDescription }}
+
+
+
+
+
+
+
+
+
+
+
+
来到了奇怪的地方呢...
+
抱歉,您请求的网站详情不存在。
+
+ 返回 Today
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/TermsOfService.vue b/src/client/views/TermsOfService.vue
new file mode 100644
index 0000000..c944683
--- /dev/null
+++ b/src/client/views/TermsOfService.vue
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+
+
+
Lingke Network 产品使用条款
+
+ 最后更新日期:2025年5月1日
+
+
+
+
+
一、总则
+
+
+ 适用范围 :本条款适用于所有使用 Lingke Network(以下简称 "本网络")提供的产品及相关服务(以下统称 "本产品")的用户(以下简称 "用户")。用户在使用本产品前,应仔细阅读并充分理解本条款的全部内容。用户点击 "同意"、注册账户、使用本产品或通过其他方式表明接受本条款的,即视为用户已同意本条款的约束。
+
+
+ 定义 :
+
+ "本网络":指提供本产品及相关服务的 Lingke Network 运营主体。
+ "本产品":指本网络开发、运营并提供的各类软件、应用程序、平台服务及相关功能等。
+ "用户":指注册、登录、使用本产品的自然人、法人或其他组织。
+
+
+
+
+
二、账户管理
+
+
(一)账户注册
+
+ 用户注册本产品账户时,应提供真实、准确、完整且有效的信息,包括但不限于姓名、联系方式、电子邮箱、身份证号码(或其他有效身份证明文件)等。用户保证所提供的信息真实无误,如有变更,应及时更新。
+ 用户应确保注册信息符合法律法规及本条款的要求,不得使用违法、违规、侵权或含有不良信息的内容作为账户名、昵称等。本网络有权对用户提交的注册信息进行审核,对于不符合要求的信息,有权拒绝用户的注册申请。
+ 每个用户仅能注册一个本产品账户,禁止重复注册。如发现用户存在重复注册行为,本网络有权对相关账户进行处理,包括但不限于冻结、注销等。
+
+
+
(二)账户所有权与使用权
+
+ 本产品账户的所有权归本网络所有。本网络依据本条款及相关服务协议,向用户授予账户的有限使用权,用户仅享有在本条款规定范围内使用账户的权利,不得对账户主张任何形式的所有权或其他权利。
+ 用户应妥善保管自己的账户信息,包括但不限于用户名、密码、验证码等,对账户的所有操作行为承担全部责任。因用户自身原因导致账户信息泄露、被盗用等情况,产生的一切后果由用户自行承担。
+ 未经本网络书面许可,用户不得将账户以任何形式出借、转让、赠与、售卖或以其他方式提供给第三方使用。如违反本规定,本网络有权采取冻结账户、暂停服务、注销账户等措施,并追究用户的相关责任。
+
+
+
(三)账户安全与保护
+
+ 用户应采取合理的安全措施保护账户安全,如设置复杂密码、定期更换密码、不向他人透露账户信息等。如发现账户存在异常情况,如非本人操作的登录、交易等,应立即通知本网络,并配合本网络进行调查处理。
+ 如用户的账户丢失、被盗用或存在其他安全风险,用户应及时通过本网络指定的客服渠道进行上报。上报时需提供相关证明材料,以便本网络及时采取冻结账户、找回账户等措施。用户未及时上报导致的损失,由用户自行承担。
+
+
+
三、产品使用规范
+
+
(一)合法合规使用
+
+ 用户在使用本产品过程中,必须严格遵守《中华人民共和国宪法》及所有派生的法律、法规、规章、规范性文件等相关规定,不得利用本产品从事任何违法违规活动。
+
+ 用户不得利用本产品制作、复制、发布、传播含有下列内容的信息:
+
+ 危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;
+ 损害国家荣誉和利益的;
+ 煽动民族仇恨、民族歧视,破坏民族团结的;
+ 破坏国家宗教政策,宣扬邪教和封建迷信的;
+ 散布谣言,扰乱社会秩序,破坏社会稳定的;
+ 散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;
+ 侮辱或者诽谤他人,侵害他人合法权益的;
+ 含有法律、行政法规禁止的其他内容的。
+
+
+
+
+
(二)遵守本网络规则
+
+ 用户应遵守本网络发布的与本产品相关的各项规则、政策、公告等内容,包括但不限于产品使用说明、功能限制、服务条款等。本网络有权根据实际情况对上述规则进行修订和更新,修订后的规则将通过本网络官方渠道公布,自公布之日起生效。用户应定期查阅相关规则,如继续使用本产品,视为同意接受修订后的规则约束。
+ 用户不得利用本产品进行任何损害本网络或其他用户合法权益的行为,包括但不限于恶意攻击本产品系统、滥用产品功能、干扰其他用户使用等。
+ 用户在使用本产品过程中,应尊重知识产权,不得侵犯本网络或第三方的著作权、商标权、专利权等知识产权。未经授权,不得复制、传播、修改本产品的任何内容或组件。
+
+
+
四、服务的提供与变更
+
+
(一)服务内容
+
本网络将根据自身情况及用户需求,为用户提供本产品的相关服务,具体服务内容以本网络实际提供的为准。本网络有权对服务内容进行调整和优化,以提升用户体验。
+
+
(二)服务变更与中断
+
+ 因技术升级、系统维护、服务器故障等原因,本网络可能会对本产品的服务进行暂时中断或限制,届时将通过官方渠道提前通知用户(紧急情况除外)。服务中断或限制期间,用户可能无法正常使用本产品,本网络将尽快恢复服务,并对因此给用户造成的合理损失(如有)按照相关规定进行处理,但因不可抗力或用户自身原因导致的除外。
+ 如因法律法规变更、政策调整、监管要求等原因,本网络可能需要对本产品的服务进行变更、暂停或终止,届时将通过官方渠道通知用户,并按照相关规定处理后续事宜。
+
+
+
五、知识产权
+
+ 本产品的所有知识产权,包括但不限于著作权、商标权、专利权、商业秘密等,均归本网络或相关权利人所有。本条款未授予用户任何知识产权方面的权利,用户仅能在本条款规定的范围内使用本产品。
+ 用户在使用本产品过程中创作的内容,其知识产权归用户所有,但用户授予本网络一项全球性、免费、非独家、可转让、可再许可的使用权,包括但不限于对该内容进行复制、传播、展示、修改、汇编等,以用于本产品的运营、推广及相关服务。
+
+
+
六、隐私保护
+
+ 本网络重视用户的隐私保护,将按照本网络公布的隐私政策收集、使用、存储和保护用户的个人信息。隐私政策将详细说明信息收集的范围、目的、使用方式、保护措施等内容,用户应仔细阅读并理解隐私政策。
+ 用户同意本网络按照隐私政策处理其个人信息,如用户不同意隐私政策的内容,应停止使用本产品。
+
+
+
七、免责声明
+
+ 本网络尽力保障本产品的稳定运行和服务质量,但不对本产品的可用性、稳定性、安全性等作出任何明示或暗示的保证。因不可抗力、网络故障、第三方攻击等原因导致本产品无法正常使用或用户遭受损失的,本网络不承担责任,但将尽力协助用户解决问题。
+ 用户因使用本产品或依赖本产品提供的信息而产生的任何直接或间接损失,本网络不承担责任,除非该损失是由于本网络的故意或重大过失造成的。
+ 本网络对第三方通过本产品提供的服务或内容不承担责任,用户与第三方之间的纠纷,应自行解决。
+
+
+
八、违约责任
+
+
+ 如用户违反本条款的规定,本网络有权采取以下一项或多项措施:
+
+ 警告用户,要求其限期改正;
+ 限制或暂停用户使用本产品的部分或全部功能;
+ 冻结或注销用户的账户;
+ 要求用户赔偿因其违约行为给本网络或其他用户造成的损失;
+ 追究用户的法律责任。
+
+
+ 如本网络违反本条款的规定,给用户造成损失的,应承担相应的赔偿责任,但赔偿范围不超过用户因使用本产品而支付的费用(如有)。
+
+
+
九、法律适用与争议解决
+
+ 本条款的订立、效力、解释、履行及争议解决均适用中华人民共和国法律(为本条款之目的,不包括香港特别行政区、澳门特别行政区和台湾地区的法律)。
+ 因本条款引起的或与本条款有关的任何争议,双方应首先通过友好协商解决;协商不成的,任何一方均有权向本网络所在地有管辖权的人民法院提起诉讼。
+
+
+
十、其他
+
+ 本条款构成用户与本网络之间关于使用本产品的完整协议,取代双方此前就此达成的任何口头或书面协议、谅解和承诺。
+ 如本条款的任何条款被认定为无效或不可执行,不影响其他条款的效力。
+ 本网络未行使或延迟行使其在本条款项下的任何权利,不应视为对该权利的放弃。
+ 本条款的标题仅为方便阅读而设,不影响本条款任何条款的解释。
+ 本网络有权根据实际情况对本条款进行修订,修订后的条款将通过官方渠道公布,自公布之日起生效。用户应定期查阅本条款,如继续使用本产品,视为同意接受修订后的条款约束。
+ 本条款的最终解释权归 Lingke Network 所有。
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/views/UserManagement.vue b/src/client/views/UserManagement.vue
new file mode 100644
index 0000000..e79c8d5
--- /dev/null
+++ b/src/client/views/UserManagement.vue
@@ -0,0 +1,1037 @@
+
+
+
+
+
+
+
+
用户管理
+
+
+
+
+
+ 总用户数
+
+
+
+ {{ totalUsers }}
+
+
+
+
+ 管理员
+
+
+
+ {{ users.filter(u => u.is_admin).length }}
+
+
+
+
+ 已封禁
+
+
+
+ {{ users.filter(u => u.is_banned).length }}
+
+
+
+
+ 活跃用户
+
+
+
+ {{ users.filter(u => !u.is_banned).length }}
+
+
+
+
+
+
+
+
+ 用户列表
+ 管理系统中的所有用户账户
+
+
+
+ 创建用户
+
+
+
+ 刷新列表
+
+
+
+
+
+
+
+
+
+ 用户
+ 邮箱
+ 状态
+ 注册时间
+ 最后登录
+ 操作
+
+
+
+
+
+
+
+
+
+
+ 暂无用户数据
+
+
+
+
+
+
+
+ {{ getUserInitials(user) }}
+
+
+
{{ user.username }}
+
ID: {{ user.id }}
+
+
+
+ {{ user.email }}
+
+
+ 管理员
+ 已封禁
+ 普通用户
+
+
+ {{ formatDate(user.created_at) }}
+ {{ formatDate(user.last_login || '') }}
+
+
+
+
+ 封禁
+
+
+
+
+ 封禁原因
+
+
+
+ 解封
+
+
+
+ 删除
+
+
+ 编辑
+
+
+
+
+
+
+
+
+
+
+
+ 显示第 {{ (currentPage - 1) * pageSize + 1 }} 到 {{ Math.min(currentPage * pageSize, totalUsers) }} 条,共 {{ totalUsers }} 条
+
+
+
+
+ 上一页
+
+
+
+
+ {{ page }}
+
+
+
+
+ 下一页
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 封禁用户
+
+ 请填写封禁原因和封禁天数
+
+
+
+
+
+
+ 取消
+ 确认封禁
+
+
+
+
+
+
+
+
+ 确认解封用户
+
+ 您确定要解封该用户吗?解封后用户将可以正常使用系统。
+
+
+
+
+
+
+
+
+
+ 用户信息
+
+
+
+
+
+
+ {{ getUserInitials(unbanUserInfo) }}
+
+
+
{{ unbanUserInfo.username }}
+
{{ unbanUserInfo.email }}
+
+
+
+
+
+
+
+
+
+
+ 封禁信息
+
+
+
+
+
封禁原因
+
{{ unbanUserInfo.ban_reason || '未提供封禁原因' }}
+
+
+
+
封禁时间
+
+ {{ unbanUserInfo.ban_expiration ? formatDate(unbanUserInfo.ban_expiration) : '未设置解封时间' }}
+
+
+
+
+
+
+
+ 取消
+
+ 确认解封
+
+
+
+
+
+
+
+
+
+ 封禁详情
+
+ 查看用户的封禁信息和账户详情
+
+
+
+
+
+
+
+
+
+ 用户信息
+
+
+
+
+
+
+ {{ getUserInitials(selectedUserForDetails) }}
+
+
+
{{ selectedUserForDetails.username }}
+
{{ selectedUserForDetails.email }}
+
ID: {{ selectedUserForDetails.id }}
+
+
+ 管理员
+ 普通用户
+
+
+
+
+
+
+
+
+
+
+ 封禁信息
+
+
+
+
+
+
封禁原因
+
+
+ {{ selectedUserForDetails.ban_reason || '未提供封禁原因' }}
+
+
+
+
+
+
+
封禁结束时间
+
+
+ {{ selectedUserForDetails.ban_expiration ? formatDate(selectedUserForDetails.ban_expiration) : '永久封禁' }}
+
+
+
+
+
+
+
剩余封禁时间
+
+
+ {{ getBanRemainingTime(selectedUserForDetails.ban_expiration || '') }}
+
+
+
+
+
+
+
+
+
+
+
+ 账户详情
+
+
+
+
+
+
注册时间
+
+ {{ formatDate(selectedUserForDetails.created_at) }}
+
+
+
+
+
最后登录
+
+ {{ formatDate(selectedUserForDetails.last_login || '') }}
+
+
+
+
+
QQ号码
+
+ {{ selectedUserForDetails.qq_num || '未设置' }}
+
+
+
+
+
+
+
+
+
+
+ {
+ if (selectedUserForDetails) {
+ showBanReasonSheet = false;
+ toggleBanStatus(selectedUserForDetails.id, selectedUserForDetails.is_banned, selectedUserForDetails);
+ }
+ }"
+ class="w-full"
+ >
+
+ 解封用户
+
+
+
+ 关闭
+
+
+
+
+
+
+
+
+
+
+ 编辑用户信息
+ 管理员可编辑部分用户信息
+
+
+
+ 用户名
+
+
+
+ 邮箱
+
+
+
+ QQ号
+
+
+
+ 头像链接
+
+
+
+
+ 邮箱已验证
+
+
+
+ 管理员
+
+
+ 取消
+
+ {{ editLoading ? '保存中...' : '保存' }}
+
+
+
+
+
+
+
+
+
+
+ 创建新用户
+ 请填写新用户信息
+
+
+
+ 用户名
+
+
+
+ 邮箱
+
+
+
+ 密码
+
+
+
+ QQ号
+
+
+
+ 取消
+
+ {{ createLoading ? '创建中...' : '创建' }}
+
+
+
+
+
+
+
+
+
+
+ 确认删除用户
+ 此操作不可撤销,确定要删除该用户吗?
+
+
+
{{ deleteUserInfo.username }}
+
{{ deleteUserInfo.email }}
+
+
+ 取消
+
+ {{ deleteLoading ? '删除中...' : '确认删除' }}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/views/UserProfile.vue b/src/client/views/UserProfile.vue
new file mode 100644
index 0000000..1c2f3f3
--- /dev/null
+++ b/src/client/views/UserProfile.vue
@@ -0,0 +1,575 @@
+
+
+
+
+
+
+
用户设置
+
+ 管理您的账户信息和偏好设置
+
+
+
+
+
+
+
+ 基本信息
+
+ 更新您的个人信息和头像
+
+
+
+
+
+
+
+ 错误
+ {{ profileError }}
+
+
+
+
+ console.log('QQ头像加载失败,使用回退头像')" />
+ {{ userInitials }}
+
+
+
更换头像
+
+ 头像将自动获取QQ头像的图片
+ 填写QQ号后,头像将自动使用QQ头像
+
+
+
+
+
+
+
+
+ 用户名
+
+
+
+ 邮箱
+
+
+
+
QQ号
+
+
+ 填写QQ号后,头像将自动使用QQ头像
+
+
+
+
+
+
+ 编辑信息
+
+
+
+ 取消
+
+
+
+ {{ isUpdating ? '保存中...' : '保存' }}
+
+
+
+
+
+
+
+
+
+ 修改密码
+
+ 定期更新密码以确保账户安全
+
+
+
+
+
+
+ 错误
+ {{ passwordError }}
+
+
+
+ 当前密码
+
+
+
+ 新密码
+
+
+
+ 确认新密码
+
+
+
+
+
+ {{ isChangingPassword ? '修改中...' : '修改密码' }}
+
+
+
+
+
+
+
+
+
+ 确认修改
+
+ 您确定要更新以下信息吗?
+
+
邮箱: {{ userInfo.email }}
+
QQ号: {{ userInfo.qq_num }}
+
+
+
+
+ 取消
+
+ 确认修改
+
+
+
+
+
+
+
+
+
+
+
用户设置
+
+ 管理您的账户信息和偏好设置
+
+
+
+
+
+
+
+ 基本信息
+
+ 更新您的个人信息和头像
+
+
+
+
+
+
+
+ 错误
+ {{ profileError }}
+
+
+
+
+ console.log('QQ头像加载失败,使用回退头像')" />
+ {{ userInitials }}
+
+
+
更换头像
+
+ 头像将自动获取QQ头像的图片
+ 填写QQ号后,头像将自动使用QQ头像
+
+
+
+
+
+
+
+
+ 用户名
+
+
+
+ 邮箱
+
+
+
+
QQ号
+
+
+ 填写QQ号后,头像将自动使用QQ头像
+
+
+
+
+
+
+ 编辑信息
+
+
+
+ 取消
+
+
+
+ {{ isUpdating ? '保存中...' : '保存' }}
+
+
+
+
+
+
+
+
+
+ 修改密码
+
+ 定期更新密码以确保账户安全
+
+
+
+
+
+
+ 错误
+ {{ passwordError }}
+
+
+
+ 当前密码
+
+
+
+ 新密码
+
+
+
+ 确认新密码
+
+
+
+
+
+ {{ isChangingPassword ? '修改中...' : '修改密码' }}
+
+
+
+
+
+
+
+
+
+ 确认修改
+
+ 您确定要更新以下信息吗?
+
+
邮箱: {{ userInfo.email }}
+
QQ号: {{ userInfo.qq_num }}
+
+
+
+
+ 取消
+
+ 确认修改
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/client/vite-env.d.ts b/src/client/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/client/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/src/manifest.json b/src/manifest.json
new file mode 100644
index 0000000..85c78b2
--- /dev/null
+++ b/src/manifest.json
@@ -0,0 +1,33 @@
+{
+ "manifest_version": 3,
+ "name": "Space.Tab",
+ "version": "1.0.0",
+ "description": "一个美观实用的新标签页替代品",
+ "permissions": [
+ "storage",
+ "geolocation"
+ ],
+ "host_permissions": [
+ "https://restapi.amap.com/*"
+ ],
+ "background": {
+ "service_worker": "background.js"
+ },
+ "chrome_url_overrides": {
+ "newtab": "index.html"
+ },
+ "action": {
+ "default_icon": {
+ "16": "icons/icon16.png",
+ "32": "icons/icon32.png",
+ "48": "icons/icon48.png",
+ "128": "icons/icon128.png"
+ }
+ },
+ "icons": {
+ "16": "icons/icon16.png",
+ "32": "icons/icon32.png",
+ "48": "icons/icon48.png",
+ "128": "icons/icon128.png"
+ }
+}
diff --git a/src/server/config/database.ts b/src/server/config/database.ts
new file mode 100644
index 0000000..a1a8f56
--- /dev/null
+++ b/src/server/config/database.ts
@@ -0,0 +1,40 @@
+import mysql, { Pool } from 'mysql2/promise'
+import { Redis } from 'ioredis'
+import ConsoleLog from '../utils/consoleLog'
+import 'dotenv/config'
+
+export const mysqlConnection : Pool = mysql.createPool({
+ host: process.env.MYSQL_HOST,
+ port: Number(process.env.MYSQL_PORT),
+ user: process.env.MYSQL_USER,
+ password: process.env.MYSQL_PASSWORD,
+ database: process.env.MYSQL_DATABASE,
+ waitForConnections: true,
+ connectionLimit: 10,
+ queueLimit: 0
+})
+
+export const redisClient : Redis = new Redis({
+ host: process.env.REDIS_HOST,
+ port: Number(process.env.REDIS_PORT),
+ password: process.env.REDIS_PASSWORD,
+ db: Number(process.env.REDIS_DATABASE || 0)
+})
+
+export async function connectToDatabases() {
+ try {
+ const mysqlConnectionTest = await mysqlConnection.getConnection()
+ ConsoleLog.log('✅ MySQL链接成功')
+ mysqlConnectionTest.release()
+
+ redisClient.on('error', (err: Error) => {
+ ConsoleLog.error(`❌ Redis连接错误:`, err)
+ })
+
+ await redisClient.ping()
+ ConsoleLog.log('✅ Redis链接成功')
+ } catch (error) {
+ ConsoleLog.error(`❌ 数据库连接错误:`, error)
+ throw error
+ }
+}
\ No newline at end of file
diff --git a/src/server/controllers/equipmentController.ts b/src/server/controllers/equipmentController.ts
new file mode 100644
index 0000000..262ac5f
--- /dev/null
+++ b/src/server/controllers/equipmentController.ts
@@ -0,0 +1,160 @@
+import { Request, Response } from 'express';
+import { mysqlConnection } from '../config/database';
+import { v4 as uuidv4 } from 'uuid';
+
+// 简单参数校验函数
+function isNonEmptyString(val: any): boolean {
+ return typeof val === 'string' && val.trim().length > 0;
+}
+function isPositiveInt(val: any): boolean {
+ return Number.isInteger(Number(val)) && Number(val) > 0;
+}
+
+// 注册设备(客户端权限)
+export async function registerEquipment(req: Request, res: Response): Promise {
+ const { bios_id, country, user_id } = req.body;
+ if (!isNonEmptyString(bios_id) || !isNonEmptyString(country) || !isPositiveInt(user_id)) {
+ res.status(400).json({ code: 400, message: 'Missing or invalid required fields' }); return;
+ }
+ const [rows]: any = await mysqlConnection.query(
+ 'SELECT * FROM equipment_info WHERE bios_id = ? AND user_id = ?',
+ [bios_id, user_id]
+ );
+ if (rows.length > 0) {
+ res.status(201).json({ code: 201, message: 'this info_uuid is on the detabest' }); return;
+ }
+ const info_uuid = uuidv4();
+ const date = new Date();
+ await mysqlConnection.query(
+ 'INSERT INTO equipment_info (info_uuid, bios_id, country, user_id, date) VALUES (?, ?, ?, ?, ?)',
+ [info_uuid, bios_id, country, user_id, date]
+ );
+ res.status(200).json({ code: 200, message: 'register is ok', info_uuid });
+}
+
+// 查询设备信息(管理员权限)
+export async function getEquipmentInfo(req: Request, res: Response): Promise {
+ let { page = 1, pageSize = 10, user_id, country } = req.query;
+ page = Number(page);
+ pageSize = Number(pageSize);
+ if (isNaN(page) || page < 1) page = 1;
+ if (isNaN(pageSize) || pageSize < 1 || pageSize > 100) pageSize = 10;
+ let where = 'WHERE 1=1';
+ const params: any[] = [];
+ if (user_id && isPositiveInt(user_id)) {
+ where += ' AND user_id = ?';
+ params.push(user_id);
+ }
+ if (country && isNonEmptyString(country)) {
+ where += ' AND country = ?';
+ params.push(country);
+ }
+ const [countRows]: any = await mysqlConnection.query(
+ `SELECT COUNT(*) as total FROM equipment_info ${where}`, params
+ );
+ const total = countRows[0]?.total || 0;
+ const [rows]: any = await mysqlConnection.query(
+ `SELECT * FROM equipment_info ${where} ORDER BY date DESC LIMIT ? OFFSET ?`,
+ [...params, pageSize, (page - 1) * pageSize]
+ );
+ res.json({ code: 200, data: rows, total, page, pageSize });
+}
+
+// 查询设备配置记录(管理员权限)
+export async function getEquipmentDisposition(req: Request, res: Response): Promise {
+ let { page = 1, pageSize = 10, equipment_uuid } = req.query;
+ page = Number(page);
+ pageSize = Number(pageSize);
+ if (isNaN(page) || page < 1) page = 1;
+ if (isNaN(pageSize) || pageSize < 1 || pageSize > 100) pageSize = 10;
+ let where = 'WHERE 1=1';
+ const params: any[] = [];
+ if (equipment_uuid && isNonEmptyString(equipment_uuid)) {
+ where += ' AND equipment_uuid = ?';
+ params.push(equipment_uuid);
+ }
+ const [countRows]: any = await mysqlConnection.query(
+ `SELECT COUNT(*) as total FROM equipment_disposition ${where}`, params
+ );
+ const total = countRows[0]?.total || 0;
+ const [rows]: any = await mysqlConnection.query(
+ `SELECT * FROM equipment_disposition ${where} ORDER BY date DESC LIMIT ? OFFSET ?`,
+ [...params, pageSize, (page - 1) * pageSize]
+ );
+ res.json({ code: 200, data: rows, total, page, pageSize });
+}
+
+// 添加设备信息(管理员权限)
+export async function addEquipmentInfo(req: Request, res: Response): Promise {
+ const { bios_id, country, user_id } = req.body;
+ if (!isNonEmptyString(bios_id) || !isNonEmptyString(country) || !isPositiveInt(user_id)) {
+ res.status(400).json({ code: 400, message: 'Missing or invalid required fields' }); return;
+ }
+ const info_uuid = uuidv4();
+ const date = new Date();
+ await mysqlConnection.query(
+ 'INSERT INTO equipment_info (info_uuid, bios_id, country, user_id, date) VALUES (?, ?, ?, ?, ?)',
+ [info_uuid, bios_id, country, user_id, date]
+ );
+ res.json({ code: 200, message: 'add ok', info_uuid });
+}
+
+// 添加设备配置记录(管理员权限)
+export async function addEquipmentDisposition(req: Request, res: Response): Promise {
+ const {
+ equipment_uuid, system_architecture, system, system_id,
+ ram_info, date, wonderlab_run_file, wonderlab_v
+ } = req.body;
+ if (!isNonEmptyString(equipment_uuid) || !isNonEmptyString(system_architecture) || !isNonEmptyString(system) || !isNonEmptyString(system_id) || !isNonEmptyString(ram_info) || !isNonEmptyString(date) || !isNonEmptyString(wonderlab_run_file) || !isNonEmptyString(wonderlab_v)) {
+ res.status(400).json({ code: 400, message: 'Missing or invalid required fields' }); return;
+ }
+ const info_uuid = uuidv4();
+ await mysqlConnection.query(
+ `INSERT INTO equipment_disposition
+ (info_uuid, equipment_uuid, system_architecture, system, system_id, ram_info, date, wonderlab_run_file, wonderlab_v)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+ [info_uuid, equipment_uuid, system_architecture, system, system_id, ram_info, date, wonderlab_run_file, wonderlab_v]
+ );
+ res.json({ code: 200, message: 'add ok', info_uuid });
+}
+
+// 编辑设备配置记录(管理员权限)
+export async function updateEquipmentDisposition(req: Request, res: Response): Promise {
+ const { info_uuid } = req.params;
+ const {
+ system_architecture, system, system_id,
+ ram_info, date, wonderlab_run_file, wonderlab_v
+ } = req.body;
+ if (!isNonEmptyString(info_uuid)) {
+ res.status(400).json({ code: 400, message: 'Missing info_uuid' }); return;
+ }
+ await mysqlConnection.query(
+ `UPDATE equipment_disposition SET
+ system_architecture = ?, system = ?, system_id = ?, ram_info = ?, date = ?, wonderlab_run_file = ?, wonderlab_v = ?
+ WHERE info_uuid = ?`,
+ [system_architecture, system, system_id, ram_info, date, wonderlab_run_file, wonderlab_v, info_uuid]
+ );
+ res.json({ code: 200, message: 'update ok' });
+}
+
+// 管理员注册设备
+export async function registerEquipmentAdmin(req: Request, res: Response): Promise {
+ const { bios_id, country, user_id, info_uuid: customInfoUuid } = req.body;
+ if (!isNonEmptyString(bios_id) || !isNonEmptyString(country) || !isPositiveInt(user_id)) {
+ res.status(400).json({ code: 400, message: 'Missing or invalid required fields' }); return;
+ }
+ const [rows]: any = await mysqlConnection.query(
+ 'SELECT * FROM equipment_info WHERE bios_id = ? AND user_id = ?',
+ [bios_id, user_id]
+ );
+ if (rows.length > 0) {
+ res.status(201).json({ code: 201, message: 'this info_uuid is on the detabest' }); return;
+ }
+ const info_uuid = isNonEmptyString(customInfoUuid) ? customInfoUuid : uuidv4();
+ const date = new Date();
+ await mysqlConnection.query(
+ 'INSERT INTO equipment_info (info_uuid, bios_id, country, user_id, date) VALUES (?, ?, ?, ?, ?)',
+ [info_uuid, bios_id, country, user_id, date]
+ );
+ res.status(200).json({ code: 200, message: 'register is ok', info_uuid });
+}
\ No newline at end of file
diff --git a/src/server/controllers/server.controller.ts b/src/server/controllers/server.controller.ts
new file mode 100644
index 0000000..1abf2e0
--- /dev/null
+++ b/src/server/controllers/server.controller.ts
@@ -0,0 +1,322 @@
+import { Request, Response } from 'express'
+import { v4 as uuidv4 } from 'uuid'
+import ApiResponse from '../utils/apiResp'
+import {
+ findServerById,
+ adminFindServerById,
+ findServerList,
+ adminFindServerList,
+ findServerAboutListByServerId,
+ addServerInfo,
+ addServerAbout,
+ delServerById,
+ delServerByIds,
+ updateServerInfo,
+ updateServerInfoAudit,
+ updateServerAboutAudit
+ } from '../models/server.model'
+
+// 获取单个服务器信息
+export const getServerInfo = async (req: Request, res: Response): Promise => {
+ const serverId = req.params.serverId
+
+ if (!serverId) {
+ res.status(400).json(ApiResponse.error(400, 'Server ID is required'))
+ return
+ }
+
+ const server = await findServerById(serverId)
+ if (!server) {
+ res.status(404).json(ApiResponse.error(404, 'Server not found'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Get server info success', server))
+ return
+}
+
+// 管理员获取单个服务器信息
+export const adminGetServerInfo = async (req: Request, res: Response): Promise => {
+ const serverId = req.params.serverId
+
+ if (!serverId) {
+ res.status(400).json(ApiResponse.error(400, 'Server ID is required'))
+ return
+ }
+
+ const server = await adminFindServerById(serverId)
+ if (!server) {
+ res.status(404).json(ApiResponse.error(404, 'Server not found'))
+ return
+ }
+
+ res.status(200).json(ApiResponse.success(200, 'Get server info success', server))
+ return
+}
+
+// 获取服务器列表
+export const getServerList = async (req: Request, res: Response): Promise => {
+ const { pageSize, pageNum } = req.query
+
+ const pageSizeNum = Number(pageSize)
+ const pageNumNum = Number(pageNum)
+
+ if (isNaN(pageSizeNum) || isNaN(pageNumNum)) {
+ res.status(400).json(ApiResponse.error(400, 'Page size and page num must be numbers'))
+ return
+ }
+
+ const offset = (pageNumNum - 1) * pageSizeNum
+
+ const servers = await findServerList(offset, pageSizeNum)
+ if (!servers) {
+ res.status(404).json(ApiResponse.error(404, 'Servers list not found'))
+ return
+ }
+
+ res.status(200).json(ApiResponse.success(200, 'Get server list success', servers))
+ return
+}
+
+// 管理员获取服务器列表
+export const adminGetServerList = async (req: Request, res: Response): Promise => {
+ const { pageSize, pageNum } = req.query
+ const pageSizeNum = Number(pageSize)
+ const pageNumNum = Number(pageNum)
+
+ if (isNaN(pageSizeNum) || isNaN(pageNumNum)) {
+ res.status(400).json(ApiResponse.error(400, 'Page size and page num must be numbers'))
+ return
+ }
+
+ const offset = (pageNumNum - 1) * pageSizeNum
+
+ const servers = await adminFindServerList(offset, pageSizeNum)
+ if (!servers) {
+ res.status(404).json(ApiResponse.error(404, 'Servers list not found'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Get server list success', servers))
+ return
+}
+
+// 获取单个服务器的多个详细信息
+export const getServerAboutListByServerId = async (req: Request, res: Response): Promise => {
+ const serverId = req.params.serverId
+ const { pageSize, pageNum } = req.query
+ const pageSizeNum = Number(pageSize)
+ const pageNumNum = Number(pageNum)
+
+ if (isNaN(pageSizeNum) || isNaN(pageNumNum)) {
+ res.status(400).json(ApiResponse.error(400, 'Page size and page num must be numbers'))
+ return
+ }
+
+ const offset = (pageNumNum - 1) * pageSizeNum
+ const aboutList = await findServerAboutListByServerId(serverId, offset, pageSizeNum)
+ if (!aboutList) {
+ res.status(404).json(ApiResponse.error(404, 'Server about list not found'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Get server about list success', aboutList))
+ return
+}
+
+// 创建服务器
+export const createServer = async (req: Request, res: Response): Promise => {
+ const { name, server_icon_url, country, user_id, date, todo, audit_status, audit_reason } = req.body
+ const serverId = uuidv4()
+ const result = await addServerInfo({
+ id: serverId,
+ name,
+ server_icon_url,
+ country,
+ user_id,
+ date,
+ todo,
+ audit_status,
+ audit_reason
+ })
+ if (!result) {
+ res.status(400).json(ApiResponse.error(400, 'Create server failed'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Create server success', serverId))
+ return
+}
+
+// 创建服务器详细信息
+export const createServerAbout = async (req: Request, res: Response): Promise => {
+ const { about, docs_url, info_id, ip, server_type, server_oauth, qq_group } = req.body
+ const aboutId = uuidv4()
+ const result = await addServerAbout({
+ id: aboutId,
+ about,
+ docs_url,
+ info_id,
+ ip,
+ server_type,
+ server_oauth,
+ qq_group,
+ date: new Date().toISOString(),
+ audit_status: 'no',
+ })
+ if (!result) {
+ res.status(400).json(ApiResponse.error(400, 'Create server about failed'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Create server about success', aboutId))
+ return
+}
+
+// 删除服务器
+export const delServer = async (req: Request, res: Response): Promise => {
+ const serverId = req.params.serverId
+ if (!serverId) {
+ res.status(400).json(ApiResponse.error(400, 'Server ID is required'))
+ return
+ }
+ const result = await delServerById(serverId)
+ if (!result) {
+ res.status(400).json(ApiResponse.error(400, 'Delete server failed'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Delete server success'))
+ return
+}
+
+// 删除多个服务器
+export const delServers = async (req: Request, res: Response): Promise => {
+ const serverIds = req.body.serverIds
+ if (!serverIds || serverIds.length === 0) {
+ res.status(400).json(ApiResponse.error(400, 'Server IDs are required'))
+ return
+ }
+ const result = await delServerByIds(serverIds)
+ if (!result) {
+ res.status(400).json(ApiResponse.error(400, 'Delete servers failed'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Delete servers success'))
+ return
+}
+
+// 更新服务器
+export const updateServer = async (req: Request, res: Response): Promise => {
+ const serverId = req.params.serverId
+ const { name, server_icon_url, country, user_id, date, todo, audit_status, audit_reason } = req.body
+ if (!serverId) {
+ res.status(400).json(ApiResponse.error(400, 'Server ID is required'))
+ return
+ }
+ const result = await updateServerInfo(serverId, {
+ id: serverId,
+ name,
+ server_icon_url,
+ country,
+ user_id,
+ date,
+ todo,
+ audit_status,
+ audit_reason
+ })
+ if (!result) {
+ res.status(400).json(ApiResponse.error(400, 'Update server failed'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Update server success'))
+ return
+}
+
+// 客户端获取单个服务器信息
+export const clientGetServerInfo = async (req: Request, res: Response): Promise => {
+ const { serverId } = req.body
+ if (!serverId) {
+ res.status(400).json(ApiResponse.error(400, 'Server ID is required'))
+ return
+ }
+ const server = await findServerById(serverId)
+ if (!server) {
+ res.status(404).json(ApiResponse.error(404, 'Server not found'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Get server info success', server))
+ return
+}
+
+// 客户端获取服务器列表
+export const clientGetServerList = async (req: Request, res: Response): Promise => {
+ const { pageSize, pageNum } = req.body
+ const pageSizeNum = Number(pageSize)
+ const pageNumNum = Number(pageNum)
+
+ if (isNaN(pageSizeNum) || isNaN(pageNumNum)) {
+ res.status(400).json(ApiResponse.error(400, 'Page size and page num must be numbers'))
+ return
+ }
+
+ const offset = (pageNumNum - 1) * pageSizeNum
+ const servers = await findServerList(offset, pageSizeNum)
+ if (!servers) {
+ res.status(404).json(ApiResponse.error(404, 'Servers list not found'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Get server list success', servers))
+ return
+}
+
+// 管理员审核Server info
+export const adminAuditServerInfo = async (req: Request, res: Response): Promise => {
+ const { serverId, audit_status, audit_reason } = req.body
+ if (!serverId) {
+ res.status(400).json(ApiResponse.error(400, 'Server ID is required'))
+ return
+ }
+ const result = await updateServerInfoAudit(serverId, {
+ id: serverId,
+ name: '',
+ server_icon_url: '',
+ country: '',
+ user_id: '',
+ date: new Date().toISOString(),
+ todo: 0,
+ audit_status,
+ audit_reason
+ })
+ if (!result) {
+ res.status(400).json(ApiResponse.error(400, 'Audit server failed'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Audit server success'))
+ return
+}
+
+// 管理员审核Server about
+export const adminAuditServerAbout = async (req: Request, res: Response): Promise => {
+ const { serverId, audit_status, audit_reason } = req.body
+ if (!serverId) {
+ res.status(400).json(ApiResponse.error(400, 'Server ID is required'))
+ return
+ }
+ const result = await updateServerAboutAudit(serverId, {
+ id: serverId,
+ about: '',
+ info_id: '',
+ ip: {
+ ip: '',
+ },
+ server_type: '原版服务器',
+ server_oauth: 'mojang',
+ qq_group: {
+ qq: '',
+ },
+ date: new Date().toISOString(),
+ audit_status,
+ audit_reason
+ })
+ if (!result) {
+ res.status(400).json(ApiResponse.error(400, 'Audit server about failed'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Audit server about success'))
+ return
+}
diff --git a/src/server/controllers/user.controller.ts b/src/server/controllers/user.controller.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/server/controllers/version.controller.ts b/src/server/controllers/version.controller.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/server/main.ts b/src/server/main.ts
new file mode 100644
index 0000000..cfaff1c
--- /dev/null
+++ b/src/server/main.ts
@@ -0,0 +1,158 @@
+import express, { Express } from "express"
+import dotenv from "dotenv"
+import cors from 'cors'
+import ViteExpress from "vite-express"
+import { connectToDatabases, redisClient } from './config/database'
+import morgan from 'morgan'
+import { Server } from "http"
+import swaggerUi from 'swagger-ui-express'
+import swaggerSpec from './utils/swagger'
+import helmet from 'helmet'
+
+import userRoute from './routes/user.route'
+import versionRoute from './routes/version.route'
+import equipmentRoute from './routes/equipment.route'
+import serverRoute from './routes/server.route'
+import ApiResponse from "./utils/apiResp"
+import ConsoleLog from "./utils/consoleLog"
+
+dotenv.config()
+
+const app: Express = express()
+
+const isDev : boolean = process.env.NODE_ENV !== 'production'
+
+if (isDev) {
+ app.use(
+ morgan('dev', {
+ skip: function (req, res) {
+ const apiPathRegex = /^\/api(\/|$)/;
+ return !apiPathRegex.test(req.originalUrl);
+ }
+ })
+ )
+} else {
+ app.use(
+ morgan('combined', {
+ skip: function (req, res) {
+ const apiPathRegex = /^\/api(\/|$)/;
+ return !apiPathRegex.test(req.originalUrl);
+ }
+ })
+ )
+}
+
+app.use(helmet({
+ contentSecurityPolicy: false // 禁用 CSP 安全策略,放行所有资源
+}))
+app.use(cors({
+ origin: [
+ 'http://localhost:5173',
+ 'https://dream-pep.niho.dpdns.org'
+ ],
+ credentials: true
+}))
+app.use(express.json())
+app.use(express.urlencoded({ extended: true }))
+
+app.set('etag', false);
+
+app.use((req, res, next) => {
+ if (req.originalUrl.startsWith('/api')) {
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
+ res.setHeader('Pragma', 'no-cache');
+ res.setHeader('Expires', '0');
+ }
+ next();
+});
+
+app.use('/api/hello', (req, res) => {
+ res.status(200).json(ApiResponse.success(200, 'If you see this, the server is working.'))
+})
+
+app.use('/api/user', userRoute)
+app.use('/api/version', versionRoute)
+app.use('/api/equipment', equipmentRoute)
+app.use('/api/server', serverRoute)
+app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
+
+app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
+ console.error('❌ 未处理的错误:', {
+ method: req.method,
+ url: req.originalUrl,
+ ip: req.ip,
+ userAgent: req.get('User-Agent'),
+ error: {
+ message: err.message,
+ stack: err.stack,
+ name: err.name
+ },
+ timestamp: new Date().toISOString()
+ })
+
+ res.status(500).json({
+ code: 500,
+ status: 'error',
+ message: '内部服务器错误',
+ data: null,
+ timestamp: new Date().toISOString()
+ })
+})
+
+let server : Server
+const PORT = Number(process.env.PORT) || 3000
+
+async function startServer() : Promise {
+ ConsoleLog.log('🚀 启动 Spacen.Tab 服务器...')
+ try {
+ ConsoleLog.log('🔌 连接到数据库...')
+ await connectToDatabases()
+
+ server = ViteExpress.listen(app, PORT, () => {
+ ConsoleLog.log(`🎉 Spacen.Tab 服务器启动成功!`)
+ ConsoleLog.log(`📍 开发服务器运行在: http://localhost:${PORT}`)
+ /*ConsoleLog.log(`📚 API 文档可用在: http://localhost:${PORT}/api/docs`)*/
+ ConsoleLog.log(`🌐 运行环境: ${isDev ? '开发环境' : '生产环境'}`)
+ })
+
+ process.on('SIGTERM', shutdown)
+ process.on('SIGINT', shutdown)
+
+ } catch (error) {
+ ConsoleLog.error(`❌ 服务器启动失败:`, error)
+ process.exit(1)
+ }
+}
+
+async function shutdown() {
+ ConsoleLog.log('🛑 收到关闭信号. 优雅关闭...')
+
+ if (server) {
+ server.close(async (err?: Error) => {
+ if (err) {
+ ConsoleLog.error(`❌ 服务器关闭时发生错误:`, err)
+ process.exit(1)
+ }
+
+ try {
+ ConsoleLog.log('🔌 关闭 Redis 连接...')
+ await redisClient.ping()
+ await redisClient.quit()
+ ConsoleLog.log('✅ Redis 连接关闭')
+
+ ConsoleLog.log('✅ 服务器关闭成功')
+ process.exit(0)
+ } catch (error) {
+ ConsoleLog.error(`❌ 关闭 Redis 连接时发生错误:`, error)
+ process.exit(1)
+ }
+ })
+ }
+
+ setTimeout(() => {
+ ConsoleLog.log('⏰ 关闭超时. 强制退出.')
+ process.exit(1)
+ }, 10000).unref()
+}
+
+startServer()
\ No newline at end of file
diff --git a/src/server/middlewares/adminMiddleware.ts b/src/server/middlewares/adminMiddleware.ts
new file mode 100644
index 0000000..938f110
--- /dev/null
+++ b/src/server/middlewares/adminMiddleware.ts
@@ -0,0 +1,53 @@
+import jwt from 'jsonwebtoken'
+import { redisClient } from '../config/database'
+import { Request, Response, NextFunction } from 'express'
+import ApiResponse from "../utils/apiResp"
+
+export const adminMiddleware = async (req: Request, res: Response, next: NextFunction) => {
+
+ const token = req.headers.authorization;
+ if (!token) {
+ res.status(401).json(ApiResponse.error(401, 'Unauthorized: Missing token'))
+ return
+ }
+
+ let decoded = {};
+ try {
+ decoded = jwt.verify(token.split(' ')[1], process.env.JWT_SECRET!) as any;
+ } catch (err) {
+ res.status(401).json(ApiResponse.error(401, 'Unauthorized: Invalid token'))
+ return
+ }
+ try {
+ const redisKey = `user:${(decoded as any).userId}`;
+ const cachedUser = await redisClient.get(redisKey);
+
+ if (!cachedUser) {
+ res.status(401).json(ApiResponse.error(401, 'Unauthorized: Session expired'))
+ return
+ }
+ const user = JSON.parse(cachedUser);
+ if (user.is_admin !== 1) {
+ res.status(403).json(ApiResponse.error(403, 'Forbidden: Admin access required'))
+ return
+ }
+
+ const sensitiveFields = ['password']
+ if (cachedUser) {
+ const user = JSON.parse(cachedUser);
+ sensitiveFields.forEach(field => delete user[field])
+ if (req.method === 'GET' || req.method === 'DELETE') {
+ req.query.user = user
+ } else {
+ if (req.body === undefined) {
+ req.body = {}
+ }
+ req.body.user = user
+ }
+ }
+ next();
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err)
+ }
+};
diff --git a/src/server/middlewares/clientMiddleware.ts b/src/server/middlewares/clientMiddleware.ts
new file mode 100644
index 0000000..4d06eaf
--- /dev/null
+++ b/src/server/middlewares/clientMiddleware.ts
@@ -0,0 +1,82 @@
+import { Request, Response, NextFunction } from 'express'
+import ApiResponse from '../utils/apiResp'
+import * as crypto from 'crypto';
+import * as fs from 'fs';
+
+import { fileURLToPath } from 'node:url'
+import path, { dirname } from 'node:path'
+const __dirname = dirname(fileURLToPath(import.meta.url))
+
+export const clientMiddleware = async (req: Request, res: Response, next: NextFunction) => {
+ try {
+ const encryptedContent = req.header('X-Encrypted-Content');
+
+ if (!encryptedContent) {
+ res.status(400).json(ApiResponse.error(400, 'Missing encrypted content'))
+ return
+ }
+
+ // 验证Base64格式
+ if (!/^[A-Za-z0-9+/=]+$/.test(encryptedContent)) {
+ console.log('Invalid Base64 format:', encryptedContent);
+ res.status(401).json(ApiResponse.error(401, 'Invalid encrypted content format'))
+ return
+ }
+
+ const privateKeyPath = path.join(__dirname, '../private_key.pem')
+ const privateKey = fs.readFileSync(privateKeyPath, 'utf8')
+
+ let decrypted;
+ try {
+ decrypted = crypto.privateDecrypt(
+ {
+ key: privateKey,
+ padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
+ oaepHash: 'sha256'
+ },
+ Buffer.from(encryptedContent, 'base64')
+ ).toString('utf8');
+ } catch (decryptError: any) {
+ console.error('Decryption error:', decryptError.message);
+ // 区分不同类型的解密错误
+ if (decryptError.code === 'ERR_OSSL_RSA_OAEP_DECODING_ERROR') {
+ res.status(400).json(ApiResponse.error(400, 'Decryption failed: Invalid ciphertext'))
+ } else {
+ res.status(500).json(ApiResponse.error(500, 'Internal decryption error'))
+ }
+ return
+ }
+
+ let clientData;
+ try {
+ clientData = JSON.parse(decrypted);
+ } catch (parseError) {
+ console.error('JSON parsing error:', parseError);
+ res.status(400).json(ApiResponse.error(400, 'Invalid client data format'))
+ return
+ }
+
+ if (!validateClientData(clientData)) {
+ console.log('Invalid client data:', clientData);
+ res.status(401).json(ApiResponse.error(401, 'Invalid client data'));
+ return
+ }
+
+ (req as any).body = clientData;
+ console.log('Client data validated successfully');
+
+ next();
+ } catch (error) {
+ console.error('Error in client middleware:', error);
+ res.status(500).json(ApiResponse.error(500, 'Internal server error'))
+ return
+ }
+};
+
+function validateClientData(data: any): boolean {
+ return (
+ data &&
+ data.timestamp &&
+ Math.abs(Date.now() - data.timestamp) < 60000
+ );
+}
\ No newline at end of file
diff --git a/src/server/middlewares/tokenMiddleware.ts b/src/server/middlewares/tokenMiddleware.ts
new file mode 100644
index 0000000..5706029
--- /dev/null
+++ b/src/server/middlewares/tokenMiddleware.ts
@@ -0,0 +1,48 @@
+import jwt from 'jsonwebtoken'
+import { redisClient } from '../config/database'
+import { Request, Response, NextFunction } from 'express'
+import ApiResponse from '../utils/apiResp'
+
+export const tokenMiddleware = async (req: Request, res: Response, next: NextFunction) => {
+
+ const token = req.headers.authorization;
+ if (!token) {
+ res.status(401).json(ApiResponse.error(401, 'Unauthorized: Missing token'))
+ return
+ }
+ let decoded = {};
+ try {
+ decoded = jwt.verify(token.split(' ')[1], process.env.JWT_SECRET!) as any;
+ } catch (err) {
+ res.status(401).json(ApiResponse.error(401, 'Unauthorized: Invalid token'))
+ return
+ }
+ try {
+ const redisKey = `user:${(decoded as any).userId}`;
+ const cachedUser = await redisClient.get(redisKey);
+
+ if (!cachedUser) {
+ res.status(401).json(ApiResponse.error(401, 'Unauthorized: Session expired'))
+ return
+ }
+
+ const sensitiveFields = ['password']
+ if (cachedUser) {
+ const user = JSON.parse(cachedUser);
+ sensitiveFields.forEach(field => delete user[field])
+ // 判断是get请求,就不设置user
+ if (req.method === 'GET' || req.method === 'DELETE') {
+ req.query.user = user
+ } else {
+ if (req.body === undefined) {
+ req.body = {}
+ }
+ req.body.user = user
+ }
+ }
+ next();
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err)
+ }
+};
diff --git a/src/server/models/equipment.model.ts b/src/server/models/equipment.model.ts
new file mode 100644
index 0000000..f6553fc
--- /dev/null
+++ b/src/server/models/equipment.model.ts
@@ -0,0 +1,24 @@
+// 适配 mysql2,导出表名和字段名常量,无需sequelize
+
+export const EQUIPMENT_INFO_TABLE = 'equipment_info';
+export const EQUIPMENT_DISPOSITION_TABLE = 'equipment_disposition';
+
+export const EquipmentInfoFields = [
+ 'info_uuid',
+ 'bios_id',
+ 'country',
+ 'user_id',
+ 'date',
+];
+
+export const EquipmentDispositionFields = [
+ 'info_uuid',
+ 'equipment_uuid',
+ 'system_architecture',
+ 'system',
+ 'system_id',
+ 'ram_info',
+ 'date',
+ 'wonderlab_run_file',
+ 'wonderlab_v',
+];
\ No newline at end of file
diff --git a/src/server/models/server.model.ts b/src/server/models/server.model.ts
new file mode 100644
index 0000000..d4ffe85
--- /dev/null
+++ b/src/server/models/server.model.ts
@@ -0,0 +1,264 @@
+import { mysqlConnection } from "../config/database"
+
+type AuditStatus = 'yes' | 'no' | 'lock'
+type ServerType = '原版服务器' | '插件服务器' | 'MOD服务器' | '插件MOD混合服务器'
+type ServerOAuth = 'mojang' | 'none' | 'yggdrasil'
+
+/**
+ * 服务器信息表接口
+ * 存储服务器的基本信息
+ */
+export interface ServerInfo {
+ // 服务器记录ID(UUID)
+ id: string;
+ // 服务器名称
+ name: string;
+ // 服务器图标URL
+ server_icon_url: string;
+ // 服务器数据 ID
+ server_about_id?: string;
+ // 设备地区(例如china)
+ country: string;
+ // 关联用户id(例如10001),默认为发起请求的用户
+ user_id: string;
+ // 服务器登记日期(UTC+8)
+ date: string;
+ // 服务器被访问数
+ todo: number;
+ // 审核状态(yes/no/lock),默认no
+ audit_status: AuditStatus;
+ // 审核原因(非必须)
+ audit_reason?: string;
+ // 服务器关于数量
+ aboutTotalCount?: number;
+}
+
+/**
+ * 服务器数据表接口
+ * 用于记录服务器的详细数据,允许服务器同时存在多个记录
+ */
+export interface ServerAbout {
+ // 记录ID(UUID)
+ id: string;
+ // 服务器描述(支持markdown内容)
+ about: string;
+ // 服务器文档(只能写一个链接,可以为空)
+ docs_url?: string;
+ // 关联服务器ID,与服务器信息表的服务器记录ID关联
+ info_id: string;
+ // 服务器地址(允许多个IP记录)
+ ip: Record;
+ // 服务器类型
+ server_type: ServerType;
+ // 验证类型
+ server_oauth: ServerOAuth;
+ // 服务器群(允许记录多个群)
+ qq_group: Record;
+ // 信息记录日期
+ date: string;
+ // 审核状态(yes/no/lock),默认no
+ audit_status: AuditStatus;
+ // 审核原因(非必须)
+ audit_reason?: string;
+}
+
+// 根据服务器ID获取单个服务器的信息,包括服务器信息表和当前服务器数据表的内容
+export const findServerById = async (id: string) => {
+ const [info] = await mysqlConnection.query(
+ 'SELECT * FROM server_info WHERE id = ?',
+ [id]
+ )
+ const serverAboutId = (info as ServerInfo[])[0].server_about_id
+
+ const [about] = await mysqlConnection.query(
+ 'SELECT * FROM server_about WHERE id = ?',
+ [serverAboutId]
+ )
+ return {
+ info: (info as ServerInfo[])[0],
+ about: (about as ServerAbout[])[0]
+ }
+}
+
+export const adminFindServerById = async (id: string) => {
+ const [info] = await mysqlConnection.query(
+ 'SELECT * FROM server_info WHERE id = ?',
+ [id]
+ )
+
+ const [about] = await mysqlConnection.query(
+ 'SELECT * FROM server_about WHERE info_id = ?',
+ [id]
+ )
+ return {
+ info: (info as ServerInfo[])[0],
+ about
+ }
+}
+
+export const findServerList = async (offset: number, limit: number) => {
+ const [total] = await mysqlConnection.query(
+ 'SELECT COUNT(*) as total FROM server_info'
+ )
+ const totalCount = (total as { total: number }[])[0].total
+
+ // 移除audit_status为lock和no的服务器
+ const [servers] = await mysqlConnection.query(
+ 'SELECT * FROM server_info WHERE audit_status != "lock" AND audit_status != "no" LIMIT ? OFFSET ?',
+ [limit, offset]
+ )
+ return {
+ total: totalCount,
+ servers
+ }
+}
+
+export const adminFindServerList = async (offset: number, limit: number) => {
+ // 获取服务器总数
+ const [total] = await mysqlConnection.query(
+ 'SELECT COUNT(*) as total FROM server_info'
+ )
+ const totalCount = (total as { total: number }[])[0].total
+
+ // 获取服务器列表
+ const [servers] = await mysqlConnection.query(
+ 'SELECT * FROM server_info LIMIT ? OFFSET ?',
+ [limit, offset]
+ )
+
+ // 通过for循环,获取每个server的about数量,并设置aboutTotalCount
+ for (const server of servers as ServerInfo[]) {
+ const [about] = await mysqlConnection.query(
+ 'SELECT COUNT(*) as total FROM server_about WHERE info_id = ?',
+ [server.id]
+ )
+ server.aboutTotalCount = (about as { total: number }[])[0].total
+ }
+
+ return {
+ total: totalCount,
+ servers
+ }
+}
+
+export const findServerAboutListByServerId = async (id: string, offset: number, limit: number) => {
+ const [total] = await mysqlConnection.query(
+ 'SELECT COUNT(*) as total FROM server_about WHERE info_id = ?',
+ [id]
+ )
+ const totalCount = (total as { total: number }[])[0].total
+ const [about] = await mysqlConnection.query(
+ 'SELECT * FROM server_about WHERE info_id = ? LIMIT ? OFFSET ?',
+ [id, limit, offset]
+ )
+ return {
+ total: totalCount,
+ about
+ }
+}
+
+export const addServerInfo = async (info: ServerInfo) => {
+ const [result] = await mysqlConnection.query(
+ 'INSERT INTO server_info (id, name, server_icon_url, server_about_id, country, user_id, date, todo, audit_status, audit_reason) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
+ [
+ info.id,
+ info.name,
+ info.server_icon_url,
+ null,
+ info.country,
+ info.user_id,
+ info.date ?? new Date().toISOString(),
+ 0,
+ 'lock',
+ info.audit_reason
+ ]
+ )
+ return result
+}
+
+export const addServerAbout = async (about: ServerAbout) => {
+ const [result] = await mysqlConnection.query(
+ 'INSERT INTO server_about (id, about, docs_url, info_id, ip, server_type, server_oauth, qq_group, date, audit_status, audit_reason) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
+ [
+ about.id,
+ about.about,
+ about.docs_url,
+ about.info_id,
+ about.ip,
+ about.server_type,
+ about.server_oauth,
+ about.qq_group,
+ about.date ?? new Date().toISOString(),
+ 'lock',
+ about.audit_reason
+ ]
+ )
+ return result
+}
+
+export const delServerById = async (id: string) => {
+ const [result] = await mysqlConnection.query(
+ 'DELETE FROM server_info WHERE id = ?',
+ [id]
+ )
+ const [result2] = await mysqlConnection.query(
+ 'DELETE FROM server_about WHERE info_id = ?',
+ [id]
+ )
+ return result
+}
+
+export const delServerByIds = async (ids: string[]) => {
+ const [result] = await mysqlConnection.query(
+ 'DELETE FROM server_info WHERE id IN (?)',
+ [ids]
+ )
+ const [result2] = await mysqlConnection.query(
+ 'DELETE FROM server_about WHERE info_id IN (?)',
+ [ids]
+ )
+ return result
+}
+
+export const updateServerInfo = async (id: string, info: ServerInfo) => {
+ const [result] = await mysqlConnection.query(
+ 'UPDATE server_info SET name = ?, server_icon_url = ?, country = ?, user_id = ?, date = ?, todo = ?, audit_status = ?, audit_reason = ? WHERE id = ?',
+ [
+ info.name,
+ info.server_icon_url,
+ info.country,
+ info.user_id,
+ info.date ?? new Date().toISOString(),
+ info.todo,
+ 'lock',
+ info.audit_reason
+ ]
+ )
+ return result
+}
+
+// 审核Server info
+export const updateServerInfoAudit = async (id: string, info: ServerInfo) => {
+ const [result] = await mysqlConnection.query(
+ 'UPDATE server_info SET audit_status = ?, audit_reason = ? WHERE id = ?',
+ [
+ info.audit_status,
+ info.audit_reason,
+ id
+ ]
+ )
+ return result
+}
+
+// 审核Server about
+export const updateServerAboutAudit = async (id: string, about: ServerAbout) => {
+ const [result] = await mysqlConnection.query(
+ 'UPDATE server_about SET audit_status = ?, audit_reason = ? WHERE id = ?',
+ [
+ about.audit_status,
+ about.audit_reason,
+ id
+ ]
+ )
+ return result
+}
diff --git a/src/server/models/user.model.ts b/src/server/models/user.model.ts
new file mode 100644
index 0000000..3a71aaa
--- /dev/null
+++ b/src/server/models/user.model.ts
@@ -0,0 +1,129 @@
+import { mysqlConnection } from "../config/database"
+
+export interface User {
+ id: number,
+ username: string,
+ email: string,
+ password: string,
+ is_banned: boolean,
+ ban_reason?: string | null,
+ ban_expiration?: Date | null,
+ is_admin: boolean,
+ created_at: Date,
+ updated_at?: Date,
+ last_login?: Date,
+ avatar?: string,
+ qq_num?: string,
+ email_verified?: boolean,
+}
+
+export const findUserById = async (userId: number): Promise => {
+ const [rows] = await mysqlConnection.query(
+ 'SELECT * FROM users WHERE id = ? LIMIT 1',
+ [userId]
+ );
+ return (rows as User[])[0] || null;
+};
+
+export const findUserByEmail = async (email: string): Promise => {
+ const [rows] = await mysqlConnection.query(
+ 'SELECT * FROM users WHERE email = ? LIMIT 1',
+ [email]
+ );
+ return (rows as User[])[0] || null;
+};
+
+export const createUser = async (user: Omit) => {
+ const [result] = await mysqlConnection.execute(
+ `INSERT INTO users (
+ username,
+ email,
+ password,
+ is_banned,
+ ban_reason,
+ ban_expiration,
+ is_admin,
+ qq_num,
+ email_verified
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
+ [
+ user.username,
+ user.email,
+ user.password,
+ user.is_banned ? 1 : 0,
+ user.ban_reason || null,
+ user.ban_expiration || null,
+ user.is_admin ? 1 : 0,
+ user.qq_num || null,
+ user.email_verified || 0,
+ ]
+ );
+ return result;
+}
+
+export const updateUser = async (user: User) => {
+ const [result] = await mysqlConnection.execute(
+ `UPDATE users SET
+ username = ?,
+ email = ?,
+ is_banned = ?,
+ ban_reason = ?,
+ ban_expiration = ?,
+ is_admin = ?,
+ updated_at = NOW(),
+ avatar = ?,
+ qq_num = ?,
+ email_verified = ?
+ WHERE id = ?`,
+ [
+ user.username,
+ user.email,
+ user.is_banned ? 1 : 0,
+ user.ban_reason || null,
+ user.ban_expiration || null,
+ user.is_admin ? 1 : 0,
+ user.avatar || null,
+ user.qq_num || null,
+ user.email_verified || 0,
+ user.id
+ ]
+ );
+ return result;
+}
+
+export const deleteUserById = async (userId: number) => {
+ const [result] = await mysqlConnection.execute(
+ 'DELETE FROM users WHERE id = ?',
+ [userId]
+ );
+ return result;
+};
+
+export const updateUserPassword = async (userId: number, newPassword: string) => {
+ const [result] = await mysqlConnection.execute(
+ 'UPDATE users SET password = ? WHERE id = ?',
+ [newPassword, userId]
+ );
+ return result;
+}
+
+export const findUserByPage = async (pageNum: number, pageSize: number) => {
+ const [rows] = await mysqlConnection.query(
+ 'SELECT id, username, email, is_banned, ban_reason, ban_expiration, is_admin, created_at, updated_at, last_login, avatar, qq_num, email_verified FROM users LIMIT ?, ?',
+ [(pageNum - 1) * pageSize, pageSize]
+ );
+ return rows as User[];
+}
+
+export const updateLastLogin = async (userId: number) => {
+ const [result] = await mysqlConnection.execute(
+ 'UPDATE users SET last_login = NOW() WHERE id = ?',
+ [userId]
+ );
+ return result;
+}
+
+export const getUserCount = async () => {
+ const [rows] = await mysqlConnection.query('SELECT COUNT(*) as count FROM users');
+ return (rows as any)[0].count;
+}
\ No newline at end of file
diff --git a/src/server/models/version.model.ts b/src/server/models/version.model.ts
new file mode 100644
index 0000000..a6bdd89
--- /dev/null
+++ b/src/server/models/version.model.ts
@@ -0,0 +1,102 @@
+import { mysqlConnection } from "../config/database"
+import axios from 'axios'
+
+export interface VersionInfo {
+ id: number;
+ tag_name: string;
+ prerelease: string;
+ created_at: string;
+ assets: {
+ name: string;
+ size: number;
+ download_count: number;
+ digest: string;
+ browser_download_url: string;
+ }[];
+}
+
+export interface GithubMirrorInfo {
+ id: number;
+ name: string;
+ url: string;
+ created_at: string;
+}
+
+// 适用于Github的Axios配置
+export const githubAxiosConfig = {
+ headers: {
+ 'Accept': 'application/vnd.github.v3+json',
+ 'User-Agent': 'WonderLab-Server'
+ }
+}
+
+export const findAllGithubMirror = async () => {
+ const [rows] = await mysqlConnection.query('SELECT * FROM github_mirror_info')
+ return rows
+}
+
+export const findGithubMirrorById = async (id: number) => {
+ const [rows] = await mysqlConnection.query('SELECT * FROM github_mirror_info WHERE id = ?', [id])
+ return (rows as GithubMirrorInfo[])[0] || null
+}
+
+export const addGithubMirror = async (name: string, url: string) => {
+ const [result] = await mysqlConnection.query('INSERT INTO github_mirror_info (name, url) VALUES (?, ?)', [name, url])
+ return result
+}
+
+export const findVersionByMirror = async (mirrorId: number, page: number, pageSize: number) => {
+ const mirror: any = await findGithubMirrorById(mirrorId);
+ if (!mirror) {
+ throw new Error('Mirror not found');
+ }
+ console.log('Mirror Info:', mirror);
+ console.log('Mirror URL:', mirror.url);
+
+ const axiosConfig = {
+ ...githubAxiosConfig,
+ baseURL: mirror.url,
+ timeout: 15000,
+
+ validateStatus: (status: any) => status >= 200 && status < 300,
+ };
+
+ let response: any;
+
+ try {
+ response = await axios.get(
+ `/repos/Lunova-Studio/WonderLab.Override/releases`,
+ {
+ ...axiosConfig,
+ params: {
+ page: page,
+ per_page: pageSize
+ }
+ }
+ );
+ } catch (error: any) {
+ if (error.code === 'ERR_TIMEOUT') {
+ throw new Error('Request Mirror Timeout');
+ } else if (error.response) {
+ throw new Error(`API Error: ${error.response.status} - ${error.response.data.message}`);
+ } else {
+ throw new Error(`Unknown Error: ${error.message}`);
+ }
+ }
+
+ let versionList: VersionInfo[] = response.data.map((release: any) => ({
+ id: release.id,
+ tag_name: release.tag_name,
+ prerelease: release.prerelease,
+ created_at: release.created_at,
+ assets: release.assets.map((asset: any) => ({
+ name: asset.name,
+ size: asset.size,
+ download_count: asset.download_count,
+ digest: asset.digest,
+ browser_download_url: asset.browser_download_url,
+ })),
+ }));
+
+ return versionList;
+};
diff --git a/src/server/private_key.pem b/src/server/private_key.pem
new file mode 100644
index 0000000..5e0b381
--- /dev/null
+++ b/src/server/private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDPZL+U8ag3BW3/
+I9X4/xmWL8Y1R5+64MbGf6Cdi5JWVOVJ4bXsCslcO+dePc/IadlNk/N8LpKtxEiE
+BIhAmcJb6aILeKrTbvv/IxezBwvQvEMz+Q2dlzXfSqtmMM64yt9csvoN5ezy6vJ7
+gELEYpRA0JSdmk0hLAWKetpYRS5UEZLpjTtsllf1GDoZveTiYmE5ZhHwwwoKed06
+37AH7EYHP8Z7f1JdpoRPFT+wp9L6sPgCt0Q07hYHJ8WCtinYXfofYy+pkmhxxenR
+E6qL/stfbqqEq2rWnf/088RMmNY4wDoleSOg0oe86qWz2jbCv+6QvQuvCuUAnWsq
+kOEeTBHbAgMBAAECggEBAJb7kpm9QCgmNMj87sZh+N+kkhixtvBWuX9C5MwtEvD0
+xyP9RPK9e5BBMBQkzcLqhD99CVKLmU8VyXEK16KoDZ3aPZU6WKqqbpx0kMlNxOVG
+WWpZ0va6YYbYlmVD/Qr87pVO2soJbHIAZ11Pp8vOtH1nG/qMncAbi0EgZRrD6pFa
+Fe78Bac4kJ+GPTTY310BfkKMVScXw4k8q1AGpSzyoDacz8q27BRMwYUthYwQPcWN
+e2l8Ei2RQRNNf7uNvW8mKBAVtEioZaRentjArmO8ylc+k8ycKZ6spJyeG5OqNo5Q
+bgc64X7SUTpXrEJDJ0ECWLsEHjKf91UrApvv3FhN4MECgYEA8xr54Rbayd2NvdVM
+XcJmwCPyXmM0r2f7HQ5sg1/q4Ed0mGZdQEbxO2XtMd0nJU92zW/dLjUL7VpxmGFU
+1bJdNft039s3RmoRKZIqNZ2MhlU6VqM4EIT7r3J6ZiOoSdQh6XxIEcNvha/V/zeF
+NK14u66xQ5zAo4x7FeJU5RAVpnsCgYEA2mTbTXueRS2E9HOr4MZRiXxJX5rxnBA3
+ScQvXiavE7Oxq2pqhMRurIjx6Suc6cwcok7cfdZbP0bwP5fgtuM1VmrHsXmQg5mi
+SWD1nrIAYNKtBEkoakF16YwJg8C39hEEN6tp3dX2Mq0kwTe2zKw0d/2P1ClfRerJ
+u0XDe0eAFCECgYBADBFdHdpSaI5ZPwK5iOftIwtNlrFgrPJZe11XV0PYBrtPE1jq
+AEa5sWbGRk9z4vWXtir9a3hS9gsLbEkfa7hm6nKmL2jmojcXTqzUJ0fIdFfET4gL
+wh1U4cIheq9xk/PDQbzzARw2ilbEwGnVsQYn3CRTfc66x939q/1Vw+EQVQKBgFEu
+8L7K1zjMBQKCBUCXODQteXEKvgnPlV3ciecr5iUixUb9Vf9oreOBPLv6tGfMslA7
+lIdKqbzJ+AELykLyDx/CrdzxXaEt+T/hxSxq6u0TI7HWKCnnj5dzsQ3DFB9qj/DJ
+zZ7jLJQF/QW+WjuXQCHiphauEnaBIuB0vIgVOi1BAoGAcIYGLcl1OZ23M+L4A+YB
+m7V5thMEy+KD8mN/q76rF+PEJqnPshBUIgLzLXr++Ewv1mwJORx7fB/HR4iMvqnW
+vtQnvXd2n1khm3IRdUNrkVpvVcYZ162TzTK55fkJA0OI7XR5hV5iGKrOQgvLYBEk
+vNem/W4h7URQbrc2AT2rnjc=
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/src/server/routes/equipment.route.ts b/src/server/routes/equipment.route.ts
new file mode 100644
index 0000000..febabb5
--- /dev/null
+++ b/src/server/routes/equipment.route.ts
@@ -0,0 +1,473 @@
+import { Router } from 'express';
+import { adminMiddleware } from '../middlewares/adminMiddleware';
+import { clientMiddleware } from '../middlewares/clientMiddleware';
+import * as equipmentController from '../controllers/equipmentController';
+
+const router = Router();
+
+/**
+ * @swagger
+ * /equipment/info:
+ * get:
+ * summary: 查询设备信息
+ * tags: [设备模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: query
+ * name: page
+ * schema:
+ * type: integer
+ * default: 1
+ * description: 页码
+ * example: 1
+ * - in: query
+ * name: pageSize
+ * schema:
+ * type: integer
+ * default: 10
+ * description: 每页数量
+ * example: 10
+ * - in: query
+ * name: user_id
+ * schema:
+ * type: integer
+ * description: 按用户ID筛选
+ * example: 10001
+ * - in: query
+ * name: country
+ * schema:
+ * type: string
+ * description: 按地区筛选
+ * example: "china"
+ * responses:
+ * 200:
+ * description: 查询成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 200
+ * data:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/EquipmentInfo'
+ * total:
+ * type: integer
+ * example: 1
+ * page:
+ * type: integer
+ * example: 1
+ * pageSize:
+ * type: integer
+ * example: 10
+ */
+router.get('/equipment/info', adminMiddleware, equipmentController.getEquipmentInfo);
+
+/**
+ * @swagger
+ * /equipment/disposition:
+ * get:
+ * summary: 查询设备配置记录
+ * tags: [设备模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: query
+ * name: page
+ * schema:
+ * type: integer
+ * default: 1
+ * description: 页码
+ * example: 1
+ * - in: query
+ * name: pageSize
+ * schema:
+ * type: integer
+ * default: 10
+ * description: 每页数量
+ * example: 10
+ * - in: query
+ * name: equipment_uuid
+ * schema:
+ * type: string
+ * description: 按设备ID筛选
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * responses:
+ * 200:
+ * description: 查询成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 200
+ * data:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/EquipmentDisposition'
+ * total:
+ * type: integer
+ * example: 1
+ * page:
+ * type: integer
+ * example: 1
+ * pageSize:
+ * type: integer
+ * example: 10
+ */
+router.get('/equipment/disposition', adminMiddleware, equipmentController.getEquipmentDisposition);
+
+/**
+ * @swagger
+ * /equipment/info:
+ * post:
+ * summary: 管理员添加设备信息
+ * tags: [设备模块]
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - bios_id
+ * - country
+ * - user_id
+ * properties:
+ * bios_id:
+ * type: string
+ * example: "ABC123XYZ"
+ * country:
+ * type: string
+ * example: "china"
+ * user_id:
+ * type: integer
+ * example: 10001
+ * example:
+ * bios_id: "ABC123XYZ"
+ * country: "china"
+ * user_id: 10001
+ * responses:
+ * 200:
+ * description: 添加成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: add ok
+ * info_uuid:
+ * type: string
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ */
+router.post('/equipment/info', adminMiddleware, equipmentController.addEquipmentInfo);
+
+/**
+ * @swagger
+ * /equipment/disposition:
+ * post:
+ * summary: 管理员添加设备配置记录
+ * tags: [设备模块]
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - equipment_uuid
+ * - system_architecture
+ * - system
+ * - system_id
+ * - ram_info
+ * - date
+ * - wonderlab_run_file
+ * - wonderlab_v
+ * properties:
+ * equipment_uuid:
+ * type: string
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * system_architecture:
+ * type: string
+ * example: "x64"
+ * system:
+ * type: string
+ * example: "windows"
+ * system_id:
+ * type: string
+ * example: "10.0.19045"
+ * ram_info:
+ * type: string
+ * example: "16GB"
+ * date:
+ * type: string
+ * format: date-time
+ * example: "2025-07-05 18:05:40"
+ * wonderlab_run_file:
+ * type: string
+ * example: "C:/Program Files/WonderLab"
+ * wonderlab_v:
+ * type: string
+ * example: "1.0.0"
+ * example:
+ * equipment_uuid: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * system_architecture: "x64"
+ * system: "windows"
+ * system_id: "10.0.19045"
+ * ram_info: "16GB"
+ * date: "2025-07-05 18:05:40"
+ * wonderlab_run_file: "C:/Program Files/WonderLab"
+ * wonderlab_v: "1.0.0"
+ * responses:
+ * 200:
+ * description: 添加成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: add ok
+ * info_uuid:
+ * type: string
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ */
+router.post('/equipment/disposition', adminMiddleware, equipmentController.addEquipmentDisposition);
+
+/**
+ * @swagger
+ * /equipment/disposition/{info_uuid}:
+ * put:
+ * summary: 管理员编辑设备配置记录
+ * tags: [设备模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: info_uuid
+ * required: true
+ * schema:
+ * type: string
+ * description: 设备配置记录ID
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - system_architecture
+ * - system
+ * - system_id
+ * - ram_info
+ * - date
+ * - wonderlab_run_file
+ * - wonderlab_v
+ * properties:
+ * system_architecture:
+ * type: string
+ * example: "x64"
+ * system:
+ * type: string
+ * example: "windows"
+ * system_id:
+ * type: string
+ * example: "10.0.19045"
+ * ram_info:
+ * type: string
+ * example: "16GB"
+ * date:
+ * type: string
+ * format: date-time
+ * example: "2025-07-05 18:05:40"
+ * wonderlab_run_file:
+ * type: string
+ * example: "C:/Program Files/WonderLab"
+ * wonderlab_v:
+ * type: string
+ * example: "1.0.0"
+ * example:
+ * system_architecture: "x64"
+ * system: "windows"
+ * system_id: "10.0.19045"
+ * ram_info: "16GB"
+ * date: "2025-07-05 18:05:40"
+ * wonderlab_run_file: "C:/Program Files/WonderLab"
+ * wonderlab_v: "1.0.0"
+ * responses:
+ * 200:
+ * description: 编辑成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: update ok
+ */
+router.put('/equipment/disposition/:info_uuid', adminMiddleware, equipmentController.updateEquipmentDisposition);
+
+/**
+ * @swagger
+ * /equipment/register:
+ * post:
+ * summary: 管理员注册设备
+ * tags: [设备模块]
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - bios_id
+ * - country
+ * - user_id
+ * properties:
+ * bios_id:
+ * type: string
+ * description: 主板BIOS-ID
+ * example: "ABC123XYZ"
+ * country:
+ * type: string
+ * description: 设备地区
+ * example: "china"
+ * user_id:
+ * type: integer
+ * description: 关联用户id
+ * example: 10001
+ * info_uuid:
+ * type: string
+ * description: 可选,自定义设备信息记录ID(UUID)
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * example:
+ * bios_id: "ABC123XYZ"
+ * country: "china"
+ * user_id: 10001
+ * info_uuid: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * responses:
+ * 200:
+ * description: 注册成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: register is ok
+ * info_uuid:
+ * type: string
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * 201:
+ * description: 设备已存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 201
+ * message:
+ * type: string
+ * example: this info_uuid is on the detabest
+ */
+router.post('/equipment/register', adminMiddleware, equipmentController.registerEquipmentAdmin);
+
+/**
+ * @swagger
+ * /system/register:
+ * post:
+ * summary: 客户端注册设备
+ * tags: [设备模块]
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - bios_id
+ * - country
+ * - user_id
+ * properties:
+ * bios_id:
+ * type: string
+ * description: 主板BIOS-ID
+ * example: "ABC123XYZ"
+ * country:
+ * type: string
+ * description: 设备地区
+ * example: "china"
+ * user_id:
+ * type: integer
+ * description: 关联用户id
+ * example: 10001
+ * example:
+ * bios_id: "ABC123XYZ"
+ * country: "china"
+ * user_id: 10001
+ * responses:
+ * 200:
+ * description: 注册成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: register is ok
+ * info_uuid:
+ * type: string
+ * example: "b7e23ec2-8d5c-4c7a-8e7a-1b2c3d4e5f6a"
+ * 201:
+ * description: 设备已存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * code:
+ * type: integer
+ * example: 201
+ * message:
+ * type: string
+ * example: this info_uuid is on the detabest
+ */
+router.post('/system/register', clientMiddleware, equipmentController.registerEquipment);
+
+export default router;
\ No newline at end of file
diff --git a/src/server/routes/server.route.ts b/src/server/routes/server.route.ts
new file mode 100644
index 0000000..f23869d
--- /dev/null
+++ b/src/server/routes/server.route.ts
@@ -0,0 +1,1669 @@
+import { Router } from "express"
+import { tokenMiddleware } from "../middlewares/tokenMiddleware"
+import { adminMiddleware } from "../middlewares/adminMiddleware"
+import { clientMiddleware } from "../middlewares/clientMiddleware"
+import {
+ adminGetServerInfo,
+ getServerInfo,
+ getServerList,
+ clientGetServerInfo,
+ clientGetServerList,
+ adminGetServerList,
+ getServerAboutListByServerId,
+ adminAuditServerAbout,
+ adminAuditServerInfo,
+ updateServer,
+ delServer,
+ delServers,
+ createServer
+ } from "../controllers/server.controller"
+
+export const router = Router()
+
+const TempFunc = () => {
+ throw new Error('NotImplementedError')
+}
+
+/**
+ * @swagger
+ * tags:
+ * name: 服务器模块
+ * description: 服务器信息管理相关接口(包含用户、管理员、客户端三种权限)
+ */
+
+/**
+ * 用户权限 API 路由
+ */
+
+/**
+ * @swagger
+ * /server/list:
+ * get:
+ * summary: 获取服务器列表
+ * description: 分页获取当前用户有权访问的服务器列表,需传入分页参数
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: query
+ * name: pageSize
+ * required: true
+ * schema:
+ * type: integer
+ * example: 10
+ * description: 每页条数(必须为数字)
+ * - in: query
+ * name: pageNum
+ * required: true
+ * schema:
+ * type: integer
+ * example: 1
+ * description: 页码(必须为数字,从1开始)
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get server list success
+ * data:
+ * type: object
+ * properties:
+ * list:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * example: "srv_123456"
+ * name:
+ * type: string
+ * example: "生存服务器"
+ * server_icon_url:
+ * type: string
+ * example: "https://example.com/icon.png"
+ * country:
+ * type: string
+ * example: "CN"
+ * audit_status:
+ * type: string
+ * example: "approved"
+ * total:
+ * type: integer
+ * example: 50
+ * 400:
+ * description: 参数错误(页码或每页条数不是数字)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Page size and page num must be numbers
+ * 401:
+ * description: 未授权(token无效或缺失)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 404:
+ * description: 服务器列表不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: Servers list not found
+ */
+// 获取服务器列表
+router.get('/list', tokenMiddleware, getServerList)
+
+/**
+ * @swagger
+ * /server:
+ * post:
+ * summary: 创建服务器
+ * description: 创建新的服务器记录,返回服务器ID
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * name:
+ * type: string
+ * description: 服务器名称
+ * example: "新生存服务器"
+ * server_icon_url:
+ * type: string
+ * description: 服务器图标URL
+ * example: "https://example.com/new-icon.png"
+ * country:
+ * type: string
+ * description: 服务器所在国家(ISO代码)
+ * example: "CN"
+ * user_id:
+ * type: string
+ * description: 服务器负责人ID
+ * example: "user_789"
+ * date:
+ * type: string
+ * format: date-time
+ * description: 创建时间
+ * example: "2023-08-01T12:00:00Z"
+ * todo:
+ * type: string
+ * description: 待办事项
+ * example: "配置服务器插件"
+ * audit_status:
+ * type: string
+ * description: 审核状态
+ * example: "pending"
+ * audit_reason:
+ * type: string
+ * description: 审核原因(未审核时可为空)
+ * example: ""
+ * responses:
+ * 200:
+ * description: 创建成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Create server success
+ * data:
+ * type: string
+ * example: "srv_789012"
+ * 400:
+ * description: 创建失败(参数错误)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Create server failed
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ */
+// 创建服务器
+router.post('/', tokenMiddleware, createServer)
+
+/**
+ * @swagger
+ * /server/about/multi/{serverId}:
+ * get:
+ * summary: 获取单个服务器的多个详细信息
+ * description: 分页获取指定服务器的详细信息列表
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * - in: query
+ * name: pageSize
+ * required: true
+ * schema:
+ * type: integer
+ * example: 10
+ * description: 每页条数(必须为数字)
+ * - in: query
+ * name: pageNum
+ * required: true
+ * schema:
+ * type: integer
+ * example: 1
+ * description: 页码(必须为数字,从1开始)
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get server about list success
+ * data:
+ * type: object
+ * properties:
+ * list:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * example: "about_123"
+ * about:
+ * type: string
+ * example: "服务器详细介绍"
+ * docs_url:
+ * type: string
+ * example: "https://example.com/docs"
+ * ip:
+ * type: object
+ * properties:
+ * ip:
+ * type: string
+ * example: "192.168.1.100"
+ * server_type:
+ * type: string
+ * example: "原版服务器"
+ * audit_status:
+ * type: string
+ * example: "approved"
+ * 400:
+ * description: 参数错误(页码或每页条数不是数字)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Page size and page num must be numbers
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 404:
+ * description: 详细信息不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: Server about list not found
+ */
+// 获取单个服务器的多个详细信息
+router.get('/about/multi/:serverId', tokenMiddleware, getServerAboutListByServerId)
+
+/**
+ * @swagger
+ * /server/{serverId}:
+ * get:
+ * summary: 获取单个服务器信息
+ * description: 根据服务器ID获取服务器基本信息
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get server info success
+ * data:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * example: "srv_123456"
+ * name:
+ * type: string
+ * example: "生存服务器"
+ * server_icon_url:
+ * type: string
+ * example: "https://example.com/icon.png"
+ * country:
+ * type: string
+ * example: "CN"
+ * user_id:
+ * type: string
+ * example: "user_789"
+ * date:
+ * type: string
+ * format: date-time
+ * example: "2023-07-01T12:00:00Z"
+ * todo:
+ * type: string
+ * example: "配置服务器插件"
+ * audit_status:
+ * type: string
+ * example: "approved"
+ * 400:
+ * description: 缺少服务器ID
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 404:
+ * description: 服务器不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: Server not found
+ */
+// 获取单个服务器信息
+router.get('/:serverId', tokenMiddleware, getServerInfo)
+
+/**
+ * @swagger
+ * /server/{serverId}:
+ * delete:
+ * summary: 删除服务器
+ * description: 根据服务器ID删除指定服务器
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * responses:
+ * 200:
+ * description: 删除成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Delete server success
+ * 400:
+ * description: 缺少服务器ID或删除失败
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ */
+// 删除服务器
+router.delete('/:serverId', tokenMiddleware, delServer)
+
+/**
+ * @swagger
+ * /server/{serverId}:
+ * put:
+ * summary: 更新服务器信息
+ * description: 根据服务器ID更新服务器信息
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * name:
+ * type: string
+ * description: 服务器名称
+ * example: "生存服务器-更新"
+ * server_icon_url:
+ * type: string
+ * description: 服务器图标URL
+ * example: "https://example.com/update-icon.png"
+ * country:
+ * type: string
+ * description: 服务器所在国家
+ * example: "CN"
+ * user_id:
+ * type: string
+ * description: 服务器负责人ID
+ * example: "user_789"
+ * date:
+ * type: string
+ * format: date-time
+ * description: 更新时间
+ * example: "2023-08-02T12:00:00Z"
+ * todo:
+ * type: string
+ * description: 待办事项
+ * example: "更新服务器插件"
+ * audit_status:
+ * type: string
+ * description: 审核状态
+ * example: "pending"
+ * audit_reason:
+ * type: string
+ * description: 审核原因
+ * example: "信息更新需重新审核"
+ * responses:
+ * 200:
+ * description: 更新成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Update server success
+ * 400:
+ * description: 缺少服务器ID或更新失败
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ */
+// 更新服务器
+router.put('/:serverId', tokenMiddleware, updateServer)
+
+/**
+ * 管理员权限 API 路由
+ */
+
+/**
+ * @swagger
+ * /server/admin/list:
+ * get:
+ * summary: 管理员获取所有服务器信息
+ * description: 管理员分页获取系统中所有服务器的列表
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: query
+ * name: pageSize
+ * required: true
+ * schema:
+ * type: integer
+ * example: 10
+ * description: 每页条数(必须为数字)
+ * - in: query
+ * name: pageNum
+ * required: true
+ * schema:
+ * type: integer
+ * example: 1
+ * description: 页码(必须为数字,从1开始)
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get server list success
+ * data:
+ * type: object
+ * properties:
+ * list:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * example: "srv_123456"
+ * name:
+ * type: string
+ * example: "生存服务器"
+ * server_icon_url:
+ * type: string
+ * example: "https://example.com/icon.png"
+ * country:
+ * type: string
+ * example: "CN"
+ * user_id:
+ * type: string
+ * example: "user_789"
+ * audit_status:
+ * type: string
+ * example: "approved"
+ * total:
+ * type: integer
+ * example: 100
+ * 400:
+ * description: 参数错误(页码或每页条数不是数字)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Page size and page num must be numbers
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有管理员权限
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有管理员权限
+ * 404:
+ * description: 服务器列表不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: Servers list not found
+ */
+// 获取所有服务器信息
+router.get('/admin/list', adminMiddleware, adminGetServerList)
+
+/**
+ * @swagger
+ * /server/admin/audit/{serverId}:
+ * post:
+ * summary: 管理员审核服务器
+ * description: 管理员审核指定服务器的信息,更新审核状态和原因
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - audit_status
+ * properties:
+ * audit_status:
+ * type: string
+ * description: 审核状态(approved/rejected/pending)
+ * example: "approved"
+ * audit_reason:
+ * type: string
+ * description: 审核原因
+ * example: "信息完整,符合要求"
+ * responses:
+ * 200:
+ * description: 审核成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Audit server success
+ * 400:
+ * description: 缺少服务器ID或审核失败
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有管理员权限
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有管理员权限
+ */
+// 审核服务器
+router.post('/admin/audit/:serverId', adminMiddleware, adminAuditServerInfo)
+
+/**
+ * @swagger
+ * /server/admin/audit/about/{serverId}:
+ * post:
+ * summary: 管理员审核服务器关于信息
+ * description: 管理员审核指定服务器的详细信息,更新审核状态和原因
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - audit_status
+ * properties:
+ * audit_status:
+ * type: string
+ * description: 审核状态(approved/rejected/pending)
+ * example: "approved"
+ * audit_reason:
+ * type: string
+ * description: 审核原因
+ * example: "详细信息完整,符合要求"
+ * responses:
+ * 200:
+ * description: 审核成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Audit server about success
+ * 400:
+ * description: 缺少服务器ID或审核失败
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有管理员权限
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有管理员权限
+ */
+// 审核服务器关于信息
+router.post('/admin/audit/about/:serverId', adminMiddleware, adminAuditServerAbout)
+
+/**
+ * @swagger
+ * /server/admin/{serverId}:
+ * get:
+ * summary: 管理员获取单个服务器信息
+ * description: 管理员获取指定服务器的详细信息(包含完整字段)
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get server info success
+ * data:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * example: "srv_123456"
+ * name:
+ * type: string
+ * example: "生存服务器"
+ * server_icon_url:
+ * type: string
+ * example: "https://example.com/icon.png"
+ * country:
+ * type: string
+ * example: "CN"
+ * user_id:
+ * type: string
+ * example: "user_789"
+ * date:
+ * type: string
+ * format: date-time
+ * example: "2023-07-01T12:00:00Z"
+ * todo:
+ * type: string
+ * example: "配置服务器插件"
+ * audit_status:
+ * type: string
+ * example: "approved"
+ * audit_reason:
+ * type: string
+ * example: "信息完整,符合要求"
+ * 400:
+ * description: 缺少服务器ID
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有管理员权限
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有管理员权限
+ * 404:
+ * description: 服务器不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: Server not found
+ */
+// 获取单个服务器信息
+router.get('/admin/:serverId', adminMiddleware, adminGetServerInfo)
+
+/**
+ * @swagger
+ * /server/admin/{serverId}:
+ * delete:
+ * summary: 管理员删除多个服务器
+ * description: 管理员批量删除服务器,通过请求体传入服务器ID数组
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 占位参数(实际通过请求体传入ID列表)
+ * example: "placeholder"
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - serverIds
+ * properties:
+ * serverIds:
+ * type: array
+ * items:
+ * type: string
+ * description: 服务器ID数组
+ * example: ["srv_123456", "srv_789012"]
+ * responses:
+ * 200:
+ * description: 删除成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Delete servers success
+ * 400:
+ * description: 缺少服务器ID列表或删除失败
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server IDs are required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有管理员权限
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有管理员权限
+ */
+// 删除服务器
+router.delete('/admin/:serverId', adminMiddleware, delServers)
+
+/**
+ * @swagger
+ * /server/admin/{serverId}:
+ * put:
+ * summary: 管理员更新服务器
+ * description: 管理员更新指定服务器的信息(拥有全部字段更新权限)
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * name:
+ * type: string
+ * description: 服务器名称
+ * example: "生存服务器-管理员更新"
+ * server_icon_url:
+ * type: string
+ * description: 服务器图标URL
+ * example: "https://example.com/admin-update-icon.png"
+ * country:
+ * type: string
+ * description: 服务器所在国家
+ * example: "CN"
+ * user_id:
+ * type: string
+ * description: 服务器负责人ID(可变更)
+ * example: "user_456"
+ * date:
+ * type: string
+ * format: date-time
+ * description: 更新时间
+ * example: "2023-08-03T12:00:00Z"
+ * todo:
+ * type: string
+ * description: 待办事项
+ * example: "完成服务器安全加固"
+ * audit_status:
+ * type: string
+ * description: 审核状态(管理员可直接变更)
+ * example: "approved"
+ * audit_reason:
+ * type: string
+ * description: 审核原因
+ * example: "管理员更新,保持通过状态"
+ * responses:
+ * 200:
+ * description: 更新成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Update server success
+ * 400:
+ * description: 缺少服务器ID或更新失败
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有管理员权限
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有管理员权限
+ */
+// 更新服务器
+router.put('/admin/:serverId', adminMiddleware, updateServer)
+
+/**
+ * 客户端权限 API 路由
+ */
+
+/**
+ * @swagger
+ * /server/client/list:
+ * get:
+ * summary: 客户端获取服务器列表
+ * description: 客户端分页获取可访问的服务器列表
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - pageSize
+ * - pageNum
+ * properties:
+ * pageSize:
+ * type: integer
+ * example: 10
+ * description: 每页条数(必须为数字)
+ * pageNum:
+ * type: integer
+ * example: 1
+ * description: 页码(必须为数字,从1开始)
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get server list success
+ * data:
+ * type: object
+ * properties:
+ * list:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * example: "srv_123456"
+ * name:
+ * type: string
+ * example: "客户端可访问服务器"
+ * server_icon_url:
+ * type: string
+ * example: "https://example.com/client-icon.png"
+ * country:
+ * type: string
+ * example: "CN"
+ * audit_status:
+ * type: string
+ * example: "approved"
+ * total:
+ * type: integer
+ * example: 30
+ * 400:
+ * description: 参数错误(页码或每页条数不是数字)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Page size and page num must be numbers
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有客户端权限
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有客户端权限
+ * 404:
+ * description: 服务器列表不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: Servers list not found
+ */
+// 客户端获取服务器列表
+router.get('/client/list', clientMiddleware, clientGetServerList)
+
+/**
+ * @swagger
+ * /server/client/{serverId}:
+ * get:
+ * summary: 客户端获取单个服务器信息
+ * description: 客户端获取指定服务器的信息
+ * tags: [服务器模块]
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: serverId
+ * required: true
+ * schema:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - serverId
+ * properties:
+ * serverId:
+ * type: string
+ * description: 服务器ID
+ * example: "srv_123456"
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get server info success
+ * data:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * example: "srv_123456"
+ * name:
+ * type: string
+ * example: "客户端可访问服务器"
+ * server_icon_url:
+ * type: string
+ * example: "https://example.com/client-icon.png"
+ * country:
+ * type: string
+ * example: "CN"
+ * audit_status:
+ * type: string
+ * example: "approved"
+ * 400:
+ * description: 缺少服务器ID
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Server ID is required
+ * 401:
+ * description: 未授权
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: 未授权访问
+ * 403:
+ * description: 没有客户端权限或无权访问
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: 没有权限访问该服务器
+ * 404:
+ * description: 服务器不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: Server not found
+ */
+// 客户端获取单个服务器信息
+router.get('/client/:serverId', clientMiddleware, clientGetServerInfo)
+
+export default router
+
\ No newline at end of file
diff --git a/src/server/routes/user.route.ts b/src/server/routes/user.route.ts
new file mode 100644
index 0000000..91dd966
--- /dev/null
+++ b/src/server/routes/user.route.ts
@@ -0,0 +1,1585 @@
+import { Router } from "express"
+import {
+ User,
+ findUserById,
+ findUserByEmail,
+ createUser,
+ updateUser,
+ deleteUserById,
+ findUserByPage,
+ getUserCount,
+ updateUserPassword,
+ updateLastLogin
+} from "../models/user.model"
+import bcrypt from 'bcryptjs'
+import jwt, { SignOptions } from 'jsonwebtoken'
+import { redisClient } from '../config/database'
+import ApiResponse from "../utils/apiResp"
+import { tokenMiddleware } from "../middlewares/tokenMiddleware"
+import { adminMiddleware } from "../middlewares/adminMiddleware"
+
+export const router = Router()
+
+/**
+ * @swagger
+ * /user/register:
+ * post:
+ * summary: 创建新用户
+ * description: 通过提供用户名、邮箱和密码注册新用户
+ * tags:
+ * - 用户模块
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - username
+ * - email
+ * - password
+ * properties:
+ * username:
+ * type: string
+ * description: 用户显示名称
+ * example: johndoe
+ * email:
+ * type: string
+ * format: email
+ * description: 用户邮箱地址
+ * example: john@example.com
+ * password:
+ * type: string
+ * format: password
+ * description: 用户密码(至少8个字符)
+ * example: Password123!
+ * qq_num:
+ * type: string
+ * description: qq 账号
+ * example: 123456789
+ * responses:
+ * 200:
+ * description: 用户创建成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: User created successfully
+ * data:
+ * type: object
+ * example: null
+ * 400:
+ * description: 缺少必要的字段
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 409:
+ * description: 邮箱已被注册
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 409
+ * message:
+ * type: string
+ * example: Email already registered
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.post('/register', async (req, res) => {
+ try {
+ const body = req.body
+
+ if (!body || !body.username || !body.email || !body.password) {
+ res.status(400).json(ApiResponse.error(400, 'Bad Request: Missing required fields'))
+ return
+ }
+
+ const { username, email, password } = req.body
+
+ if (!req.body.username || !req.body.email || !req.body.password) {
+ res.status(400).json(ApiResponse.error(400, 'Bad Request: Missing required fields'))
+ return
+ }
+
+ const existingUser = await findUserByEmail(email)
+ if (existingUser) {
+ res.status(409).json(ApiResponse.error(409, 'Conflict: User already exists'))
+ return
+ }
+
+ const hashedPassword = await bcrypt.hash(password, 12)
+ const newUser = await createUser(
+ {
+ username,
+ email,
+ password: hashedPassword,
+ qq_num: body.qq_num || '',
+ is_banned: false,
+ is_admin: false
+ } as User)
+
+ if (!newUser) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ return
+ }
+
+ res.status(200).json(ApiResponse.success(200, 'User created successfully', null))
+ return
+ } catch (err) {
+ console.error(err)
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ return
+ }
+})
+
+/**
+ * @swagger
+ * /user/login:
+ * post:
+ * summary: 用户登录认证
+ * description: 通过ID/邮箱和密码获取访问令牌
+ * tags:
+ * - 用户模块
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - account
+ * - password
+ * properties:
+ * account:
+ * type: string
+ * description: ID/邮箱地址
+ * example: 10000 或 john@example.com
+ * password:
+ * type: string
+ * format: password
+ * description: 用户密码
+ * example: Password123!
+ * responses:
+ * 200:
+ * description: 登录成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Login successful
+ * data:
+ * type: object
+ * properties:
+ * token:
+ * type: string
+ * description: JWT访问令牌
+ * example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
+ * userId:
+ * type: string
+ * description: 用户唯一标识
+ * example: 10000
+ * 400:
+ * description: 缺少必要的字段
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 401:
+ * description: 认证失败(用户未注册)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: User or Email not registered
+ * 402:
+ * description: 认证失败(无效的账号或密码)
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 402
+ * message:
+ * type: string
+ * example: User or Email not registered
+ * 403:
+ * description: 用户被封禁
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 403
+ * message:
+ * type: string
+ * example: User is banned
+ * details:
+ * type: object
+ * properties:
+ * banReason:
+ * type: string
+ * description: 封禁原因
+ * example: Violation of terms
+ * banExpiration:
+ * type: string
+ * description: 封禁到期时间
+ * example: 2023-12-31T23:59:59Z
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.post('/login', async (req, res) => {
+ try {
+ const body = req.body
+
+ if (!body || !body.account || !body.password) {
+ res.status(400).json(ApiResponse.error(400, 'Bad Request: Missing required fields'))
+ return
+ }
+
+ if (!req.body || !req.body.account || !req.body.password) {
+ res.status(400).json(ApiResponse.error(400, 'Bad Request: Account and password are required'))
+ return
+ }
+
+ const { account, password } = req.body
+
+ const isEmail = account.includes('@');
+ let user: any = undefined;
+ if (isEmail) {
+ user = await findUserByEmail(account);
+ if (!user) {
+ res.status(401).json(ApiResponse.error(401, 'User or Email not registered'))
+ return;
+ }
+ } else {
+ user = await findUserById(account);
+ if (!user) {
+ res.status(401).json(ApiResponse.error(401, ' User or Email not registered'))
+ return;
+ }
+ }
+
+ console.log(user);
+
+
+ if (user.is_banned) {
+ res.status(403).json(ApiResponse.error(403, 'Forbidden: User is banned', {
+ banReason: user.ban_reason || 'No reason provided',
+ banExpiration: user.ban_expiration || 'No expiration date'
+ }));
+ return;
+ }
+
+ const isValidPassword = await bcrypt.compare(password, user.password);
+ if (!isValidPassword) {
+ res.status(402).json(ApiResponse.error(402, 'Invalid account or password'))
+ return;
+ }
+
+ const token = jwt.sign(
+ { userId: user.id, email: user.email },
+ process.env.JWT_SECRET!,
+ {
+ expiresIn: process.env.JWT_EXPIRES_IN as string,
+ } as SignOptions
+ );
+
+ await redisClient.set(`user:${user.id}`, JSON.stringify(user), 'EX', 72 * 60 * 60);
+
+ await updateLastLogin(user.id);
+
+ res.status(200).json(ApiResponse.success(200, 'Login successful', {
+ token: token,
+ userId: user.id
+ }));
+ return;
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err);
+
+ }
+});
+
+/**
+ * @swagger
+ * /user/info/{id}:
+ * get:
+ * summary: 获取用户信息
+ * description: 通过token获取当前登录用户的详细信息
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: path
+ * name: id
+ * required: true
+ * schema:
+ * type: integer
+ * minimum: 10000
+ * description: The user ID
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Success
+ * data:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * description: 用户唯一标识
+ * example: 10000
+ * username:
+ * type: string
+ * description: 用户名
+ * example: johndoe
+ * email:
+ * type: string
+ * description: 邮箱地址
+ * example: john@example.com
+ * is_banned:
+ * type: boolean
+ * description: 用户是否被封禁
+ * example: false
+ * ban_reason:
+ * type: string
+ * description: 封禁原因
+ * example: Violation of terms
+ * ban_expiration:
+ * type: string
+ * description: 封禁到期时间
+ * example: 2023-12-31T23:59:59Z
+ * is_admin:
+ * type: boolean
+ * description: 用户是否为管理员
+ * example: false
+ * avatar:
+ * type: string
+ * description: 用户头像
+ * example: https://example.com/avatar.jpg
+ * last_login:
+ * type: string
+ * description: 上次登录时间
+ * example: 2023-12-31T23:59:59Z
+ * qq_num:
+ * type: string
+ * description: QQ 号
+ * example: 123456789
+ * 400:
+ * description: 缺少必要的字段
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 401:
+ * description: Token 失效或未提供
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: Missing token
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.get('/info/:id', tokenMiddleware, async (req, res) => {
+ try {
+ const userId = Number(req.params.id);
+ if(isNaN(userId)) {
+ res.status(400).json(ApiResponse.error(400, 'Invalid user ID'));
+ return;
+ }
+
+ const user = await findUserById(userId);
+ if (!user) {
+ res.status(404).json(ApiResponse.error(404, 'User not found'));
+ return;
+ }
+ res.status(200).json(ApiResponse.success(200, 'Query user info successfully', {
+ id: user.id,
+ username: user.username,
+ email: user.email,
+ is_banned: user.is_banned,
+ ban_reason: user.ban_reason,
+ ban_expiration: user.ban_expiration,
+ is_admin: user.is_admin,
+ avatar: user.avatar,
+ qq_num: user.qq_num,
+ last_login: user.last_login,
+ }));
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err);
+ }
+})
+
+/**
+ * @swagger
+ * /user/update:
+ * post:
+ * summary: 更新用户信息
+ * description: 通过token获取当前登录用户并更新其信息,用户只能更新自己的信息,且无法更新敏感字段如封禁状态、管理员权限等
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * username:
+ * type: string
+ * description: 新用户名(选填)
+ * example: johndoe_new
+ * email:
+ * type: string
+ * description: 新邮箱地址(选填)
+ * example: john_new@example.com
+ * qq_num:
+ * type: string
+ * description: qq 号
+ * example: 123456789
+ * responses:
+ * 200:
+ * description: 更新成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Update user info successfully
+ * 400:
+ * description: 缺少必要的字段或参数格式错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 401:
+ * description: Token 失效或未提供
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: Missing token
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.post('/update', tokenMiddleware, async (req, res) => {
+ try {
+ const oldInfo = req.body.user as User;
+
+ const newInfo = req.body;
+ newInfo.id = oldInfo.id
+ delete newInfo.password;
+ delete newInfo.is_banned;
+ delete newInfo.is_admin;
+ delete newInfo.ban_reason;
+ delete newInfo.ban_expiration;
+ delete newInfo.created_at;
+ delete newInfo.updated_at;
+ delete newInfo.last_login;
+ delete newInfo.avatar;
+ delete newInfo.email_verified;
+
+ const userInfo = { ...oldInfo, ...newInfo };
+ delete userInfo.user;
+
+ userInfo.updated_at = new Date();
+
+ const result: any = await updateUser(userInfo);
+ console.log('result', result);
+
+ if(result.affectedRows === 0) {
+ res.status(500).json(ApiResponse.error(500, 'Update user info failed'));
+ return;
+ }
+
+ res.status(200).json(ApiResponse.success(200, 'Update user info successfully'));
+
+
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err);
+ }
+})
+
+/**
+ * @swagger
+ * /user/update/password:
+ * post:
+ * summary: 更新用户密码
+ * description: 通过token获取当前登录用户并更新其密码
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * oldPw:
+ * type: string
+ * description: 旧密码
+ * example: oldpassword123
+ * newPw:
+ * type: string
+ * description: 新密码(不能与旧密码相同)
+ * example: newpassword123
+ * responses:
+ * 200:
+ * description: 密码更新成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Update user password successfully
+ * 400:
+ * description: 缺少必要的字段、旧密码错误或新旧密码相同
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Old password is incorrect
+ * 401:
+ * description: Token 失效或未提供
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: Missing token
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.post('/update/password', tokenMiddleware, async (req, res) => {
+ try {
+ const { oldPw, newPw } = req.body
+
+ if(!oldPw || !newPw) {
+ res.status(400).json(ApiResponse.error(400, 'Missing required fields'));
+ return;
+ }
+
+ const { id } = req.body.user;
+
+ //判断旧密码是否正确
+ const user = await findUserById(id);
+ if (!user) {
+ res.status(404).json(ApiResponse.error(404, 'User not found'));
+ return;
+ }
+ const isValidPassword = await bcrypt.compare(oldPw, user.password);
+ if (!isValidPassword) {
+ res.status(400).json(ApiResponse.error(400, 'Old password is incorrect'));
+ return;
+ }
+
+ // 判断旧密码和新密码是否相同
+ if(oldPw === newPw) {
+ res.status(400).json(ApiResponse.error(400, 'New password cannot be the same as the old password'));
+ return;
+ }
+
+ const result: any = await updateUserPassword(id, await bcrypt.hash(newPw, 12));
+
+ if(result.affectedRows === 0) {
+ res.status(500).json(ApiResponse.error(500, 'Update user password failed'));
+ return;
+ }
+
+ redisClient.del(`user:${id}`);
+ res.status(200).json(ApiResponse.success(200, 'Update user password successfully'));
+ return
+ }catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err);
+ }
+})
+
+/**
+ * @swagger
+ * /user/list:
+ * get:
+ * summary: 分页获取用户列表
+ * description: 管理员权限获取用户列表
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - in: query
+ * name: page
+ * required: true
+ * schema:
+ * type: integer
+ * minimum: 1
+ * description: 分页页码
+ * - in: query
+ * name: size
+ * required: true
+ * schema:
+ * type: integer
+ * minimum: 1
+ * description: 分页大小
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get user list successfully
+ * data:
+ * type: object
+ * properties:
+ * total:
+ * type: integer
+ * description: 用户总数
+ * data:
+ * type: array
+ * description: 用户列表
+ * items:
+ * type: object
+ * properties:
+ * id:
+ * type: string
+ * description: 用户唯一标识
+ * example: 10000
+ * username:
+ * type: string
+ * description: 用户名
+ * example: johndoe
+ * email:
+ * type: string
+ * description: 邮箱地址
+ * example: john@example.com
+ * is_banned:
+ * type: boolean
+ * description: 用户是否被封禁
+ * example: false
+ * ban_reason:
+ * type: string
+ * description: 封禁原因
+ * example: Violation of terms
+ * ban_expiration:
+ * type: string
+ * description: 封禁到期时间
+ * example: 2023-12-31
+ * is_admin:
+ * type: boolean
+ * description: 用户是否为管理员
+ * example: false
+ * created_at:
+ * type: string
+ * description: 用户创建时间
+ * example: 2023-01-01 00:00:00
+ * updated_at:
+ * type: string
+ * description: 用户更新时间
+ * example: 2023-01-01 00:00:00
+ * last_login:
+ * type: string
+ * description: 用户上次登录时间
+ * example: 2023-01-01 00:00:00
+ * avatar:
+ * type: string
+ * description: 用户头像
+ * example: https://example.com/avatar.jpg
+ * qq_num:
+ * type: string
+ * description: 用户 QQ 号
+ * example: 123456789
+ * email_verified:
+ * type: boolean
+ * description: 邮箱是否已验证
+ * example: true
+ * 400:
+ * description: 缺少必要的字段
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.get('/list', adminMiddleware, async (req, res) => {
+ try {
+ const { page, size } = req.query;
+
+ if (!page || !size) {
+ res.status(400).json(ApiResponse.error(400, 'Missing required fields'));
+ return;
+ }
+
+ const pageNum = parseInt(page as string);
+ const pageSize = parseInt(size as string);
+ const [users, total] = await Promise.all([
+ findUserByPage(pageNum, pageSize),
+ getUserCount()
+ ]);
+
+ res.status(200).json(ApiResponse.success(200, 'Get user list successfully', {
+ data: users,
+ total: total
+ }));
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err);
+ }
+})
+
+/**
+ * @swagger
+ * /user/ban:
+ * post:
+ * summary: 封禁用户
+ * description: 管理员封禁指定用户,设置封禁原因和期限
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * userId:
+ * type: string
+ * description: 被封禁用户的ID
+ * example: "10001"
+ * reason:
+ * type: string
+ * description: 封禁原因
+ * example: "违反社区规定"
+ * duration:
+ * type: integer
+ * description: 封禁天数
+ * example: 7
+ * responses:
+ * 200:
+ * description: 封禁成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: "Ban user successfully"
+ * 400:
+ * description: 参数错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: "Missing required fields"
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: "User not found"
+ * 500:
+ * description: 服务器错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: "Internal Server Error"
+ */
+router.post('/ban', adminMiddleware, async (req, res) => {
+ try {
+ const { userId, reason, duration } = req.body;
+
+ if (!userId || !reason || !duration) {
+ res.status(400).json(ApiResponse.error(400, 'Missing required fields'));
+ return;
+ }
+
+ const user = await findUserById(userId);
+ if (!user) {
+ res.status(404).json(ApiResponse.error(404, 'User not found'));
+ return;
+ }
+ if (user.is_banned) {
+ res.status(400).json(ApiResponse.error(400, 'User is already banned'));
+ return;
+ }
+ const banExpiration = new Date(Date.now() + duration * 24 * 60 * 60 * 1000);
+ const result: any = await updateUser({
+ ...user,
+ is_banned: true,
+ ban_reason: reason,
+ ban_expiration: banExpiration,
+ updated_at: new Date(),
+ });
+ if (result.affectedRows === 0) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'));
+ return;
+ }
+ res.status(200).json(ApiResponse.success(200, 'Ban user successfully'));
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'));
+ console.log(err);
+ }
+})
+
+/**
+ * @swagger
+ * /user/unBan:
+ * post:
+ * summary: 解封用户
+ * description: 管理员解除对指定用户的封禁
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * userId:
+ * type: string
+ * description: 被解封用户的ID
+ * example: "10001"
+ * responses:
+ * 200:
+ * description: 解封成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: "Unban user successfully"
+ * 400:
+ * description: 参数错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: "Missing required fields"
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: "User not found"
+ * 500:
+ * description: 服务器错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: "Internal Server Error"
+ */
+router.post('/unBan', adminMiddleware, async (req, res) => {
+ try {
+ const { userId } = req.body;
+ if (!userId) {
+ res.status(400).json(ApiResponse.error(400, 'Missing required fields'));
+ return;
+ }
+ const user = await findUserById(userId);
+ if (!user) {
+ res.status(404).json(ApiResponse.error(404, 'User not found'));
+ return;
+ }
+ if (!user.is_banned) {
+ res.status(400).json(ApiResponse.error(400, 'User is not banned'));
+ return;
+ }
+ const result: any = await updateUser({
+ ...user,
+ is_banned: false,
+ ban_reason: null,
+ ban_expiration: null,
+ updated_at: new Date(),
+ });
+ if (result.affectedRows === 0) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'));
+ return;
+ }
+ res.status(200).json(ApiResponse.success(200, 'Unban user successfully'));
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'));
+ console.log(err);
+ }
+})
+
+/**
+ * @swagger
+ * /user/admin/update:
+ * post:
+ * summary: 更新用户信息(管理员)
+ * description: 通过token获取当前登录用户并更新其信息,管理员只能更新用户的部分信息,且无法更新敏感字段如id, password, created_at, updated_at, last_login等
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * username:
+ * type: string
+ * description: 新用户名(选填)
+ * example: johndoe_new
+ * email:
+ * type: string
+ * description: 新邮箱地址(选填)
+ * example: john_new@example.com
+ * avatar:
+ * type: string
+ * description: 新头像地址(选填)
+ * example: https://example.com/avatar.jpg
+ * is_admin:
+ * type: boolean
+ * description: 是否为管理员(选填)
+ * example: true
+ * qq_num:
+ * type: string
+ * description: qq 号(选填)
+ * example: 123456789
+ * email_verified:
+ * type: boolean
+ * description: 邮箱是否已验证(选填)
+ * example: true
+ * responses:
+ * 200:
+ * description: 更新成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Update user info successfully
+ * 400:
+ * description: 缺少必要的字段或参数格式错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 401:
+ * description: Token 失效或未提供
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 401
+ * message:
+ * type: string
+ * example: Missing token
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.post('/admin/update', adminMiddleware, async (req, res) => {
+ try {
+ const newInfo = req.body;
+
+ if (!newInfo.id) {
+ res.status(400).json(ApiResponse.error(400, 'Missing required fields'));
+ return;
+ }
+
+ const user = await findUserById(newInfo.id);
+ if (!user) {
+ res.status(404).json(ApiResponse.error(404, 'User not found'));
+ return;
+ }
+
+ newInfo.password = user.password;
+ newInfo.created_at = user.created_at;
+ newInfo.updated_at = user.updated_at;
+ newInfo.last_login = user.last_login;
+ newInfo.is_banned = user.is_banned;
+ newInfo.ban_reason = user.ban_reason;
+ newInfo.ban_expiration = user.ban_expiration;
+ delete newInfo.user;
+
+ newInfo.updated_at = new Date();
+
+ const result: any = await updateUser(newInfo);
+ console.log('result', result);
+ if (result.affectedRows === 0) {
+ res.status(500).json(ApiResponse.error(500, 'Update user info failed'));
+ return;
+ }
+
+ res.status(200).json(ApiResponse.success(200, 'Update user info successfully'));
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'));
+ console.log(err);
+ }
+})
+
+/**
+ * @swagger
+ * /user/delete:
+ * post:
+ * summary: 删除用户
+ * description: 通过token获取当前登录用户并删除其信息,管理员只能删除普通用户,且无法删除管理员
+ * tags:
+ * - 用户模块
+ * security:
+ * - bearerAuth: []
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * userId:
+ * type: integer
+ * description: 用户id
+ * example: 1
+ * responses:
+ * 200:
+ * description: 删除成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Delete user successfully
+ * 400:
+ * description: 缺少必要的字段或参数格式错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ * 501:
+ * description: 管理员权限不足,无法删除管理员
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 501
+ * message:
+ * type: string
+ * example: Admin permission is required
+ */
+router.post('/delete', adminMiddleware, async (req, res) => {
+ try {
+ const userId = req.body.userId;
+ if (!userId) {
+ res.status(400).json(ApiResponse.error(400, 'Missing required fields'));
+ return;
+ }
+ const user = await findUserById(userId);
+ if (!user) {
+ res.status(404).json(ApiResponse.error(404, 'User not found'));
+ return;
+ }
+ if (user.is_admin) {
+ res.status(501).json(ApiResponse.error(501, 'Admin permission is required'));
+ return;
+ }
+ const result: any = await deleteUserById(userId);
+ if (result.affectedRows === 0) {
+ res.status(500).json(ApiResponse.error(500, 'Delete user failed'));
+ return;
+ }
+ res.status(200).json(ApiResponse.success(200, 'Delete user successfully'));
+ return;
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'));
+ console.log(err);
+ }
+})
+
+export default router;
\ No newline at end of file
diff --git a/src/server/routes/version.route.ts b/src/server/routes/version.route.ts
new file mode 100644
index 0000000..15c403a
--- /dev/null
+++ b/src/server/routes/version.route.ts
@@ -0,0 +1,490 @@
+import { Router } from "express"
+import {
+ findAllGithubMirror,
+ findGithubMirrorById,
+ findVersionByMirror,
+ addGithubMirror
+} from '../models/version.model'
+import ApiResponse from "../utils/apiResp"
+
+import { tokenMiddleware } from "../middlewares/tokenMiddleware"
+import { adminMiddleware } from "../middlewares/adminMiddleware"
+import { clientMiddleware } from "../middlewares/clientMiddleware"
+
+export const router = Router()
+
+/**
+ * @swagger
+ * /version/github/mirror:
+ * get:
+ * summary: 获取单个GitHub镜像
+ * description: 通过镜像ID获取GitHub镜像详情,需要管理员权限
+ * tags:
+ * - 版本管理
+ * # 移除安全要求,公开API
+ * parameters:
+ * - name: id
+ * in: query
+ * description: 镜像ID
+ * required: true
+ * schema:
+ * type: integer
+ * format: int64
+ * responses:
+ * 200:
+ * description: 获取成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get mirror info successfully
+ * data:
+ * type: object
+ * properties:
+ * id:
+ * type: integer
+ * example: 1
+ * name:
+ * type: string
+ * example: GitHub Mirror
+ * url:
+ * type: string
+ * example: https://api.github.com
+ * description:
+ * type: string
+ * example: A mirror of GitHub repository
+ * created_at:
+ * type: string
+ * example: 2021-01-01T00:00:00.000Z
+ *
+ * 400:
+ * description: 缺少必要的字段或参数格式错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 400
+ * message:
+ * type: string
+ * example: Missing required fields
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.get('/github/mirror', adminMiddleware, async (req, res) => {
+ try {
+ const { id } = req.query
+
+ if (!id) {
+ res.status(400).json(ApiResponse.error(400, 'id is required'))
+ return
+ }
+
+ const mirror = await findGithubMirrorById(Number(id))
+
+ if (!mirror) {
+ res.status(404).json(ApiResponse.error(404, 'Mirror not found'))
+ return
+ }
+
+ res.status(200).json(ApiResponse.success(200, 'Get mirror success', mirror))
+ return
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err)
+ }
+})
+
+/**
+ * @swagger
+ * /version/github/mirror/list:
+ * get:
+ * summary: 获取GitHub镜像列表
+ * description: 获取所有GitHub镜像列表,需要管理员权限
+ * tags:
+ * - 版本管理
+ * # 公开API,无需认证
+ * responses:
+ * 200:
+ * description: 获取镜像列表成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get mirror list success
+ * data:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * id:
+ * type: integer
+ * example: 1
+ * name:
+ * type: string
+ * example: GitHub Mirror
+ * url:
+ * type: string
+ * example: https://api.github.com
+ * created_at:
+ * type: string
+ * example: 2021-01-01T00:00:00.000Z
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.get('/github/mirror/list', adminMiddleware, async (req, res) => {
+ try {
+ const mirrorList = await findAllGithubMirror()
+ if (!mirrorList) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Get mirror list success', mirrorList))
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err)
+ }
+})
+
+/**
+ * @swagger
+ * /version/github/mirror:
+ * post:
+ * summary: 添加GitHub镜像
+ * description: 添加新的GitHub镜像,需要管理员权限
+ * tags:
+ * - 版本管理
+ * # 公开API,无需认证
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * name:
+ * type: string
+ * description: 镜像名称
+ * example: "GitHub Public Mirror"
+ * url:
+ * type: string
+ * description: 镜像URL
+ * example: "https://github.com/mirror"
+ * required:
+ * - name
+ * - url
+ * responses:
+ * 200:
+ * description: 添加镜像成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Add mirror success
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.post('/github/mirror', adminMiddleware, async (req, res) => {
+ try {
+ const { name, url } = req.body
+ if (!name || !url) {
+ res.status(400).json(ApiResponse.error(400, 'name and url are required'))
+ return
+ }
+ const result = await addGithubMirror(name, url)
+ if (!result) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ return
+ }
+ res.status(200).json(ApiResponse.success(200, 'Add mirror success'))
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err)
+ }
+})
+
+/**
+ * @swagger
+ * /version/getLatestList:
+ * get:
+ * summary: 获取发行版最新列表
+ * description: 根据镜像ID分页获取最新发行版列表,公开API无需认证
+ * tags:
+ * - 版本管理
+ * security:
+ * - bearerAuth: []
+ * parameters:
+ * - name: mirrorId
+ * in: query
+ * description: 镜像ID
+ * required: true
+ * schema:
+ * type: integer
+ * format: int64
+ * - name: pageNum
+ * in: query
+ * description: 页码
+ * required: true
+ * schema:
+ * type: integer
+ * format: int32
+ * minimum: 1
+ * default: 1
+ * - name: pageSize
+ * in: query
+ * description: 每页数量
+ * required: true
+ * schema:
+ * type: integer
+ * format: int32
+ * minimum: 1
+ * maximum: 100
+ * default: 10
+ * responses:
+ * 200:
+ * description: 获取版本列表成功
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: success
+ * code:
+ * type: integer
+ * example: 200
+ * message:
+ * type: string
+ * example: Get version list success
+ * data:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * id:
+ * type: integer
+ * example: 1
+ * tag_name:
+ * type: string
+ * example: 2.5.0
+ * prerelease:
+ * type: boolean
+ * example: false
+ * created_at:
+ * type: string
+ * example: 2023-01-01T00:00:00Z
+ * assets:
+ * type: array
+ * items:
+ * type: object
+ * properties:
+ * name:
+ * type: string
+ * example: 2.5.0-linux-x64.tar.gz
+ * size:
+ * type: integer
+ * example: 1024
+ * download_count:
+ * type: integer
+ * example: 1024
+ * digest:
+ * type: string
+ * example: sha256:1234567890
+ * browser_download_url:
+ * type: string
+ * example: https://github.com/WonderLab/WonderLab/releases/download/2.5.0/2.5.0-linux-x64.tar.gz
+
+ * 404:
+ * description: 用户不存在
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 404
+ * message:
+ * type: string
+ * example: User not found
+ * 500:
+ * description: 服务器内部错误
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * status:
+ * type: string
+ * example: error
+ * code:
+ * type: integer
+ * example: 500
+ * message:
+ * type: string
+ * example: Internal Server Error, Please contact support
+ */
+router.get('/getLatestList', async (req, res) => {
+ try {
+ const { mirrorId, pageNum, pageSize } = req.query;
+
+ if (!mirrorId || !pageNum || !pageSize) {
+ res.status(400).json(ApiResponse.error(400, 'mirrorId, pageNum, pageSize are required'))
+ return
+ }
+
+ const parsedMirrorId = Number(mirrorId);
+ const parsedPageNum = Number(pageNum);
+ const parsedPageSize = Number(pageSize);
+
+ if (isNaN(parsedMirrorId) || isNaN(parsedPageNum) || isNaN(parsedPageSize)) {
+ res.status(400).json(ApiResponse.error(400, 'mirrorId, pageNum, pageSize must be number'))
+ return
+ }
+
+ const versions = await findVersionByMirror(parsedMirrorId, parsedPageNum, parsedPageSize);
+
+ if (!versions || versions.length === 0) {
+ res.status(404).json(ApiResponse.error(404, 'Not found version'))
+ return
+ }
+
+ res.status(200).json(ApiResponse.success(200, 'Get version list success', versions));
+ } catch (err: any) {
+ console.error('Interface processing exception:', err.stack || err);
+
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: ' + err.message))
+ return
+
+ }
+})
+
+router.post('/getLatest', clientMiddleware, async (req, res) => {
+ try {
+ console.log(req.body);
+
+ console.log();
+ const resp = {
+ latest: '2.5.0',
+ downloadUrl: 'https://github.com/WonderLab/WonderLab/releases/download/2.5.0/WonderLab-2.5.0.exe',
+ }
+ res.status(200).json(ApiResponse.success(200, 'This is a test response: Get latest version success', resp))
+ console.log(resp);
+
+ } catch (err) {
+ res.status(500).json(ApiResponse.error(500, 'Internal Server Error: Please contact support'))
+ console.log(err)
+ }
+})
+
+export default router;
\ No newline at end of file
diff --git a/src/server/utils/apiResp.ts b/src/server/utils/apiResp.ts
new file mode 100644
index 0000000..a3eeaae
--- /dev/null
+++ b/src/server/utils/apiResp.ts
@@ -0,0 +1,20 @@
+export default class ApiResponse {
+ constructor(
+ public code: number = 200,
+ public status: string = "success",
+ public message: string = "Request Success",
+ public data?: T
+ ) { }
+
+ static success(code: number, message: string): ApiResponse;
+ static success(code: number, message: string, data: T): ApiResponse;
+ static success(code: number = 200, message: string = "Request Success", data?: T): ApiResponse {
+ return new ApiResponse(code, "success", message, data);
+ }
+
+ static error(code: number, message: string): ApiResponse;
+ static error(code: number, message: string, data: T): ApiResponse;
+ static error(code: number = 500, message: string = "Internal Server Error", data?: T): ApiResponse {
+ return new ApiResponse(code, "error", message, data);
+ }
+}
\ No newline at end of file
diff --git a/src/server/utils/consoleLog.ts b/src/server/utils/consoleLog.ts
new file mode 100644
index 0000000..359c2d6
--- /dev/null
+++ b/src/server/utils/consoleLog.ts
@@ -0,0 +1,78 @@
+type LogType = 'info' | 'warn' | 'error';
+
+// ANSI 颜色转义序列(终端专用)
+const colors = {
+ reset: '\x1b[0m',
+ gray: '\x1b[90m', // 时间颜色
+ green: '\x1b[32m', // info/成功颜色
+ yellow: '\x1b[33m', // warn 颜色
+ red: '\x1b[31m', // error 颜色
+ blue: '\x1b[34m', // URL 颜色
+ cyan: '\x1b[36m', // 其他信息颜色
+ magenta: '\x1b[35m' // 错误详情颜色
+};
+
+const getTimeNow = () => {
+ const now = new Date();
+
+ let hours = now.getHours();
+ const minutes = now.getMinutes();
+ const seconds = now.getSeconds();
+
+ const period = hours >= 12 ? 'PM' : 'AM';
+
+ hours = hours % 12;
+ hours = hours ? hours : 12;
+
+ const formattedHours = hours.toString();
+ const formattedMinutes = minutes.toString().padStart(2, '0');
+ const formattedSeconds = seconds.toString().padStart(2, '0');
+
+ return `${formattedHours}:${formattedMinutes}:${formattedSeconds} ${period}`;
+};
+
+export default class ConsoleLog {
+ // 生成带颜色的文本
+ private static colorize(text: string, colorCode: string): string {
+ return `${colorCode}${text}${colors.reset}`;
+ }
+
+ // 通用日志方法
+ static log(message: string, type: LogType = 'info') {
+ const time = this.colorize(getTimeNow(), colors.gray);
+ let contentColor = colors.green; // 默认info用绿色
+
+ if (type === 'warn') contentColor = colors.yellow;
+ if (type === 'error') contentColor = colors.red;
+
+ const content = this.colorize(message, contentColor);
+ console.log(`${time} ${content}`);
+ }
+
+ // 警告日志
+ static warn(message: string) {
+ this.log(message, 'warn');
+ }
+
+ // 错误日志(支持同时输出消息和错误对象)
+ static error(message: string, error?: Error | unknown) {
+ const time = this.colorize(getTimeNow(), colors.gray);
+ const content = this.colorize(message, colors.red);
+
+ // 如果提供了错误对象,单独输出错误详情
+ if (error) {
+ console.log(`${time} ${content}`);
+ console.log(`${time} ${this.colorize('详情:', colors.magenta)}`, error);
+ } else {
+ console.log(`${time} ${content}`);
+ }
+ }
+
+ // URL信息日志(蓝色+下划线)
+ static url(message: string, url: string) {
+ const time = this.colorize(getTimeNow(), colors.gray);
+ const label = this.colorize(message, colors.cyan);
+ const link = this.colorize(url, '\x1b[34;4m'); // 蓝色+下划线
+ console.log(`${time} ${label} ${link}`);
+ }
+}
diff --git a/src/server/utils/swagger.ts b/src/server/utils/swagger.ts
new file mode 100644
index 0000000..6a3c9e5
--- /dev/null
+++ b/src/server/utils/swagger.ts
@@ -0,0 +1,74 @@
+import { fileURLToPath } from 'node:url'
+import path, { dirname } from 'node:path'
+import swaggerJsdoc from 'swagger-jsdoc'
+
+const __dirname = dirname(fileURLToPath(import.meta.url))
+
+const options: swaggerJsdoc.Options = {
+ definition: {
+ openapi: '3.0.0',
+ info: {
+ title: 'WonderLab Server Developer API Docs',
+ version: '1.0.0',
+ description: '前端开发请参照此文档'
+ },
+ servers: [
+ {
+ url: 'http://localhost:3000/api',
+ description: 'Development server',
+ },
+ ],
+ components: {
+ schemas: {
+ SuccessResponse: {
+ type: Object,
+ properties: {
+ status: { type: 'string', example: 'success' },
+ code: { type: 'integer', example: 200 },
+ message: { type: 'string', example: 'Operation successful' },
+ data: { type: 'object', nullable: true }
+ }
+ },
+ ErrorResponse: {
+ type: Object,
+ properties: {
+ status: { type: 'string', example: 'error' },
+ code: { type: 'integer', example: 400 },
+ message: { type: 'string', example: 'Error message' },
+ data: { type: 'object', nullable: true }
+ }
+ },
+ EquipmentInfo: {
+ type: Object,
+ properties: {
+ info_uuid: { type: 'string' },
+ bios_id: { type: 'string' },
+ country: { type: 'string' },
+ user_id: { type: 'integer' },
+ date: { type: 'string', format: 'date-time' }
+ }
+ },
+ EquipmentDisposition: {
+ type: Object,
+ properties: {
+ info_uuid: { type: 'string' },
+ equipment_uuid: { type: 'string' },
+ system_architecture: { type: 'string' },
+ system: { type: 'string' },
+ system_id: { type: 'string' },
+ ram_info: { type: 'string' },
+ date: { type: 'string', format: 'date-time' },
+ wonderlab_run_file: { type: 'string' },
+ wonderlab_v: { type: 'string' }
+ }
+ }
+ }
+ }
+ },
+ apis: [path.join(__dirname, '../routes/*.ts')],
+}
+
+const swaggerSpec = swaggerJsdoc(options);
+
+
+export default swaggerSpec;
\ No newline at end of file
diff --git a/src/utils/supabase.ts b/src/utils/supabase.ts
new file mode 100644
index 0000000..39e0af6
--- /dev/null
+++ b/src/utils/supabase.ts
@@ -0,0 +1,165 @@
+import { createClient } from '@supabase/supabase-js'
+
+// 从环境变量中获取Supabase配置
+// 支持 NEXT_PUBLIC_ 和 VITE_ 前缀的环境变量
+const supabaseUrl = import.meta.env.NEXT_PUBLIC_SUPABASE_URL ||
+ import.meta.env.VITE_SUPABASE_URL ||
+ process.env.NEXT_PUBLIC_SUPABASE_URL ||
+ process.env.VITE_SUPABASE_URL
+
+const supabaseAnonKey = import.meta.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ||
+ import.meta.env.VITE_SUPABASE_ANON_KEY ||
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ||
+ process.env.VITE_SUPABASE_ANON_KEY
+
+// 确保配置存在
+if (!supabaseUrl || !supabaseAnonKey) {
+ console.error('缺少Supabase配置。请确保环境变量已正确设置。')
+ console.log('当前环境变量:', import.meta.env)
+}
+
+// 调试信息
+console.log('Supabase URL:', supabaseUrl)
+
+// 创建Supabase客户端
+export const supabase = createClient(supabaseUrl || '', supabaseAnonKey || '', {
+ auth: {
+ persistSession: true,
+ autoRefreshToken: true,
+ detectSessionInUrl: true
+ },
+ global: {
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ fetch: (...args) => {
+ // 添加自定义的fetch处理,增加重试机制
+ return new Promise((resolve, reject) => {
+ const maxRetries = 3;
+ let retries = 0;
+
+ const attemptFetch = () => {
+ retries++;
+ console.log(`Supabase API 请求尝试 ${retries}/${maxRetries}`);
+
+ fetch(...args)
+ .then(resolve)
+ .catch((error) => {
+ console.error(`Supabase API 请求失败 (${retries}/${maxRetries}):`, error);
+
+ if (retries < maxRetries) {
+ console.log(`${500 * retries}ms 后重试...`);
+ setTimeout(attemptFetch, 500 * retries); // 指数退避
+ } else {
+ console.error('Supabase API 请求失败,已达到最大重试次数');
+ reject(error);
+ }
+ });
+ };
+
+ attemptFetch();
+ });
+ }
+ }
+})
+
+// 添加错误处理
+supabase.auth.onAuthStateChange((event, session) => {
+ console.log('Supabase 认证状态变更:', event)
+ if (event === 'SIGNED_OUT') {
+ console.log('用户已登出')
+ } else if (event === 'SIGNED_IN') {
+ console.log('用户已登录')
+ } else if (event === 'TOKEN_REFRESHED') {
+ console.log('令牌已刷新')
+ } else if (event === 'USER_UPDATED') {
+ console.log('用户信息已更新')
+ }
+})
+
+// 身份验证相关方法
+export const auth = {
+ // 使用邮箱和密码注册
+ signUp: async (email: string, password: string) => {
+ return await supabase.auth.signUp({
+ email,
+ password,
+ })
+ },
+
+ // 使用邮箱和密码登录
+ signIn: async (email: string, password: string) => {
+ return await supabase.auth.signInWithPassword({
+ email,
+ password,
+ })
+ },
+
+ // 退出登录
+ signOut: async () => {
+ return await supabase.auth.signOut()
+ },
+
+ // 获取当前用户
+ getUser: async () => {
+ return await supabase.auth.getUser()
+ },
+
+ // 获取会话
+ getSession: async () => {
+ return await supabase.auth.getSession()
+ },
+}
+
+// 数据库操作方法
+export const db = {
+ // 从表中获取数据
+ select: async (table: string, options?: any) => {
+ return await supabase.from(table).select(options?.columns || '*')
+ },
+
+ // 插入数据到表中
+ insert: async (table: string, data: any) => {
+ return await supabase.from(table).insert(data)
+ },
+
+ // 更新表中的数据
+ update: async (table: string, data: any, match: any) => {
+ return await supabase.from(table).update(data).match(match)
+ },
+
+ // 删除表中的数据
+ delete: async (table: string, match: any) => {
+ return await supabase.from(table).delete().match(match)
+ },
+}
+
+// 存储操作方法
+export const storage = {
+ // 上传文件
+ upload: async (bucket: string, path: string, file: File) => {
+ return await supabase.storage.from(bucket).upload(path, file)
+ },
+
+ // 下载文件
+ download: async (bucket: string, path: string) => {
+ return await supabase.storage.from(bucket).download(path)
+ },
+
+ // 获取文件公共URL
+ getPublicUrl: (bucket: string, path: string) => {
+ return supabase.storage.from(bucket).getPublicUrl(path)
+ },
+
+ // 删除文件
+ remove: async (bucket: string, paths: string[]) => {
+ return await supabase.storage.from(bucket).remove(paths)
+ },
+}
+
+export default {
+ client: supabase,
+ auth,
+ db,
+ storage,
+}
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..d5ee818
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "strict": true,
+ "jsx": "preserve",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "lib": ["ESNext", "DOM"],
+ "skipLibCheck": true,
+ "noEmit": true,
+ "forceConsistentCasingInFileNames": true,
+ "paths": {
+ "@/*": ["./src/client/*"]
+ },
+ "types": ["node"]
+ },
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..df39eb5
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,100 @@
+import path from 'node:path'
+import tailwindcss from '@tailwindcss/vite'
+import { defineConfig, loadEnv } from "vite"
+import vue from "@vitejs/plugin-vue"
+import { resolve } from 'path'
+import { copyFileSync, mkdirSync, existsSync } from 'fs'
+
+// https://vitejs.dev/config/
+export default defineConfig(({ mode }) => {
+ // 加载环境变量
+ const env = loadEnv(mode, process.cwd(), '')
+
+ return {
+ plugins: [
+ vue({
+ template: {
+ compilerOptions: {
+ // 将 spline-viewer 标记为自定义元素,避免 Vue 尝试解析它
+ isCustomElement: (tag) => tag === 'spline-viewer'
+ }
+ }
+ }),
+ tailwindcss(),
+ // 自定义插件,用于复制扩展必要文件到构建目录
+ {
+ name: 'copy-extension-files',
+ buildEnd() {
+ // 复制 manifest.json
+ copyFileSync('src/manifest.json', 'dist/manifest.json');
+ console.log('Manifest file copied to dist folder');
+
+ // 复制 background.js
+ copyFileSync('src/background.js', 'dist/background.js');
+ console.log('Background script copied to dist folder');
+
+ // 创建图标目录
+ if (!fs.existsSync('dist/icons')) {
+ fs.mkdirSync('dist/icons', { recursive: true });
+ }
+
+ // 复制图标文件(如果存在)
+ const iconSizes = [16, 32, 48, 128];
+ iconSizes.forEach(size => {
+ const iconPath = `src/icons/icon${size}.png`;
+ if (fs.existsSync(iconPath)) {
+ copyFileSync(iconPath, `dist/icons/icon${size}.png`);
+ console.log(`Icon ${size}px copied to dist folder`);
+ }
+ });
+ }
+ }
+ ],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src/client"),
+ },
+ },
+ server: {
+ port: 5173,
+ proxy: {
+ // 代理所有 /api 请求到后端
+ '/api': {
+ target: 'http://localhost:3001',
+ changeOrigin: true,
+ secure: false,
+ },
+ // 代理高德地图API请求
+ '/amap-api': {
+ target: 'https://restapi.amap.com',
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/amap-api/, ''),
+ secure: false,
+ },
+ },
+ allowedHosts: ['space.lingke.ink'],
+ },
+ build: {
+ // 浏览器扩展特定配置
+ outDir: 'dist',
+ emptyOutDir: true,
+ rollupOptions: {
+ input: {
+ main: resolve(__dirname, 'index.html')
+ },
+ output: {
+ entryFileNames: 'assets/[name].js',
+ chunkFileNames: 'assets/[name].js',
+ assetFileNames: 'assets/[name].[ext]'
+ }
+ }
+ },
+ define: {
+ __VUE_OPTIONS_API__: true,
+ __VUE_PROD_DEVTOOLS__: false,
+ // 确保环境变量可用于客户端
+ 'import.meta.env.NEXT_PUBLIC_SUPABASE_URL': JSON.stringify(env.NEXT_PUBLIC_SUPABASE_URL),
+ 'import.meta.env.NEXT_PUBLIC_SUPABASE_ANON_KEY': JSON.stringify(env.NEXT_PUBLIC_SUPABASE_ANON_KEY)
+ }
+ }
+});