From 88799ddee1abf19b3dc0b45e941079e2230de318 Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Wed, 9 Sep 2026 20:59:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(sync):=20=E6=B7=BB=E5=8A=A0=20Vue=20TypeSc?= =?UTF-8?q?ript=20=E7=AE=A1=E7=90=86=E6=8E=A7=E5=88=B6=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/ci.yml | 6 + .gitignore | 1 + frontend/src-tauri/build.rs | 1 + .../autogenerated/credentials_cleanup.toml | 11 + server sync/Dockerfile | 9 + server sync/README.md | 8 + server sync/console/index.html | 14 + server sync/console/package.json | 21 + server sync/console/pnpm-lock.yaml | 958 ++++++++++++++++++ server sync/console/src/App.vue | 250 +++++ server sync/console/src/api.ts | 140 +++ server sync/console/src/env.d.ts | 1 + server sync/console/src/main.ts | 5 + server sync/console/src/style.css | 175 ++++ server sync/console/tsconfig.json | 19 + server sync/console/vite.config.ts | 22 + server sync/sync_server/app.py | 23 + .../static/assets/index-BS5pfOkS.css | 1 + .../static/assets/index-CV7tuz7b.js | 17 + server sync/sync_server/static/index.html | 15 + server sync/tests/test_console.py | 63 ++ 21 files changed, 1760 insertions(+) create mode 100644 frontend/src-tauri/permissions/autogenerated/credentials_cleanup.toml create mode 100644 server sync/console/index.html create mode 100644 server sync/console/package.json create mode 100644 server sync/console/pnpm-lock.yaml create mode 100644 server sync/console/src/App.vue create mode 100644 server sync/console/src/api.ts create mode 100644 server sync/console/src/env.d.ts create mode 100644 server sync/console/src/main.ts create mode 100644 server sync/console/src/style.css create mode 100644 server sync/console/tsconfig.json create mode 100644 server sync/console/vite.config.ts create mode 100644 server sync/sync_server/static/assets/index-BS5pfOkS.css create mode 100644 server sync/sync_server/static/assets/index-CV7tuz7b.js create mode 100644 server sync/sync_server/static/index.html create mode 100644 server sync/tests/test_console.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 62a2a57..67d3873 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -38,6 +38,12 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: { python-version: "3.12" } + - uses: actions/setup-node@v4 + with: { node-version: "22", cache: pnpm, cache-dependency-path: "server sync/console/pnpm-lock.yaml" } + - run: corepack enable && corepack prepare pnpm@10.28.0 --activate + - run: pnpm install --frozen-lockfile && pnpm build + working-directory: server sync/console + - run: git diff --exit-code -- "server sync/sync_server/static" - run: pip install uv==0.9.24 - run: uv sync --frozen working-directory: backend diff --git a/.gitignore b/.gitignore index bfbca82..1b33737 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ Thumbs.db **/.pytest_cache/ server sync/.venv/ server sync/.env +server sync/console/node_modules/ community-server/.venv/ community-server/.env frontend/src-tauri/target/ diff --git a/frontend/src-tauri/build.rs b/frontend/src-tauri/build.rs index 6662aa6..bde0b03 100644 --- a/frontend/src-tauri/build.rs +++ b/frontend/src-tauri/build.rs @@ -43,6 +43,7 @@ fn main() { "credentials_lock", "credentials_change_password", "credentials_import", + "credentials_cleanup", "credentials_backup", "credentials_restore", "core_request", diff --git a/frontend/src-tauri/permissions/autogenerated/credentials_cleanup.toml b/frontend/src-tauri/permissions/autogenerated/credentials_cleanup.toml new file mode 100644 index 0000000..6e4fc9a --- /dev/null +++ b/frontend/src-tauri/permissions/autogenerated/credentials_cleanup.toml @@ -0,0 +1,11 @@ +# Automatically generated - DO NOT EDIT! + +[[permission]] +identifier = "allow-credentials-cleanup" +description = "Enables the credentials_cleanup command without any pre-configured scope." +commands.allow = ["credentials_cleanup"] + +[[permission]] +identifier = "deny-credentials-cleanup" +description = "Denies the credentials_cleanup command without any pre-configured scope." +commands.deny = ["credentials_cleanup"] diff --git a/server sync/Dockerfile b/server sync/Dockerfile index 7503f0b..a4c3ad1 100644 --- a/server sync/Dockerfile +++ b/server sync/Dockerfile @@ -1,9 +1,18 @@ +FROM node:22-alpine AS console +WORKDIR /console +RUN corepack enable +COPY console/package.json console/pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile +COPY console ./ +RUN pnpm build + FROM python:3.12-slim COPY --from=ghcr.io/astral-sh/uv:0.9.24 /uv /bin/uv WORKDIR /service COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-dev COPY sync_server ./sync_server +COPY --from=console /sync_server/static ./sync_server/static RUN useradd --uid 10001 --create-home opennexus && mkdir /staging && chown opennexus /staging USER 10001 ENV SYNC_STAGING_DIR=/staging diff --git a/server sync/README.md b/server sync/README.md index d169de9..c2bca56 100644 --- a/server sync/README.md +++ b/server sync/README.md @@ -2,6 +2,14 @@ 协议及限制见 [Sync v1](../docs/contracts/Sync-v1契约.md)。服务独立于 AI Core,生产入口仅支持 PostgreSQL 和 S3。当前尚未达到 M3 运维退出条件。 +服务根路径 `/` 与 `/console/` 提供同源的 Vue 3 + TypeScript Sync Console,可查看服务健康与依赖就绪状态,并使用普通 Sync 账户管理自己的 Vault 和设备。页面只调用公开的 Sync v1 API;密码在请求发出前从输入框清除,访问和刷新令牌只保留在页面内存,刷新或关闭页面即丢弃。控制台源码位于 `console/`,生产静态文件由 Docker 多阶段构建生成。 + +```powershell +cd console +pnpm install --frozen-lockfile +pnpm build +``` + ## 隔离测试 ```powershell diff --git a/server sync/console/index.html b/server sync/console/index.html new file mode 100644 index 0000000..ff101bd --- /dev/null +++ b/server sync/console/index.html @@ -0,0 +1,14 @@ + + + + + + + + OpenNexus Sync Console + + +
+ + + diff --git a/server sync/console/package.json b/server sync/console/package.json new file mode 100644 index 0000000..87a061b --- /dev/null +++ b/server sync/console/package.json @@ -0,0 +1,21 @@ +{ + "name": "opennexus-sync-console", + "private": true, + "version": "0.3.0-alpha.1", + "packageManager": "pnpm@10.28.0", + "type": "module", + "scripts": { + "build": "vue-tsc --noEmit && vite build", + "dev": "vite", + "type-check": "vue-tsc --noEmit" + }, + "dependencies": { + "vue": "3.5.21" + }, + "devDependencies": { + "@vitejs/plugin-vue": "5.2.4", + "typescript": "5.9.2", + "vite": "6.1.0", + "vue-tsc": "2.2.12" + } +} diff --git a/server sync/console/pnpm-lock.yaml b/server sync/console/pnpm-lock.yaml new file mode 100644 index 0000000..e6b6223 --- /dev/null +++ b/server sync/console/pnpm-lock.yaml @@ -0,0 +1,958 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + vue: + specifier: 3.5.21 + version: 3.5.21(typescript@5.9.2) + devDependencies: + '@vitejs/plugin-vue': + specifier: 5.2.4 + version: 5.2.4(vite@6.1.0)(vue@3.5.21(typescript@5.9.2)) + typescript: + specifier: 5.9.2 + version: 5.9.2 + vite: + specifier: 6.1.0 + version: 6.1.0 + vue-tsc: + specifier: 2.2.12 + version: 2.2.12(typescript@5.9.2) + +packages: + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-android-arm-eabi@4.63.1': + resolution: {integrity: sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.63.1': + resolution: {integrity: sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.63.1': + resolution: {integrity: sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.63.1': + resolution: {integrity: sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.63.1': + resolution: {integrity: sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.63.1': + resolution: {integrity: sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': + resolution: {integrity: sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.63.1': + resolution: {integrity: sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.63.1': + resolution: {integrity: sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.63.1': + resolution: {integrity: sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.63.1': + resolution: {integrity: sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.63.1': + resolution: {integrity: sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.63.1': + resolution: {integrity: sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.63.1': + resolution: {integrity: sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.63.1': + resolution: {integrity: sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.63.1': + resolution: {integrity: sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.63.1': + resolution: {integrity: sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.63.1': + resolution: {integrity: sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.63.1': + resolution: {integrity: sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.63.1': + resolution: {integrity: sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.63.1': + resolution: {integrity: sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.63.1': + resolution: {integrity: sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.63.1': + resolution: {integrity: sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.63.1': + resolution: {integrity: sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.63.1': + resolution: {integrity: sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==} + cpu: [x64] + os: [win32] + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@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.21': + resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==} + + '@vue/compiler-core@3.5.42': + resolution: {integrity: sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==} + + '@vue/compiler-dom@3.5.21': + resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==} + + '@vue/compiler-dom@3.5.42': + resolution: {integrity: sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==} + + '@vue/compiler-sfc@3.5.21': + resolution: {integrity: sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==} + + '@vue/compiler-ssr@3.5.21': + resolution: {integrity: sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/language-core@2.2.12': + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.21': + resolution: {integrity: sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==} + + '@vue/runtime-core@3.5.21': + resolution: {integrity: sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==} + + '@vue/runtime-dom@3.5.21': + resolution: {integrity: sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==} + + '@vue/server-renderer@3.5.21': + resolution: {integrity: sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==} + peerDependencies: + vue: 3.5.21 + + '@vue/shared@3.5.21': + resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==} + + '@vue/shared@3.5.42': + resolution: {integrity: sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==} + + alien-signals@1.0.13: + resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.1.4: + resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + rollup@4.63.1: + resolution: {integrity: sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + vite@6.1.0: + resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} + 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.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} + + vue-tsc@2.2.12: + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.21: + resolution: {integrity: sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + +snapshots: + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@esbuild/aix-ppc64@0.24.2': + optional: true + + '@esbuild/android-arm64@0.24.2': + optional: true + + '@esbuild/android-arm@0.24.2': + optional: true + + '@esbuild/android-x64@0.24.2': + optional: true + + '@esbuild/darwin-arm64@0.24.2': + optional: true + + '@esbuild/darwin-x64@0.24.2': + optional: true + + '@esbuild/freebsd-arm64@0.24.2': + optional: true + + '@esbuild/freebsd-x64@0.24.2': + optional: true + + '@esbuild/linux-arm64@0.24.2': + optional: true + + '@esbuild/linux-arm@0.24.2': + optional: true + + '@esbuild/linux-ia32@0.24.2': + optional: true + + '@esbuild/linux-loong64@0.24.2': + optional: true + + '@esbuild/linux-mips64el@0.24.2': + optional: true + + '@esbuild/linux-ppc64@0.24.2': + optional: true + + '@esbuild/linux-riscv64@0.24.2': + optional: true + + '@esbuild/linux-s390x@0.24.2': + optional: true + + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': + optional: true + + '@esbuild/netbsd-x64@0.24.2': + optional: true + + '@esbuild/openbsd-arm64@0.24.2': + optional: true + + '@esbuild/openbsd-x64@0.24.2': + optional: true + + '@esbuild/sunos-x64@0.24.2': + optional: true + + '@esbuild/win32-arm64@0.24.2': + optional: true + + '@esbuild/win32-ia32@0.24.2': + optional: true + + '@esbuild/win32-x64@0.24.2': + optional: true + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@rollup/rollup-android-arm-eabi@4.63.1': + optional: true + + '@rollup/rollup-android-arm64@4.63.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.63.1': + optional: true + + '@rollup/rollup-darwin-x64@4.63.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.63.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.63.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.63.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.63.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.63.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.63.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.63.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.63.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.63.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.63.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.63.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.63.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.63.1': + optional: true + + '@types/estree@1.0.9': {} + + '@vitejs/plugin-vue@5.2.4(vite@6.1.0)(vue@3.5.21(typescript@5.9.2))': + dependencies: + vite: 6.1.0 + vue: 3.5.21(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.2.0 + + '@vue/compiler-core@3.5.21': + dependencies: + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.21 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-core@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.42 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.21': + dependencies: + '@vue/compiler-core': 3.5.21 + '@vue/shared': 3.5.21 + + '@vue/compiler-dom@3.5.42': + dependencies: + '@vue/compiler-core': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/compiler-sfc@3.5.21': + dependencies: + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.21 + '@vue/compiler-dom': 3.5.21 + '@vue/compiler-ssr': 3.5.21 + '@vue/shared': 3.5.21 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.28 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.21': + dependencies: + '@vue/compiler-dom': 3.5.21 + '@vue/shared': 3.5.21 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/language-core@2.2.12(typescript@5.9.2)': + dependencies: + '@volar/language-core': 2.4.15 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.42 + alien-signals: 1.0.13 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.9.2 + + '@vue/reactivity@3.5.21': + dependencies: + '@vue/shared': 3.5.21 + + '@vue/runtime-core@3.5.21': + dependencies: + '@vue/reactivity': 3.5.21 + '@vue/shared': 3.5.21 + + '@vue/runtime-dom@3.5.21': + dependencies: + '@vue/reactivity': 3.5.21 + '@vue/runtime-core': 3.5.21 + '@vue/shared': 3.5.21 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.21(vue@3.5.21(typescript@5.9.2))': + dependencies: + '@vue/compiler-ssr': 3.5.21 + '@vue/shared': 3.5.21 + vue: 3.5.21(typescript@5.9.2) + + '@vue/shared@3.5.21': {} + + '@vue/shared@3.5.42': {} + + alien-signals@1.0.13: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.1.4: + dependencies: + balanced-match: 1.0.2 + + csstype@3.2.3: {} + + de-indent@1.0.2: {} + + entities@4.5.0: {} + + entities@7.0.1: {} + + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + + estree-walker@2.0.2: {} + + fsevents@2.3.3: + optional: true + + he@1.2.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.4 + + muggle-string@0.4.1: {} + + nanoid@3.3.18: {} + + path-browserify@1.0.1: {} + + picocolors@1.1.1: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rollup@4.63.1: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.63.1 + '@rollup/rollup-android-arm64': 4.63.1 + '@rollup/rollup-darwin-arm64': 4.63.1 + '@rollup/rollup-darwin-x64': 4.63.1 + '@rollup/rollup-freebsd-arm64': 4.63.1 + '@rollup/rollup-freebsd-x64': 4.63.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.63.1 + '@rollup/rollup-linux-arm-musleabihf': 4.63.1 + '@rollup/rollup-linux-arm64-gnu': 4.63.1 + '@rollup/rollup-linux-arm64-musl': 4.63.1 + '@rollup/rollup-linux-loong64-gnu': 4.63.1 + '@rollup/rollup-linux-loong64-musl': 4.63.1 + '@rollup/rollup-linux-ppc64-gnu': 4.63.1 + '@rollup/rollup-linux-ppc64-musl': 4.63.1 + '@rollup/rollup-linux-riscv64-gnu': 4.63.1 + '@rollup/rollup-linux-riscv64-musl': 4.63.1 + '@rollup/rollup-linux-s390x-gnu': 4.63.1 + '@rollup/rollup-linux-x64-gnu': 4.63.1 + '@rollup/rollup-linux-x64-musl': 4.63.1 + '@rollup/rollup-openbsd-x64': 4.63.1 + '@rollup/rollup-openharmony-arm64': 4.63.1 + '@rollup/rollup-win32-arm64-msvc': 4.63.1 + '@rollup/rollup-win32-ia32-msvc': 4.63.1 + '@rollup/rollup-win32-x64-gnu': 4.63.1 + '@rollup/rollup-win32-x64-msvc': 4.63.1 + fsevents: 2.3.3 + + source-map-js@1.2.1: {} + + typescript@5.9.2: {} + + vite@6.1.0: + dependencies: + esbuild: 0.24.2 + postcss: 8.5.28 + rollup: 4.63.1 + optionalDependencies: + fsevents: 2.3.3 + + vscode-uri@3.2.0: {} + + 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.21(typescript@5.9.2): + dependencies: + '@vue/compiler-dom': 3.5.21 + '@vue/compiler-sfc': 3.5.21 + '@vue/runtime-dom': 3.5.21 + '@vue/server-renderer': 3.5.21(vue@3.5.21(typescript@5.9.2)) + '@vue/shared': 3.5.21 + optionalDependencies: + typescript: 5.9.2 diff --git a/server sync/console/src/App.vue b/server sync/console/src/App.vue new file mode 100644 index 0000000..c33d3c3 --- /dev/null +++ b/server sync/console/src/App.vue @@ -0,0 +1,250 @@ + + + diff --git a/server sync/console/src/api.ts b/server sync/console/src/api.ts new file mode 100644 index 0000000..1644d29 --- /dev/null +++ b/server sync/console/src/api.ts @@ -0,0 +1,140 @@ +export interface ServiceStatus { + health: boolean + ready: boolean + protocol: number + maxObjectSize: number +} + +export interface Session { + access_token: string + refresh_token: string + expires_in: number + device_id: string +} + +export interface Vault { + id: string + name: string + sequence: number + used: number + quota: number +} + +export interface Device { + id: string + name: string + revoked: number | boolean +} + +interface ErrorPayload { + error?: { code?: string } +} + +async function safeJson(response: Response): Promise { + try { return await response.json() as T } + catch { return null } +} + +export class SyncApi { + private access = '' + private refresh = '' + deviceId = '' + + get signedIn() { return Boolean(this.access) } + + private async raw(path: string, init: RequestInit = {}, authenticate = true): Promise { + const headers = new Headers(init.headers) + if (init.body && !headers.has('Content-Type')) headers.set('Content-Type', 'application/json') + if (authenticate && this.access) headers.set('Authorization', `Bearer ${this.access}`) + return fetch(path, { ...init, headers, cache: 'no-store', credentials: 'same-origin' }) + } + + private async rotate(): Promise { + if (!this.refresh) return false + const response = await this.raw('/sync/v1/auth/refresh', { + method: 'POST', + body: JSON.stringify({ refresh_token: this.refresh }), + }, false) + if (!response.ok) return false + const session = await safeJson(response) + if (!session) return false + this.accept(session) + return true + } + + private async request(path: string, init: RequestInit = {}, allowRotate = true): Promise { + let response = await this.raw(path, init) + if (response.status === 401 && allowRotate && await this.rotate()) response = await this.raw(path, init) + if (!response.ok) { + const payload = await safeJson(response) + if (response.status === 401) this.clear() + throw new Error(payload?.error?.code ?? `HTTP_${response.status}`) + } + if (response.status === 204) return undefined as T + const payload = await safeJson(response) + if (payload === null) throw new Error('INVALID_RESPONSE') + return payload + } + + private accept(session: Session) { + this.access = session.access_token + this.refresh = session.refresh_token + this.deviceId = session.device_id + } + + async status(): Promise { + const [health, ready, handshake] = await Promise.allSettled([ + this.raw('/health', {}, false), + this.raw('/ready', {}, false), + this.raw('/sync/v1/handshake?protocol=1', {}, false), + ]) + let protocol = 1 + let maxObjectSize = 100 * 1024 * 1024 + if (handshake.status === 'fulfilled' && handshake.value.ok) { + const payload = await safeJson<{ protocol: number; max_object_size: number }>(handshake.value) + protocol = payload?.protocol ?? protocol + maxObjectSize = payload?.max_object_size ?? maxObjectSize + } + return { + health: health.status === 'fulfilled' && health.value.ok, + ready: ready.status === 'fulfilled' && ready.value.ok, + protocol, + maxObjectSize, + } + } + + async login(username: string, password: string, deviceName: string): Promise { + const response = await this.raw('/sync/v1/auth/sessions', { + method: 'POST', + body: JSON.stringify({ username, password, device_name: deviceName }), + }, false) + if (!response.ok) { + const payload = await safeJson(response) + throw new Error(payload?.error?.code ?? `HTTP_${response.status}`) + } + const session = await safeJson(response) + if (!session) throw new Error('INVALID_RESPONSE') + this.accept(session) + } + + vaults() { return this.request<{ items: Vault[] }>('/sync/v1/vaults') } + devices() { return this.request<{ items: Device[] }>('/sync/v1/devices') } + createVault(name: string) { + return this.request<{ vault_id: string; name: string }>('/sync/v1/vaults', { + method: 'POST', body: JSON.stringify({ name }), + }) + } + revokeDevice(id: string) { + return this.request(`/sync/v1/devices/${encodeURIComponent(id)}`, { method: 'DELETE' }) + } + async logout(): Promise { + try { + if (this.access) await this.request('/sync/v1/auth/sessions', { method: 'DELETE' }, false) + } finally { this.clear() } + } + clear() { + this.access = '' + this.refresh = '' + this.deviceId = '' + } +} diff --git a/server sync/console/src/env.d.ts b/server sync/console/src/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/server sync/console/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/server sync/console/src/main.ts b/server sync/console/src/main.ts new file mode 100644 index 0000000..fe5bae3 --- /dev/null +++ b/server sync/console/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import App from './App.vue' +import './style.css' + +createApp(App).mount('#app') diff --git a/server sync/console/src/style.css b/server sync/console/src/style.css new file mode 100644 index 0000000..882495f --- /dev/null +++ b/server sync/console/src/style.css @@ -0,0 +1,175 @@ +:root { + color-scheme: dark; + --bg: #06100d; + --surface: rgba(13, 29, 24, .84); + --surface-strong: #10241d; + --line: rgba(169, 222, 196, .15); + --line-strong: rgba(169, 222, 196, .28); + --text: #edf7f2; + --muted: #8fa99c; + --mint: #72e0ad; + --mint-bright: #a6f4cf; + --purple: #a991ff; + --danger: #ff8e8e; + --warning: #f5ca72; + --shadow: 0 26px 90px rgba(0, 0, 0, .34); + font-family: Inter, "Segoe UI", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; +} + +* { box-sizing: border-box; } +html { min-width: 320px; min-height: 100%; background: var(--bg); } +body { + margin: 0; + min-height: 100vh; + color: var(--text); + background: + linear-gradient(rgba(255,255,255,.018) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px), + radial-gradient(circle at 48% 0%, #15372c 0, var(--bg) 46%); + background-size: 52px 52px, 52px 52px, auto; + overflow-x: hidden; +} +button, input { font: inherit; } +button { color: inherit; } +button:focus-visible, input:focus-visible { outline: 2px solid var(--mint); outline-offset: 3px; } +button:disabled { cursor: default; opacity: .5; } + +.ambient { position: fixed; border-radius: 50%; filter: blur(80px); pointer-events: none; opacity: .16; } +.ambient-one { width: 420px; height: 420px; background: var(--mint); top: -220px; right: 8%; } +.ambient-two { width: 360px; height: 360px; background: var(--purple); bottom: -220px; left: -100px; opacity: .1; } +.topbar { + width: min(1280px, calc(100% - 48px)); + height: 88px; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--line); + position: relative; + z-index: 2; +} +.brand { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 13px; } +.brand > span:last-child { display: grid; gap: 1px; } +.brand strong { font-size: 17px; letter-spacing: -.02em; } +.brand small { color: var(--muted); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; } +.brand-mark { width: 34px; height: 34px; display: grid; place-items: center; position: relative; transform: rotate(30deg); } +.brand-mark i { position: absolute; display: block; border: 1.5px solid var(--mint); border-radius: 4px; } +.brand-mark i:nth-child(1) { width: 25px; height: 25px; opacity: .45; } +.brand-mark i:nth-child(2) { width: 17px; height: 17px; opacity: .72; } +.brand-mark i:nth-child(3) { width: 8px; height: 8px; background: var(--mint); box-shadow: 0 0 20px rgba(114,224,173,.8); } +.service-strip { display: flex; align-items: center; gap: 9px; } +.status-chip { min-height: 34px; padding: 0 12px; border: 1px solid var(--line); border-radius: 999px; display: flex; align-items: center; gap: 7px; background: rgba(7,18,15,.7); } +.status-chip i { width: 7px; height: 7px; border-radius: 50%; background: var(--warning); box-shadow: 0 0 12px currentColor; } +.status-chip b { color: var(--muted); font-size: 11px; font-weight: 500; } +.status-chip em { font-size: 11px; font-style: normal; } +.status-chip.ok i { background: var(--mint); } +.status-chip.bad i { background: var(--danger); } +.status-chip.bad em { color: var(--danger); } +.icon-button { width: 36px; height: 36px; border: 1px solid var(--line); border-radius: 10px; display: grid; place-items: center; background: rgba(14,31,25,.7); cursor: pointer; transition: .18s ease; } +.icon-button:hover { border-color: var(--line-strong); background: var(--surface-strong); transform: translateY(-1px); } +.icon-button svg { width: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } + +main { width: min(1180px, calc(100% - 48px)); margin: 0 auto; position: relative; z-index: 1; } +.hero { min-height: calc(100vh - 150px); display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(360px, .75fr); gap: clamp(48px, 8vw, 116px); align-items: center; padding: 64px 0 82px; } +.hero-copy { max-width: 680px; } +.eyebrow { color: var(--mint); font-size: 11px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; display: flex; align-items: center; gap: 10px; } +.eyebrow span { width: 24px; height: 1px; background: var(--mint); box-shadow: 0 0 10px var(--mint); } +.hero h1, .console-heading h1 { margin: 20px 0; letter-spacing: -.06em; line-height: .99; font-size: clamp(50px, 6.4vw, 86px); font-weight: 620; } +.hero h1 em { font-style: normal; color: transparent; background: linear-gradient(95deg, var(--mint-bright), #82d8c1 47%, var(--purple)); background-clip: text; -webkit-background-clip: text; } +.hero-copy > p { max-width: 620px; color: var(--muted); font-size: 16px; line-height: 1.85; } +.protocol-grid { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 42px; border: 1px solid var(--line); border-radius: 16px; overflow: hidden; background: rgba(8,22,17,.45); backdrop-filter: blur(15px); } +.protocol-grid article { padding: 19px 22px; display: grid; gap: 5px; border-right: 1px solid var(--line); } +.protocol-grid article:last-child { border-right: 0; } +.protocol-grid strong { font-size: 18px; font-weight: 600; } +.protocol-grid span { color: var(--muted); font-size: 11px; } +.login-card { padding: 31px; border: 1px solid var(--line-strong); border-radius: 24px; background: linear-gradient(145deg, rgba(18,40,32,.94), rgba(8,21,17,.9)); box-shadow: var(--shadow); position: relative; overflow: hidden; backdrop-filter: blur(25px); } +.card-glow { position: absolute; width: 210px; height: 210px; right: -100px; top: -120px; border-radius: 50%; background: var(--mint); filter: blur(60px); opacity: .16; } +.card-heading { display: flex; gap: 14px; align-items: center; margin-bottom: 28px; position: relative; } +.card-heading p, .surface-heading p { margin: 0 0 3px; color: var(--mint); font-size: 10px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; } +.card-heading h2, .surface-heading h2 { margin: 0; font-size: 21px; letter-spacing: -.025em; } +.lock-mark { width: 43px; height: 43px; border-radius: 13px; display: grid; place-items: center; background: rgba(114,224,173,.1); color: var(--mint); border: 1px solid rgba(114,224,173,.18); } +.lock-mark svg { width: 21px; fill: none; stroke: currentColor; stroke-width: 1.7; } +form { position: relative; } +.login-card form { display: grid; gap: 17px; } +label { display: grid; gap: 7px; color: var(--muted); font-size: 11px; font-weight: 600; letter-spacing: .04em; } +input { width: 100%; color: var(--text); background: rgba(3,12,9,.58); border: 1px solid var(--line); border-radius: 11px; padding: 12px 13px; transition: border-color .18s, background .18s; } +input::placeholder { color: #5f786c; } +input:hover, input:focus { border-color: rgba(114,224,173,.46); background: rgba(3,12,9,.8); } +.primary-button { width: 100%; margin-top: 5px; padding: 13px 16px; border: 0; border-radius: 11px; display: flex; justify-content: center; align-items: center; gap: 8px; color: #062015; background: linear-gradient(100deg, var(--mint-bright), var(--mint)); font-weight: 750; cursor: pointer; box-shadow: 0 10px 35px rgba(114,224,173,.16); transition: .18s ease; } +.primary-button:hover { transform: translateY(-1px); filter: brightness(1.05); } +.primary-button svg { width: 18px; fill: none; stroke: currentColor; stroke-width: 2; } +.privacy-note { margin: 18px 0 0; color: #718b7e; font-size: 10px; line-height: 1.5; display: flex; gap: 8px; } +.privacy-note span { color: var(--mint); font-size: 7px; margin-top: 3px; } + +.console-view { padding: 64px 0 90px; min-height: calc(100vh - 150px); } +.console-heading { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 38px; } +.console-heading h1 { font-size: clamp(42px, 5vw, 64px); margin: 13px 0 7px; } +.console-heading p { margin: 0; color: var(--muted); } +.secondary-button { padding: 10px 15px; border-radius: 10px; border: 1px solid var(--line-strong); background: transparent; cursor: pointer; } +.secondary-button:hover { border-color: var(--mint); color: var(--mint-bright); } +.metric-grid { display: grid; grid-template-columns: repeat(4, 1fr); border: 1px solid var(--line); background: rgba(8,22,17,.52); border-radius: 18px; overflow: hidden; margin-bottom: 22px; } +.metric-grid article { min-height: 134px; padding: 23px; display: flex; flex-direction: column; border-right: 1px solid var(--line); } +.metric-grid article:last-child { border-right: 0; } +.metric-grid span { color: var(--muted); font-size: 11px; } +.metric-grid strong { margin: 12px 0 8px; font-size: 27px; font-weight: 620; letter-spacing: -.04em; } +.metric-grid small { color: #668075; font-size: 10px; } +.content-grid { display: grid; grid-template-columns: 1.25fr .75fr; gap: 22px; } +.surface { border: 1px solid var(--line); border-radius: 18px; background: var(--surface); padding: 25px; backdrop-filter: blur(18px); } +.surface-heading { display: flex; justify-content: space-between; align-items: center; margin-bottom: 22px; } +.secure-badge { color: var(--mint); background: rgba(114,224,173,.08); border: 1px solid rgba(114,224,173,.18); padding: 6px 9px; border-radius: 999px; font-size: 9px; letter-spacing: .08em; } +.surface-intro { margin: -9px 0 19px; color: var(--muted); font-size: 12px; line-height: 1.65; } +.create-form { margin-bottom: 20px; padding: 16px; border-radius: 13px; background: rgba(4,15,11,.42); border: 1px solid var(--line); } +.create-form > div { display: flex; gap: 8px; margin-top: 8px; } +.create-form input { padding: 10px 12px; } +.create-form button { border: 0; border-radius: 9px; padding: 0 17px; color: #082016; background: var(--mint); font-weight: 700; cursor: pointer; } +.vault-list, .device-list { display: grid; gap: 9px; } +.vault-item, .device-item { min-width: 0; border: 1px solid var(--line); background: rgba(6,19,14,.48); border-radius: 13px; padding: 15px 16px; display: flex; align-items: center; gap: 13px; } +.vault-symbol, .device-symbol { width: 38px; height: 38px; flex: 0 0 auto; display: grid; place-items: center; border-radius: 11px; background: rgba(114,224,173,.08); color: var(--mint); font-weight: 700; } +.device-symbol { color: var(--purple); background: rgba(169,145,255,.08); } +.item-copy { min-width: 0; flex: 1; } +.item-copy strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; } +.item-copy small { color: var(--muted); font-size: 10px; } +progress { display: block; width: 100%; height: 4px; margin-top: 9px; border: 0; border-radius: 99px; overflow: hidden; accent-color: var(--mint); } +progress::-webkit-progress-bar { background: rgba(255,255,255,.06); } +progress::-webkit-progress-value { background: linear-gradient(90deg, var(--mint), var(--purple)); } +.item-side { text-align: right; flex: 0 0 auto; } +.item-side strong { display: block; font-size: 12px; } +.item-side small { color: var(--muted); font-size: 9px; } +.danger-button { border: 1px solid rgba(255,142,142,.25); color: var(--danger); background: rgba(255,142,142,.05); border-radius: 8px; padding: 7px 9px; cursor: pointer; font-size: 10px; } +.danger-button:hover { background: rgba(255,142,142,.12); } +.revoked { opacity: .48; } +.empty-state { min-height: 100px; display: grid; place-items: center; color: var(--muted); border: 1px dashed var(--line); border-radius: 12px; font-size: 12px; } +footer { width: min(1280px, calc(100% - 48px)); min-height: 62px; margin: 0 auto; border-top: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; color: #597166; font-size: 9px; letter-spacing: .04em; position: relative; z-index: 1; } +.toast { position: fixed; z-index: 10; right: 24px; bottom: 24px; max-width: min(420px, calc(100% - 48px)); padding: 13px 16px; border: 1px solid var(--line-strong); border-radius: 11px; background: #142b23; box-shadow: var(--shadow); font-size: 12px; } +.toast.error { color: #ffd2d2; border-color: rgba(255,142,142,.35); background: #321a1a; } + +@media (max-width: 920px) { + .hero { grid-template-columns: 1fr; padding-top: 76px; } + .hero-copy { max-width: none; } + .login-card { max-width: 560px; width: 100%; } + .content-grid { grid-template-columns: 1fr; } + .metric-grid { grid-template-columns: repeat(2, 1fr); } + .metric-grid article:nth-child(2) { border-right: 0; } + .metric-grid article:nth-child(-n+2) { border-bottom: 1px solid var(--line); } +} +@media (max-width: 620px) { + .topbar, main, footer { width: min(100% - 28px, 1180px); } + .topbar { height: 74px; } + .brand small, .status-chip b, footer span:last-child { display: none; } + .status-chip { padding: 0 9px; } + .hero { padding: 52px 0 64px; gap: 45px; } + .hero h1 { font-size: 43px; } + .hero-copy > p { font-size: 14px; } + .protocol-grid article { padding: 15px 11px; } + .protocol-grid strong { font-size: 14px; } + .login-card, .surface { padding: 21px; } + .console-heading { align-items: flex-start; gap: 18px; } + .metric-grid { grid-template-columns: 1fr; } + .metric-grid article { min-height: 116px; border-right: 0; border-bottom: 1px solid var(--line); } + .metric-grid article:last-child { border-bottom: 0; } + .create-form > div { flex-direction: column; } + .create-form button { padding: 10px; } +} +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; } +} diff --git a/server sync/console/tsconfig.json b/server sync/console/tsconfig.json new file mode 100644 index 0000000..b1da094 --- /dev/null +++ b/server sync/console/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts"] +} diff --git a/server sync/console/vite.config.ts b/server sync/console/vite.config.ts new file mode 100644 index 0000000..07801d4 --- /dev/null +++ b/server sync/console/vite.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + base: '/console/', + plugins: [vue()], + build: { + outDir: '../sync_server/static', + emptyOutDir: true, + sourcemap: false, + assetsInlineLimit: 0, + }, + server: { + port: 5174, + strictPort: true, + proxy: { + '/health': 'http://127.0.0.1:18081', + '/ready': 'http://127.0.0.1:18081', + '/sync': 'http://127.0.0.1:18081', + }, + }, +}) diff --git a/server sync/sync_server/app.py b/server sync/sync_server/app.py index 50bdf0a..89fd2c5 100644 --- a/server sync/sync_server/app.py +++ b/server sync/sync_server/app.py @@ -16,6 +16,7 @@ from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse, FileResponse from starlette.background import BackgroundTask from starlette.concurrency import run_in_threadpool +from starlette.staticfiles import StaticFiles from .database import Database, password_hash, row, rows, run from .models import Commit, Login, Refresh, Upload, VaultCreate @@ -69,6 +70,28 @@ def create_app(db: Database, objects, staging: Path, *, quota=1024**3, clock=tim app = FastAPI(title="OpenNexus Sync", version="1.0.0", lifespan=lifespan) app.state.database = db worker_id = secrets.token_hex(8) + console_root = Path(__file__).with_name("static") + + @app.middleware("http") + async def console_security(request: Request, call_next): + response = await call_next(request) + if request.url.path == "/" or request.url.path.startswith("/console"): + response.headers["Cache-Control"] = "no-store" + response.headers["Content-Security-Policy"] = ( + "default-src 'none'; script-src 'self'; style-src 'self'; " + "img-src 'self' data:; connect-src 'self'; font-src 'self'; " + "base-uri 'none'; form-action 'self'; frame-ancestors 'none'" + ) + response.headers["Referrer-Policy"] = "no-referrer" + response.headers["X-Content-Type-Options"] = "nosniff" + response.headers["X-Frame-Options"] = "DENY" + return response + + @app.get("/", include_in_schema=False) + def console(): + return FileResponse(console_root / "index.html", media_type="text/html; charset=utf-8") + + app.mount("/console", StaticFiles(directory=console_root, html=True), name="sync-console") @app.exception_handler(SyncError) async def error(_request, exc): diff --git a/server sync/sync_server/static/assets/index-BS5pfOkS.css b/server sync/sync_server/static/assets/index-BS5pfOkS.css new file mode 100644 index 0000000..bf1bd9f --- /dev/null +++ b/server sync/sync_server/static/assets/index-BS5pfOkS.css @@ -0,0 +1 @@ +:root{color-scheme:dark;--bg: #06100d;--surface: rgba(13, 29, 24, .84);--surface-strong: #10241d;--line: rgba(169, 222, 196, .15);--line-strong: rgba(169, 222, 196, .28);--text: #edf7f2;--muted: #8fa99c;--mint: #72e0ad;--mint-bright: #a6f4cf;--purple: #a991ff;--danger: #ff8e8e;--warning: #f5ca72;--shadow: 0 26px 90px rgba(0, 0, 0, .34);font-family:Inter,Segoe UI,PingFang SC,Microsoft YaHei,system-ui,sans-serif}*{box-sizing:border-box}html{min-width:320px;min-height:100%;background:var(--bg)}body{margin:0;min-height:100vh;color:var(--text);background:linear-gradient(rgba(255,255,255,.018) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.018) 1px,transparent 1px),radial-gradient(circle at 48% 0%,#15372c 0,var(--bg) 46%);background-size:52px 52px,52px 52px,auto;overflow-x:hidden}button,input{font:inherit}button{color:inherit}button:focus-visible,input:focus-visible{outline:2px solid var(--mint);outline-offset:3px}button:disabled{cursor:default;opacity:.5}.ambient{position:fixed;border-radius:50%;filter:blur(80px);pointer-events:none;opacity:.16}.ambient-one{width:420px;height:420px;background:var(--mint);top:-220px;right:8%}.ambient-two{width:360px;height:360px;background:var(--purple);bottom:-220px;left:-100px;opacity:.1}.topbar{width:min(1280px,calc(100% - 48px));height:88px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);position:relative;z-index:2}.brand{color:inherit;text-decoration:none;display:inline-flex;align-items:center;gap:13px}.brand>span:last-child{display:grid;gap:1px}.brand strong{font-size:17px;letter-spacing:-.02em}.brand small{color:var(--muted);font-size:11px;letter-spacing:.12em;text-transform:uppercase}.brand-mark{width:34px;height:34px;display:grid;place-items:center;position:relative;transform:rotate(30deg)}.brand-mark i{position:absolute;display:block;border:1.5px solid var(--mint);border-radius:4px}.brand-mark i:nth-child(1){width:25px;height:25px;opacity:.45}.brand-mark i:nth-child(2){width:17px;height:17px;opacity:.72}.brand-mark i:nth-child(3){width:8px;height:8px;background:var(--mint);box-shadow:0 0 20px #72e0adcc}.service-strip{display:flex;align-items:center;gap:9px}.status-chip{min-height:34px;padding:0 12px;border:1px solid var(--line);border-radius:999px;display:flex;align-items:center;gap:7px;background:#07120fb3}.status-chip i{width:7px;height:7px;border-radius:50%;background:var(--warning);box-shadow:0 0 12px currentColor}.status-chip b{color:var(--muted);font-size:11px;font-weight:500}.status-chip em{font-size:11px;font-style:normal}.status-chip.ok i{background:var(--mint)}.status-chip.bad i{background:var(--danger)}.status-chip.bad em{color:var(--danger)}.icon-button{width:36px;height:36px;border:1px solid var(--line);border-radius:10px;display:grid;place-items:center;background:#0e1f19b3;cursor:pointer;transition:.18s ease}.icon-button:hover{border-color:var(--line-strong);background:var(--surface-strong);transform:translateY(-1px)}.icon-button svg{width:17px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}main{width:min(1180px,calc(100% - 48px));margin:0 auto;position:relative;z-index:1}.hero{min-height:calc(100vh - 150px);display:grid;grid-template-columns:minmax(0,1.25fr) minmax(360px,.75fr);gap:clamp(48px,8vw,116px);align-items:center;padding:64px 0 82px}.hero-copy{max-width:680px}.eyebrow{color:var(--mint);font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;display:flex;align-items:center;gap:10px}.eyebrow span{width:24px;height:1px;background:var(--mint);box-shadow:0 0 10px var(--mint)}.hero h1,.console-heading h1{margin:20px 0;letter-spacing:-.06em;line-height:.99;font-size:clamp(50px,6.4vw,86px);font-weight:620}.hero h1 em{font-style:normal;color:transparent;background:linear-gradient(95deg,var(--mint-bright),#82d8c1 47%,var(--purple));background-clip:text;-webkit-background-clip:text}.hero-copy>p{max-width:620px;color:var(--muted);font-size:16px;line-height:1.85}.protocol-grid{display:grid;grid-template-columns:repeat(3,1fr);margin-top:42px;border:1px solid var(--line);border-radius:16px;overflow:hidden;background:#08161173;-webkit-backdrop-filter:blur(15px);backdrop-filter:blur(15px)}.protocol-grid article{padding:19px 22px;display:grid;gap:5px;border-right:1px solid var(--line)}.protocol-grid article:last-child{border-right:0}.protocol-grid strong{font-size:18px;font-weight:600}.protocol-grid span{color:var(--muted);font-size:11px}.login-card{padding:31px;border:1px solid var(--line-strong);border-radius:24px;background:linear-gradient(145deg,#122820f0,#081511e6);box-shadow:var(--shadow);position:relative;overflow:hidden;-webkit-backdrop-filter:blur(25px);backdrop-filter:blur(25px)}.card-glow{position:absolute;width:210px;height:210px;right:-100px;top:-120px;border-radius:50%;background:var(--mint);filter:blur(60px);opacity:.16}.card-heading{display:flex;gap:14px;align-items:center;margin-bottom:28px;position:relative}.card-heading p,.surface-heading p{margin:0 0 3px;color:var(--mint);font-size:10px;font-weight:700;letter-spacing:.13em;text-transform:uppercase}.card-heading h2,.surface-heading h2{margin:0;font-size:21px;letter-spacing:-.025em}.lock-mark{width:43px;height:43px;border-radius:13px;display:grid;place-items:center;background:#72e0ad1a;color:var(--mint);border:1px solid rgba(114,224,173,.18)}.lock-mark svg{width:21px;fill:none;stroke:currentColor;stroke-width:1.7}form{position:relative}.login-card form{display:grid;gap:17px}label{display:grid;gap:7px;color:var(--muted);font-size:11px;font-weight:600;letter-spacing:.04em}input{width:100%;color:var(--text);background:#030c0994;border:1px solid var(--line);border-radius:11px;padding:12px 13px;transition:border-color .18s,background .18s}input::placeholder{color:#5f786c}input:hover,input:focus{border-color:#72e0ad75;background:#030c09cc}.primary-button{width:100%;margin-top:5px;padding:13px 16px;border:0;border-radius:11px;display:flex;justify-content:center;align-items:center;gap:8px;color:#062015;background:linear-gradient(100deg,var(--mint-bright),var(--mint));font-weight:750;cursor:pointer;box-shadow:0 10px 35px #72e0ad29;transition:.18s ease}.primary-button:hover{transform:translateY(-1px);filter:brightness(1.05)}.primary-button svg{width:18px;fill:none;stroke:currentColor;stroke-width:2}.privacy-note{margin:18px 0 0;color:#718b7e;font-size:10px;line-height:1.5;display:flex;gap:8px}.privacy-note span{color:var(--mint);font-size:7px;margin-top:3px}.console-view{padding:64px 0 90px;min-height:calc(100vh - 150px)}.console-heading{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:38px}.console-heading h1{font-size:clamp(42px,5vw,64px);margin:13px 0 7px}.console-heading p{margin:0;color:var(--muted)}.secondary-button{padding:10px 15px;border-radius:10px;border:1px solid var(--line-strong);background:transparent;cursor:pointer}.secondary-button:hover{border-color:var(--mint);color:var(--mint-bright)}.metric-grid{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);background:#08161185;border-radius:18px;overflow:hidden;margin-bottom:22px}.metric-grid article{min-height:134px;padding:23px;display:flex;flex-direction:column;border-right:1px solid var(--line)}.metric-grid article:last-child{border-right:0}.metric-grid span{color:var(--muted);font-size:11px}.metric-grid strong{margin:12px 0 8px;font-size:27px;font-weight:620;letter-spacing:-.04em}.metric-grid small{color:#668075;font-size:10px}.content-grid{display:grid;grid-template-columns:1.25fr .75fr;gap:22px}.surface{border:1px solid var(--line);border-radius:18px;background:var(--surface);padding:25px;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.surface-heading{display:flex;justify-content:space-between;align-items:center;margin-bottom:22px}.secure-badge{color:var(--mint);background:#72e0ad14;border:1px solid rgba(114,224,173,.18);padding:6px 9px;border-radius:999px;font-size:9px;letter-spacing:.08em}.surface-intro{margin:-9px 0 19px;color:var(--muted);font-size:12px;line-height:1.65}.create-form{margin-bottom:20px;padding:16px;border-radius:13px;background:#040f0b6b;border:1px solid var(--line)}.create-form>div{display:flex;gap:8px;margin-top:8px}.create-form input{padding:10px 12px}.create-form button{border:0;border-radius:9px;padding:0 17px;color:#082016;background:var(--mint);font-weight:700;cursor:pointer}.vault-list,.device-list{display:grid;gap:9px}.vault-item,.device-item{min-width:0;border:1px solid var(--line);background:#06130e7a;border-radius:13px;padding:15px 16px;display:flex;align-items:center;gap:13px}.vault-symbol,.device-symbol{width:38px;height:38px;flex:0 0 auto;display:grid;place-items:center;border-radius:11px;background:#72e0ad14;color:var(--mint);font-weight:700}.device-symbol{color:var(--purple);background:#a991ff14}.item-copy{min-width:0;flex:1}.item-copy strong{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.item-copy small{color:var(--muted);font-size:10px}progress{display:block;width:100%;height:4px;margin-top:9px;border:0;border-radius:99px;overflow:hidden;accent-color:var(--mint)}progress::-webkit-progress-bar{background:#ffffff0f}progress::-webkit-progress-value{background:linear-gradient(90deg,var(--mint),var(--purple))}.item-side{text-align:right;flex:0 0 auto}.item-side strong{display:block;font-size:12px}.item-side small{color:var(--muted);font-size:9px}.danger-button{border:1px solid rgba(255,142,142,.25);color:var(--danger);background:#ff8e8e0d;border-radius:8px;padding:7px 9px;cursor:pointer;font-size:10px}.danger-button:hover{background:#ff8e8e1f}.revoked{opacity:.48}.empty-state{min-height:100px;display:grid;place-items:center;color:var(--muted);border:1px dashed var(--line);border-radius:12px;font-size:12px}footer{width:min(1280px,calc(100% - 48px));min-height:62px;margin:0 auto;border-top:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;color:#597166;font-size:9px;letter-spacing:.04em;position:relative;z-index:1}.toast{position:fixed;z-index:10;right:24px;bottom:24px;max-width:min(420px,calc(100% - 48px));padding:13px 16px;border:1px solid var(--line-strong);border-radius:11px;background:#142b23;box-shadow:var(--shadow);font-size:12px}.toast.error{color:#ffd2d2;border-color:#ff8e8e59;background:#321a1a}@media (max-width: 920px){.hero{grid-template-columns:1fr;padding-top:76px}.hero-copy{max-width:none}.login-card{max-width:560px;width:100%}.content-grid{grid-template-columns:1fr}.metric-grid{grid-template-columns:repeat(2,1fr)}.metric-grid article:nth-child(2){border-right:0}.metric-grid article:nth-child(-n+2){border-bottom:1px solid var(--line)}}@media (max-width: 620px){.topbar,main,footer{width:min(100% - 28px,1180px)}.topbar{height:74px}.brand small,.status-chip b,footer span:last-child{display:none}.status-chip{padding:0 9px}.hero{padding:52px 0 64px;gap:45px}.hero h1{font-size:43px}.hero-copy>p{font-size:14px}.protocol-grid article{padding:15px 11px}.protocol-grid strong{font-size:14px}.login-card,.surface{padding:21px}.console-heading{align-items:flex-start;gap:18px}.metric-grid{grid-template-columns:1fr}.metric-grid article{min-height:116px;border-right:0;border-bottom:1px solid var(--line)}.metric-grid article:last-child{border-bottom:0}.create-form>div{flex-direction:column}.create-form button{padding:10px}}@media (prefers-reduced-motion: reduce){*,*:before,*:after{scroll-behavior:auto!important;transition:none!important}} diff --git a/server sync/sync_server/static/assets/index-CV7tuz7b.js b/server sync/sync_server/static/assets/index-CV7tuz7b.js new file mode 100644 index 0000000..2d0c085 --- /dev/null +++ b/server sync/sync_server/static/assets/index-CV7tuz7b.js @@ -0,0 +1,17 @@ +var zr=Object.defineProperty;var Yr=(e,t,s)=>t in e?zr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s;var jt=(e,t,s)=>Yr(e,typeof t!="symbol"?t+"":t,s);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))n(r);new MutationObserver(r=>{for(const i of r)if(i.type==="childList")for(const l of i.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&n(l)}).observe(document,{childList:!0,subtree:!0});function s(r){const i={};return r.integrity&&(i.integrity=r.integrity),r.referrerPolicy&&(i.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?i.credentials="include":r.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(r){if(r.ep)return;r.ep=!0;const i=s(r);fetch(r.href,i)}})();/** +* @vue/shared v3.5.21 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function js(e){const t=Object.create(null);for(const s of e.split(","))t[s]=1;return s=>s in t}const K={},ut=[],Fe=()=>{},Vn=()=>!1,is=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Vs=e=>e.startsWith("onUpdate:"),ne=Object.assign,Us=(e,t)=>{const s=e.indexOf(t);s>-1&&e.splice(s,1)},Xr=Object.prototype.hasOwnProperty,V=(e,t)=>Xr.call(e,t),R=Array.isArray,at=e=>ls(e)==="[object Map]",Un=e=>ls(e)==="[object Set]",F=e=>typeof e=="function",Y=e=>typeof e=="string",Ye=e=>typeof e=="symbol",J=e=>e!==null&&typeof e=="object",Bn=e=>(J(e)||F(e))&&F(e.then)&&F(e.catch),Kn=Object.prototype.toString,ls=e=>Kn.call(e),Zr=e=>ls(e).slice(8,-1),Wn=e=>ls(e)==="[object Object]",Bs=e=>Y(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,St=js(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),os=e=>{const t=Object.create(null);return s=>t[s]||(t[s]=e(s))},Qr=/-\w/g,Je=os(e=>e.replace(Qr,t=>t.slice(1).toUpperCase())),ei=/\B([A-Z])/g,rt=os(e=>e.replace(ei,"-$1").toLowerCase()),qn=os(e=>e.charAt(0).toUpperCase()+e.slice(1)),vs=os(e=>e?`on${qn(e)}`:""),ke=(e,t)=>!Object.is(e,t),kt=(e,...t)=>{for(let s=0;s{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:n,value:s})},As=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let cn;const cs=()=>cn||(cn=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Ks(e){if(R(e)){const t={};for(let s=0;s{if(s){const n=s.split(si);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function st(e){let t="";if(Y(e))t=e;else if(R(e))for(let s=0;s!!(e&&e.__v_isRef===!0),G=e=>Y(e)?e:e==null?"":R(e)||J(e)&&(e.toString===Kn||!F(e.toString))?Gn(e)?G(e.value):JSON.stringify(e,zn,2):String(e),zn=(e,t)=>Gn(t)?zn(e,t.value):at(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((s,[n,r],i)=>(s[bs(n,i)+" =>"]=r,s),{})}:Un(t)?{[`Set(${t.size})`]:[...t.values()].map(s=>bs(s))}:Ye(t)?bs(t):J(t)&&!R(t)&&!Wn(t)?String(t):t,bs=(e,t="")=>{var s;return Ye(e)?`Symbol(${(s=e.description)!=null?s:t})`:e};/** +* @vue/reactivity v3.5.21 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let fe;class oi{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=fe,!t&&fe&&(this.index=(fe.scopes||(fe.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,s;if(this.scopes)for(t=0,s=this.scopes.length;t0&&--this._on===0&&(fe=this.prevScope,this.prevScope=void 0)}stop(t){if(this._active){this._active=!1;let s,n;for(s=0,n=this.effects.length;s0)return;if(Tt){let t=Tt;for(Tt=void 0;t;){const s=t.next;t.next=void 0,t.flags&=-9,t=s}}let e;for(;Et;){let t=Et;for(Et=void 0;t;){const s=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(n){e||(e=n)}t=s}}if(e)throw e}function Qn(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function er(e){let t,s=e.depsTail,n=s;for(;n;){const r=n.prevDep;n.version===-1?(n===s&&(s=r),ks(n),fi(n)):t=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=r}e.deps=t,e.depsTail=s}function Ps(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(tr(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function tr(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===It)||(e.globalVersion=It,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Ps(e))))return;e.flags|=2;const t=e.dep,s=q,n=we;q=e,we=!0;try{Qn(e);const r=e.fn(e._value);(t.version===0||ke(r,e._value))&&(e.flags|=128,e._value=r,t.version++)}catch(r){throw t.version++,r}finally{q=s,we=n,er(e),e.flags&=-3}}function ks(e,t=!1){const{dep:s,prevSub:n,nextSub:r}=e;if(n&&(n.nextSub=r,e.prevSub=void 0),r&&(r.prevSub=n,e.nextSub=void 0),s.subs===e&&(s.subs=n,!n&&s.computed)){s.computed.flags&=-5;for(let i=s.computed.deps;i;i=i.nextDep)ks(i,!0)}!t&&!--s.sc&&s.map&&s.map.delete(s.key)}function fi(e){const{prevDep:t,nextDep:s}=e;t&&(t.nextDep=s,e.prevDep=void 0),s&&(s.prevDep=t,e.nextDep=void 0)}let we=!0;const sr=[];function Ve(){sr.push(we),we=!1}function Ue(){const e=sr.pop();we=e===void 0?!0:e}function fn(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const s=q;q=void 0;try{t()}finally{q=s}}}let It=0;class ui{constructor(t,s){this.sub=t,this.dep=s,this.version=s.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class Js{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!q||!we||q===this.computed)return;let s=this.activeLink;if(s===void 0||s.sub!==q)s=this.activeLink=new ui(q,this),q.deps?(s.prevDep=q.depsTail,q.depsTail.nextDep=s,q.depsTail=s):q.deps=q.depsTail=s,nr(s);else if(s.version===-1&&(s.version=this.version,s.nextDep)){const n=s.nextDep;n.prevDep=s.prevDep,s.prevDep&&(s.prevDep.nextDep=n),s.prevDep=q.depsTail,s.nextDep=void 0,q.depsTail.nextDep=s,q.depsTail=s,q.deps===s&&(q.deps=n)}return s}trigger(t){this.version++,It++,this.notify(t)}notify(t){Ws();try{for(let s=this.subs;s;s=s.prevSub)s.sub.notify()&&s.sub.dep.notify()}finally{qs()}}}function nr(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let n=t.deps;n;n=n.nextDep)nr(n)}const s=e.dep.subs;s!==e&&(e.prevSub=s,s&&(s.nextSub=e)),e.dep.subs=e}}const Is=new WeakMap,nt=Symbol(""),Ms=Symbol(""),Mt=Symbol("");function te(e,t,s){if(we&&q){let n=Is.get(e);n||Is.set(e,n=new Map);let r=n.get(s);r||(n.set(s,r=new Js),r.map=n,r.key=s),r.track()}}function He(e,t,s,n,r,i){const l=Is.get(e);if(!l){It++;return}const o=f=>{f&&f.trigger()};if(Ws(),t==="clear")l.forEach(o);else{const f=R(e),p=f&&Bs(s);if(f&&s==="length"){const a=Number(n);l.forEach((m,E)=>{(E==="length"||E===Mt||!Ye(E)&&E>=a)&&o(m)})}else switch((s!==void 0||l.has(void 0))&&o(l.get(s)),p&&o(l.get(Mt)),t){case"add":f?p&&o(l.get("length")):(o(l.get(nt)),at(e)&&o(l.get(Ms)));break;case"delete":f||(o(l.get(nt)),at(e)&&o(l.get(Ms)));break;case"set":at(e)&&o(l.get(nt));break}}qs()}function lt(e){const t=j(e);return t===e?t:(te(t,"iterate",Mt),ye(e)?t:t.map(ee))}function fs(e){return te(e=j(e),"iterate",Mt),e}const ai={__proto__:null,[Symbol.iterator](){return ys(this,Symbol.iterator,ee)},concat(...e){return lt(this).concat(...e.map(t=>R(t)?lt(t):t))},entries(){return ys(this,"entries",e=>(e[1]=ee(e[1]),e))},every(e,t){return Le(this,"every",e,t,void 0,arguments)},filter(e,t){return Le(this,"filter",e,t,s=>s.map(ee),arguments)},find(e,t){return Le(this,"find",e,t,ee,arguments)},findIndex(e,t){return Le(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return Le(this,"findLast",e,t,ee,arguments)},findLastIndex(e,t){return Le(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return Le(this,"forEach",e,t,void 0,arguments)},includes(...e){return xs(this,"includes",e)},indexOf(...e){return xs(this,"indexOf",e)},join(e){return lt(this).join(e)},lastIndexOf(...e){return xs(this,"lastIndexOf",e)},map(e,t){return Le(this,"map",e,t,void 0,arguments)},pop(){return yt(this,"pop")},push(...e){return yt(this,"push",e)},reduce(e,...t){return un(this,"reduce",e,t)},reduceRight(e,...t){return un(this,"reduceRight",e,t)},shift(){return yt(this,"shift")},some(e,t){return Le(this,"some",e,t,void 0,arguments)},splice(...e){return yt(this,"splice",e)},toReversed(){return lt(this).toReversed()},toSorted(e){return lt(this).toSorted(e)},toSpliced(...e){return lt(this).toSpliced(...e)},unshift(...e){return yt(this,"unshift",e)},values(){return ys(this,"values",ee)}};function ys(e,t,s){const n=fs(e),r=n[t]();return n!==e&&!ye(e)&&(r._next=r.next,r.next=()=>{const i=r._next();return i.value&&(i.value=s(i.value)),i}),r}const di=Array.prototype;function Le(e,t,s,n,r,i){const l=fs(e),o=l!==e&&!ye(e),f=l[t];if(f!==di[t]){const m=f.apply(e,i);return o?ee(m):m}let p=s;l!==e&&(o?p=function(m,E){return s.call(this,ee(m),E,e)}:s.length>2&&(p=function(m,E){return s.call(this,m,E,e)}));const a=f.call(l,p,n);return o&&r?r(a):a}function un(e,t,s,n){const r=fs(e);let i=s;return r!==e&&(ye(e)?s.length>3&&(i=function(l,o,f){return s.call(this,l,o,f,e)}):i=function(l,o,f){return s.call(this,l,ee(o),f,e)}),r[t](i,...n)}function xs(e,t,s){const n=j(e);te(n,"iterate",Mt);const r=n[t](...s);return(r===-1||r===!1)&&Ys(s[0])?(s[0]=j(s[0]),n[t](...s)):r}function yt(e,t,s=[]){Ve(),Ws();const n=j(e)[t].apply(e,s);return qs(),Ue(),n}const hi=js("__proto__,__v_isRef,__isVue"),rr=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Ye));function pi(e){Ye(e)||(e=String(e));const t=j(this);return te(t,"has",e),t.hasOwnProperty(e)}class ir{constructor(t=!1,s=!1){this._isReadonly=t,this._isShallow=s}get(t,s,n){if(s==="__v_skip")return t.__v_skip;const r=this._isReadonly,i=this._isShallow;if(s==="__v_isReactive")return!r;if(s==="__v_isReadonly")return r;if(s==="__v_isShallow")return i;if(s==="__v_raw")return n===(r?i?Ei:fr:i?cr:or).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(n)?t:void 0;const l=R(t);if(!r){let f;if(l&&(f=ai[s]))return f;if(s==="hasOwnProperty")return pi}const o=Reflect.get(t,s,se(t)?t:n);return(Ye(s)?rr.has(s):hi(s))||(r||te(t,"get",s),i)?o:se(o)?l&&Bs(s)?o:o.value:J(o)?r?ur(o):us(o):o}}class lr extends ir{constructor(t=!1){super(!1,t)}set(t,s,n,r){let i=t[s];if(!this._isShallow){const f=Ge(i);if(!ye(n)&&!Ge(n)&&(i=j(i),n=j(n)),!R(t)&&se(i)&&!se(n))return f||(i.value=n),!0}const l=R(t)&&Bs(s)?Number(s)e,Vt=e=>Reflect.getPrototypeOf(e);function _i(e,t,s){return function(...n){const r=this.__v_raw,i=j(r),l=at(i),o=e==="entries"||e===Symbol.iterator&&l,f=e==="keys"&&l,p=r[e](...n),a=s?Rs:t?Zt:ee;return!t&&te(i,"iterate",f?Ms:nt),{next(){const{value:m,done:E}=p.next();return E?{value:m,done:E}:{value:o?[a(m[0]),a(m[1])]:a(m),done:E}},[Symbol.iterator](){return this}}}}function Ut(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function yi(e,t){const s={get(r){const i=this.__v_raw,l=j(i),o=j(r);e||(ke(r,o)&&te(l,"get",r),te(l,"get",o));const{has:f}=Vt(l),p=t?Rs:e?Zt:ee;if(f.call(l,r))return p(i.get(r));if(f.call(l,o))return p(i.get(o));i!==l&&i.get(r)},get size(){const r=this.__v_raw;return!e&&te(j(r),"iterate",nt),r.size},has(r){const i=this.__v_raw,l=j(i),o=j(r);return e||(ke(r,o)&&te(l,"has",r),te(l,"has",o)),r===o?i.has(r):i.has(r)||i.has(o)},forEach(r,i){const l=this,o=l.__v_raw,f=j(o),p=t?Rs:e?Zt:ee;return!e&&te(f,"iterate",nt),o.forEach((a,m)=>r.call(i,p(a),p(m),l))}};return ne(s,e?{add:Ut("add"),set:Ut("set"),delete:Ut("delete"),clear:Ut("clear")}:{add(r){!t&&!ye(r)&&!Ge(r)&&(r=j(r));const i=j(this);return Vt(i).has.call(i,r)||(i.add(r),He(i,"add",r,r)),this},set(r,i){!t&&!ye(i)&&!Ge(i)&&(i=j(i));const l=j(this),{has:o,get:f}=Vt(l);let p=o.call(l,r);p||(r=j(r),p=o.call(l,r));const a=f.call(l,r);return l.set(r,i),p?ke(i,a)&&He(l,"set",r,i):He(l,"add",r,i),this},delete(r){const i=j(this),{has:l,get:o}=Vt(i);let f=l.call(i,r);f||(r=j(r),f=l.call(i,r)),o&&o.call(i,r);const p=i.delete(r);return f&&He(i,"delete",r,void 0),p},clear(){const r=j(this),i=r.size!==0,l=r.clear();return i&&He(r,"clear",void 0,void 0),l}}),["keys","values","entries",Symbol.iterator].forEach(r=>{s[r]=_i(r,e,t)}),s}function Gs(e,t){const s=yi(e,t);return(n,r,i)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?n:Reflect.get(V(s,r)&&r in n?s:n,r,i)}const xi={get:Gs(!1,!1)},wi={get:Gs(!1,!0)},Si={get:Gs(!0,!1)};const or=new WeakMap,cr=new WeakMap,fr=new WeakMap,Ei=new WeakMap;function Ti(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Ci(e){return e.__v_skip||!Object.isExtensible(e)?0:Ti(Zr(e))}function us(e){return Ge(e)?e:zs(e,!1,mi,xi,or)}function Oi(e){return zs(e,!1,bi,wi,cr)}function ur(e){return zs(e,!0,vi,Si,fr)}function zs(e,t,s,n,r){if(!J(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=Ci(e);if(i===0)return e;const l=r.get(e);if(l)return l;const o=new Proxy(e,i===2?n:s);return r.set(e,o),o}function dt(e){return Ge(e)?dt(e.__v_raw):!!(e&&e.__v_isReactive)}function Ge(e){return!!(e&&e.__v_isReadonly)}function ye(e){return!!(e&&e.__v_isShallow)}function Ys(e){return e?!!e.__v_raw:!1}function j(e){const t=e&&e.__v_raw;return t?j(t):e}function Ai(e){return!V(e,"__v_skip")&&Object.isExtensible(e)&&kn(e,"__v_skip",!0),e}const ee=e=>J(e)?us(e):e,Zt=e=>J(e)?ur(e):e;function se(e){return e?e.__v_isRef===!0:!1}function ve(e){return Pi(e,!1)}function Pi(e,t){return se(e)?e:new Ii(e,t)}class Ii{constructor(t,s){this.dep=new Js,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=s?t:j(t),this._value=s?t:ee(t),this.__v_isShallow=s}get value(){return this.dep.track(),this._value}set value(t){const s=this._rawValue,n=this.__v_isShallow||ye(t)||Ge(t);t=n?t:j(t),ke(t,s)&&(this._rawValue=t,this._value=n?t:ee(t),this.dep.trigger())}}function ar(e){return se(e)?e.value:e}const Mi={get:(e,t,s)=>t==="__v_raw"?e:ar(Reflect.get(e,t,s)),set:(e,t,s,n)=>{const r=e[t];return se(r)&&!se(s)?(r.value=s,!0):Reflect.set(e,t,s,n)}};function dr(e){return dt(e)?e:new Proxy(e,Mi)}class Ri{constructor(t,s,n){this.fn=t,this.setter=s,this._value=void 0,this.dep=new Js(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=It-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!s,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&q!==this)return Zn(this,!0),!0}get value(){const t=this.dep.track();return tr(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function Fi(e,t,s=!1){let n,r;return F(e)?n=e:(n=e.get,r=e.set),new Ri(n,r,s)}const Bt={},Qt=new WeakMap;let tt;function Di(e,t=!1,s=tt){if(s){let n=Qt.get(s);n||Qt.set(s,n=[]),n.push(e)}}function Ni(e,t,s=K){const{immediate:n,deep:r,once:i,scheduler:l,augmentJob:o,call:f}=s,p=P=>r?P:ye(P)||r===!1||r===0?je(P,1):je(P);let a,m,E,C,N=!1,L=!1;if(se(e)?(m=()=>e.value,N=ye(e)):dt(e)?(m=()=>p(e),N=!0):R(e)?(L=!0,N=e.some(P=>dt(P)||ye(P)),m=()=>e.map(P=>{if(se(P))return P.value;if(dt(P))return p(P);if(F(P))return f?f(P,2):P()})):F(e)?t?m=f?()=>f(e,2):e:m=()=>{if(E){Ve();try{E()}finally{Ue()}}const P=tt;tt=a;try{return f?f(e,3,[C]):e(C)}finally{tt=P}}:m=Fe,t&&r){const P=m,z=r===!0?1/0:r;m=()=>je(P(),z)}const Z=ci(),H=()=>{a.stop(),Z&&Z.active&&Us(Z.effects,a)};if(i&&t){const P=t;t=(...z)=>{P(...z),H()}}let B=L?new Array(e.length).fill(Bt):Bt;const k=P=>{if(!(!(a.flags&1)||!a.dirty&&!P))if(t){const z=a.run();if(r||N||(L?z.some((Se,ge)=>ke(Se,B[ge])):ke(z,B))){E&&E();const Se=tt;tt=a;try{const ge=[z,B===Bt?void 0:L&&B[0]===Bt?[]:B,C];B=z,f?f(t,3,ge):t(...ge)}finally{tt=Se}}}else a.run()};return o&&o(k),a=new Yn(m),a.scheduler=l?()=>l(k,!1):k,C=P=>Di(P,!1,a),E=a.onStop=()=>{const P=Qt.get(a);if(P){if(f)f(P,4);else for(const z of P)z();Qt.delete(a)}},t?n?k(!0):B=a.run():l?l(k.bind(null,!0),!0):a.run(),H.pause=a.pause.bind(a),H.resume=a.resume.bind(a),H.stop=H,H}function je(e,t=1/0,s){if(t<=0||!J(e)||e.__v_skip||(s=s||new Map,(s.get(e)||0)>=t))return e;if(s.set(e,t),t--,se(e))je(e.value,t,s);else if(R(e))for(let n=0;n{je(n,t,s)});else if(Wn(e)){for(const n in e)je(e[n],t,s);for(const n of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,n)&&je(e[n],t,s)}return e}/** +* @vue/runtime-core v3.5.21 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function Nt(e,t,s,n){try{return n?e(...n):e()}catch(r){as(r,t,s)}}function Ne(e,t,s,n){if(F(e)){const r=Nt(e,t,s,n);return r&&Bn(r)&&r.catch(i=>{as(i,t,s)}),r}if(R(e)){const r=[];for(let i=0;i>>1,r=ie[n],i=Rt(r);i=Rt(s)?ie.push(e):ie.splice(Hi(t),0,e),e.flags|=1,pr()}}function pr(){es||(es=hr.then(mr))}function ji(e){R(e)?ht.push(...e):We&&e.id===-1?We.splice(ct+1,0,e):e.flags&1||(ht.push(e),e.flags|=1),pr()}function an(e,t,s=Me+1){for(;sRt(s)-Rt(n));if(ht.length=0,We){We.push(...t);return}for(We=t,ct=0;cte.id==null?e.flags&2?-1:1/0:e.id;function mr(e){try{for(Me=0;Me{n._d&&wn(-1);const i=ts(t);let l;try{l=e(...r)}finally{ts(i),n._d&&wn(1)}return l};return n._n=!0,n._c=!0,n._d=!0,n}function Kt(e,t){if(_e===null)return e;const s=gs(_e),n=e.dirs||(e.dirs=[]);for(let r=0;re.__isTeleport,Ki=Symbol("_leaveCb");function Zs(e,t){e.shapeFlag&6&&e.component?(e.transition=t,Zs(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Wi(e,t){return F(e)?ne({name:e.name},t,{setup:e}):e}function br(e){e.ids=[e.ids[0]+e.ids[2]+++"-",0,0]}const ss=new WeakMap;function Ct(e,t,s,n,r=!1){if(R(e)){e.forEach((N,L)=>Ct(N,t&&(R(t)?t[L]:t),s,n,r));return}if(Ot(n)&&!r){n.shapeFlag&512&&n.type.__asyncResolved&&n.component.subTree.component&&Ct(e,t,s,n.component.subTree);return}const i=n.shapeFlag&4?gs(n.component):n.el,l=r?null:i,{i:o,r:f}=e,p=t&&t.r,a=o.refs===K?o.refs={}:o.refs,m=o.setupState,E=j(m),C=m===K?Vn:N=>V(E,N);if(p!=null&&p!==f){if(dn(t),Y(p))a[p]=null,C(p)&&(m[p]=null);else if(se(p)){p.value=null;const N=t;N.k&&(a[N.k]=null)}}if(F(f))Nt(f,o,12,[l,a]);else{const N=Y(f),L=se(f);if(N||L){const Z=()=>{if(e.f){const H=N?C(f)?m[f]:a[f]:f.value;if(r)R(H)&&Us(H,i);else if(R(H))H.includes(i)||H.push(i);else if(N)a[f]=[i],C(f)&&(m[f]=a[f]);else{const B=[i];f.value=B,e.k&&(a[e.k]=B)}}else N?(a[f]=l,C(f)&&(m[f]=l)):L&&(f.value=l,e.k&&(a[e.k]=l))};if(l){const H=()=>{Z(),ss.delete(e)};H.id=-1,ss.set(e,H),de(H,s)}else dn(e),Z()}}}function dn(e){const t=ss.get(e);t&&(t.flags|=8,ss.delete(e))}cs().requestIdleCallback;cs().cancelIdleCallback;const Ot=e=>!!e.type.__asyncLoader,_r=e=>e.type.__isKeepAlive;function qi(e,t){yr(e,"a",t)}function ki(e,t){yr(e,"da",t)}function yr(e,t,s=le){const n=e.__wdc||(e.__wdc=()=>{let r=s;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(ds(t,n,s),s){let r=s.parent;for(;r&&r.parent;)_r(r.parent.vnode)&&Ji(n,t,s,r),r=r.parent}}function Ji(e,t,s,n){const r=ds(t,e,n,!0);Sr(()=>{Us(n[t],r)},s)}function ds(e,t,s=le,n=!1){if(s){const r=s[e]||(s[e]=[]),i=t.__weh||(t.__weh=(...l)=>{Ve();const o=Lt(s),f=Ne(t,s,e,l);return o(),Ue(),f});return n?r.unshift(i):r.push(i),i}}const Be=e=>(t,s=le)=>{(!Dt||e==="sp")&&ds(e,(...n)=>t(...n),s)},Gi=Be("bm"),xr=Be("m"),zi=Be("bu"),Yi=Be("u"),wr=Be("bum"),Sr=Be("um"),Xi=Be("sp"),Zi=Be("rtg"),Qi=Be("rtc");function el(e,t=le){ds("ec",e,t)}const tl=Symbol.for("v-ndc");function hn(e,t,s,n){let r;const i=s,l=R(e);if(l||Y(e)){const o=l&&dt(e);let f=!1,p=!1;o&&(f=!ye(e),p=Ge(e),e=fs(e)),r=new Array(e.length);for(let a=0,m=e.length;at(o,f,void 0,i));else{const o=Object.keys(e);r=new Array(o.length);for(let f=0,p=o.length;fe?Wr(e)?gs(e):Fs(e.parent):null,At=ne(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Fs(e.parent),$root:e=>Fs(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Tr(e),$forceUpdate:e=>e.f||(e.f=()=>{Xs(e.update)}),$nextTick:e=>e.n||(e.n=$i.bind(e.proxy)),$watch:e=>Sl.bind(e)}),ws=(e,t)=>e!==K&&!e.__isScriptSetup&&V(e,t),sl={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:s,setupState:n,data:r,props:i,accessCache:l,type:o,appContext:f}=e;let p;if(t[0]!=="$"){const C=l[t];if(C!==void 0)switch(C){case 1:return n[t];case 2:return r[t];case 4:return s[t];case 3:return i[t]}else{if(ws(n,t))return l[t]=1,n[t];if(r!==K&&V(r,t))return l[t]=2,r[t];if((p=e.propsOptions[0])&&V(p,t))return l[t]=3,i[t];if(s!==K&&V(s,t))return l[t]=4,s[t];Ds&&(l[t]=0)}}const a=At[t];let m,E;if(a)return t==="$attrs"&&te(e.attrs,"get",""),a(e);if((m=o.__cssModules)&&(m=m[t]))return m;if(s!==K&&V(s,t))return l[t]=4,s[t];if(E=f.config.globalProperties,V(E,t))return E[t]},set({_:e},t,s){const{data:n,setupState:r,ctx:i}=e;return ws(r,t)?(r[t]=s,!0):n!==K&&V(n,t)?(n[t]=s,!0):V(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=s,!0)},has({_:{data:e,setupState:t,accessCache:s,ctx:n,appContext:r,propsOptions:i,type:l}},o){let f,p;return!!(s[o]||e!==K&&o[0]!=="$"&&V(e,o)||ws(t,o)||(f=i[0])&&V(f,o)||V(n,o)||V(At,o)||V(r.config.globalProperties,o)||(p=l.__cssModules)&&p[o])},defineProperty(e,t,s){return s.get!=null?e._.accessCache[t]=0:V(s,"value")&&this.set(e,t,s.value,null),Reflect.defineProperty(e,t,s)}};function pn(e){return R(e)?e.reduce((t,s)=>(t[s]=null,t),{}):e}let Ds=!0;function nl(e){const t=Tr(e),s=e.proxy,n=e.ctx;Ds=!1,t.beforeCreate&&gn(t.beforeCreate,e,"bc");const{data:r,computed:i,methods:l,watch:o,provide:f,inject:p,created:a,beforeMount:m,mounted:E,beforeUpdate:C,updated:N,activated:L,deactivated:Z,beforeDestroy:H,beforeUnmount:B,destroyed:k,unmounted:P,render:z,renderTracked:Se,renderTriggered:ge,errorCaptured:X,serverPrefetch:Ke,expose:me,inheritAttrs:Xe,components:Ee,directives:it,filters:mt}=t;if(p&&rl(p,n,null),l)for(const O in l){const d=l[O];F(d)&&(n[O]=d.bind(s))}if(r){const O=r.call(s,s);J(O)&&(e.data=us(O))}if(Ds=!0,i)for(const O in i){const d=i[O],D=F(d)?d.bind(s,s):F(d.get)?d.get.bind(s,s):Fe,ue=!F(d)&&F(d.set)?d.set.bind(s):Fe,Ze=Yt({get:D,set:ue});Object.defineProperty(n,O,{enumerable:!0,configurable:!0,get:()=>Ze.value,set:Te=>Ze.value=Te})}if(o)for(const O in o)Er(o[O],n,s,O);if(f){const O=F(f)?f.call(s):f;Reflect.ownKeys(O).forEach(d=>{ul(d,O[d])})}a&&gn(a,e,"c");function Q(O,d){R(d)?d.forEach(D=>O(D.bind(s))):d&&O(d.bind(s))}if(Q(Gi,m),Q(xr,E),Q(zi,C),Q(Yi,N),Q(qi,L),Q(ki,Z),Q(el,X),Q(Qi,Se),Q(Zi,ge),Q(wr,B),Q(Sr,P),Q(Xi,Ke),R(me))if(me.length){const O=e.exposed||(e.exposed={});me.forEach(d=>{Object.defineProperty(O,d,{get:()=>s[d],set:D=>s[d]=D,enumerable:!0})})}else e.exposed||(e.exposed={});z&&e.render===Fe&&(e.render=z),Xe!=null&&(e.inheritAttrs=Xe),Ee&&(e.components=Ee),it&&(e.directives=it),Ke&&br(e)}function rl(e,t,s=Fe){R(e)&&(e=Ns(e));for(const n in e){const r=e[n];let i;J(r)?"default"in r?i=Jt(r.from||n,r.default,!0):i=Jt(r.from||n):i=Jt(r),se(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:l=>i.value=l}):t[n]=i}}function gn(e,t,s){Ne(R(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,s)}function Er(e,t,s,n){let r=n.includes(".")?Hr(s,n):()=>s[n];if(Y(e)){const i=t[e];F(i)&&Es(r,i)}else if(F(e))Es(r,e.bind(s));else if(J(e))if(R(e))e.forEach(i=>Er(i,t,s,n));else{const i=F(e.handler)?e.handler.bind(s):t[e.handler];F(i)&&Es(r,i,e)}}function Tr(e){const t=e.type,{mixins:s,extends:n}=t,{mixins:r,optionsCache:i,config:{optionMergeStrategies:l}}=e.appContext,o=i.get(t);let f;return o?f=o:!r.length&&!s&&!n?f=t:(f={},r.length&&r.forEach(p=>ns(f,p,l,!0)),ns(f,t,l)),J(t)&&i.set(t,f),f}function ns(e,t,s,n=!1){const{mixins:r,extends:i}=t;i&&ns(e,i,s,!0),r&&r.forEach(l=>ns(e,l,s,!0));for(const l in t)if(!(n&&l==="expose")){const o=il[l]||s&&s[l];e[l]=o?o(e[l],t[l]):t[l]}return e}const il={data:mn,props:vn,emits:vn,methods:wt,computed:wt,beforeCreate:re,created:re,beforeMount:re,mounted:re,beforeUpdate:re,updated:re,beforeDestroy:re,beforeUnmount:re,destroyed:re,unmounted:re,activated:re,deactivated:re,errorCaptured:re,serverPrefetch:re,components:wt,directives:wt,watch:ol,provide:mn,inject:ll};function mn(e,t){return t?e?function(){return ne(F(e)?e.call(this,this):e,F(t)?t.call(this,this):t)}:t:e}function ll(e,t){return wt(Ns(e),Ns(t))}function Ns(e){if(R(e)){const t={};for(let s=0;s1)return s&&F(t)?t.call(n&&n.proxy):t}}const Or={},Ar=()=>Object.create(Or),Pr=e=>Object.getPrototypeOf(e)===Or;function al(e,t,s,n=!1){const r={},i=Ar();e.propsDefaults=Object.create(null),Ir(e,t,r,i);for(const l in e.propsOptions[0])l in r||(r[l]=void 0);s?e.props=n?r:Oi(r):e.type.props?e.props=r:e.props=i,e.attrs=i}function dl(e,t,s,n){const{props:r,attrs:i,vnode:{patchFlag:l}}=e,o=j(r),[f]=e.propsOptions;let p=!1;if((n||l>0)&&!(l&16)){if(l&8){const a=e.vnode.dynamicProps;for(let m=0;m{f=!0;const[E,C]=Mr(m,t,!0);ne(l,E),C&&o.push(...C)};!s&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}if(!i&&!f)return J(e)&&n.set(e,ut),ut;if(R(i))for(let a=0;ae==="_"||e==="_ctx"||e==="$stable",en=e=>R(e)?e.map(Re):[Re(e)],pl=(e,t,s)=>{if(t._n)return t;const n=Vi((...r)=>en(t(...r)),s);return n._c=!1,n},Rr=(e,t,s)=>{const n=e._ctx;for(const r in e){if(Qs(r))continue;const i=e[r];if(F(i))t[r]=pl(r,i,n);else if(i!=null){const l=en(i);t[r]=()=>l}}},Fr=(e,t)=>{const s=en(t);e.slots.default=()=>s},Dr=(e,t,s)=>{for(const n in t)(s||!Qs(n))&&(e[n]=t[n])},gl=(e,t,s)=>{const n=e.slots=Ar();if(e.vnode.shapeFlag&32){const r=t._;r?(Dr(n,t,s),s&&kn(n,"_",r,!0)):Rr(t,n)}else t&&Fr(e,t)},ml=(e,t,s)=>{const{vnode:n,slots:r}=e;let i=!0,l=K;if(n.shapeFlag&32){const o=t._;o?s&&o===1?i=!1:Dr(r,t,s):(i=!t.$stable,Rr(t,r)),l=t}else t&&(Fr(e,t),l={default:1});if(i)for(const o in r)!Qs(o)&&l[o]==null&&delete r[o]},de=Ml;function vl(e){return bl(e)}function bl(e,t){const s=cs();s.__VUE__=!0;const{insert:n,remove:r,patchProp:i,createElement:l,createText:o,createComment:f,setText:p,setElementText:a,parentNode:m,nextSibling:E,setScopeId:C=Fe,insertStaticContent:N}=e,L=(c,u,h,_=null,v=null,b=null,S=void 0,w=null,x=!!u.dynamicChildren)=>{if(c===u)return;c&&!xt(c,u)&&(_=Ht(c),Te(c,v,b,!0),c=null),u.patchFlag===-2&&(x=!1,u.dynamicChildren=null);const{type:y,ref:I,shapeFlag:T}=u;switch(y){case ps:Z(c,u,h,_);break;case ze:H(c,u,h,_);break;case Gt:c==null&&B(u,h,_,S);break;case he:Ee(c,u,h,_,v,b,S,w,x);break;default:T&1?z(c,u,h,_,v,b,S,w,x):T&6?it(c,u,h,_,v,b,S,w,x):(T&64||T&128)&&y.process(c,u,h,_,v,b,S,w,x,bt)}I!=null&&v?Ct(I,c&&c.ref,b,u||c,!u):I==null&&c&&c.ref!=null&&Ct(c.ref,null,b,c,!0)},Z=(c,u,h,_)=>{if(c==null)n(u.el=o(u.children),h,_);else{const v=u.el=c.el;u.children!==c.children&&p(v,u.children)}},H=(c,u,h,_)=>{c==null?n(u.el=f(u.children||""),h,_):u.el=c.el},B=(c,u,h,_)=>{[c.el,c.anchor]=N(c.children,u,h,_,c.el,c.anchor)},k=({el:c,anchor:u},h,_)=>{let v;for(;c&&c!==u;)v=E(c),n(c,h,_),c=v;n(u,h,_)},P=({el:c,anchor:u})=>{let h;for(;c&&c!==u;)h=E(c),r(c),c=h;r(u)},z=(c,u,h,_,v,b,S,w,x)=>{u.type==="svg"?S="svg":u.type==="math"&&(S="mathml"),c==null?Se(u,h,_,v,b,S,w,x):Ke(c,u,v,b,S,w,x)},Se=(c,u,h,_,v,b,S,w)=>{let x,y;const{props:I,shapeFlag:T,transition:A,dirs:M}=c;if(x=c.el=l(c.type,b,I&&I.is,I),T&8?a(x,c.children):T&16&&X(c.children,x,null,_,v,Ss(c,b),S,w),M&&Qe(c,null,_,"created"),ge(x,c,c.scopeId,S,_),I){for(const W in I)W!=="value"&&!St(W)&&i(x,W,null,I[W],b,_);"value"in I&&i(x,"value",null,I.value,b),(y=I.onVnodeBeforeMount)&&Pe(y,_,c)}M&&Qe(c,null,_,"beforeMount");const $=_l(v,A);$&&A.beforeEnter(x),n(x,u,h),((y=I&&I.onVnodeMounted)||$||M)&&de(()=>{y&&Pe(y,_,c),$&&A.enter(x),M&&Qe(c,null,_,"mounted")},v)},ge=(c,u,h,_,v)=>{if(h&&C(c,h),_)for(let b=0;b<_.length;b++)C(c,_[b]);if(v){let b=v.subTree;if(u===b||Vr(b.type)&&(b.ssContent===u||b.ssFallback===u)){const S=v.vnode;ge(c,S,S.scopeId,S.slotScopeIds,v.parent)}}},X=(c,u,h,_,v,b,S,w,x=0)=>{for(let y=x;y{const w=u.el=c.el;let{patchFlag:x,dynamicChildren:y,dirs:I}=u;x|=c.patchFlag&16;const T=c.props||K,A=u.props||K;let M;if(h&&et(h,!1),(M=A.onVnodeBeforeUpdate)&&Pe(M,h,u,c),I&&Qe(u,c,h,"beforeUpdate"),h&&et(h,!0),(T.innerHTML&&A.innerHTML==null||T.textContent&&A.textContent==null)&&a(w,""),y?me(c.dynamicChildren,y,w,h,_,Ss(u,v),b):S||d(c,u,w,null,h,_,Ss(u,v),b,!1),x>0){if(x&16)Xe(w,T,A,h,v);else if(x&2&&T.class!==A.class&&i(w,"class",null,A.class,v),x&4&&i(w,"style",T.style,A.style,v),x&8){const $=u.dynamicProps;for(let W=0;W<$.length;W++){const U=$[W],oe=T[U],ce=A[U];(ce!==oe||U==="value")&&i(w,U,oe,ce,v,h)}}x&1&&c.children!==u.children&&a(w,u.children)}else!S&&y==null&&Xe(w,T,A,h,v);((M=A.onVnodeUpdated)||I)&&de(()=>{M&&Pe(M,h,u,c),I&&Qe(u,c,h,"updated")},_)},me=(c,u,h,_,v,b,S)=>{for(let w=0;w{if(u!==h){if(u!==K)for(const b in u)!St(b)&&!(b in h)&&i(c,b,u[b],null,v,_);for(const b in h){if(St(b))continue;const S=h[b],w=u[b];S!==w&&b!=="value"&&i(c,b,w,S,v,_)}"value"in h&&i(c,"value",u.value,h.value,v)}},Ee=(c,u,h,_,v,b,S,w,x)=>{const y=u.el=c?c.el:o(""),I=u.anchor=c?c.anchor:o("");let{patchFlag:T,dynamicChildren:A,slotScopeIds:M}=u;M&&(w=w?w.concat(M):M),c==null?(n(y,h,_),n(I,h,_),X(u.children||[],h,I,v,b,S,w,x)):T>0&&T&64&&A&&c.dynamicChildren?(me(c.dynamicChildren,A,h,v,b,S,w),(u.key!=null||v&&u===v.subTree)&&Nr(c,u,!0)):d(c,u,h,I,v,b,S,w,x)},it=(c,u,h,_,v,b,S,w,x)=>{u.slotScopeIds=w,c==null?u.shapeFlag&512?v.ctx.activate(u,h,_,S,x):mt(u,h,_,v,b,S,x):$t(c,u,x)},mt=(c,u,h,_,v,b,S)=>{const w=c.component=Vl(c,_,v);if(_r(c)&&(w.ctx.renderer=bt),Bl(w,!1,S),w.asyncDep){if(v&&v.registerDep(w,Q,S),!c.el){const x=w.subTree=De(ze);H(null,x,u,h),c.placeholder=x.el}}else Q(w,c,u,h,v,b,S)},$t=(c,u,h)=>{const _=u.component=c.component;if(Pl(c,u,h))if(_.asyncDep&&!_.asyncResolved){O(_,u,h);return}else _.next=u,_.update();else u.el=c.el,_.vnode=u},Q=(c,u,h,_,v,b,S)=>{const w=()=>{if(c.isMounted){let{next:T,bu:A,u:M,parent:$,vnode:W}=c;{const Oe=Lr(c);if(Oe){T&&(T.el=W.el,O(c,T,S)),Oe.asyncDep.then(()=>{c.isUnmounted||w()});return}}let U=T,oe;et(c,!1),T?(T.el=W.el,O(c,T,S)):T=W,A&&kt(A),(oe=T.props&&T.props.onVnodeBeforeUpdate)&&Pe(oe,$,T,W),et(c,!0);const ce=yn(c),Ce=c.subTree;c.subTree=ce,L(Ce,ce,m(Ce.el),Ht(Ce),c,v,b),T.el=ce.el,U===null&&Il(c,ce.el),M&&de(M,v),(oe=T.props&&T.props.onVnodeUpdated)&&de(()=>Pe(oe,$,T,W),v)}else{let T;const{el:A,props:M}=u,{bm:$,m:W,parent:U,root:oe,type:ce}=c,Ce=Ot(u);et(c,!1),$&&kt($),!Ce&&(T=M&&M.onVnodeBeforeMount)&&Pe(T,U,u),et(c,!0);{oe.ce&&oe.ce._def.shadowRoot!==!1&&oe.ce._injectChildStyle(ce);const Oe=c.subTree=yn(c);L(null,Oe,h,_,c,v,b),u.el=Oe.el}if(W&&de(W,v),!Ce&&(T=M&&M.onVnodeMounted)){const Oe=u;de(()=>Pe(T,U,Oe),v)}(u.shapeFlag&256||U&&Ot(U.vnode)&&U.vnode.shapeFlag&256)&&c.a&&de(c.a,v),c.isMounted=!0,u=h=_=null}};c.scope.on();const x=c.effect=new Yn(w);c.scope.off();const y=c.update=x.run.bind(x),I=c.job=x.runIfDirty.bind(x);I.i=c,I.id=c.uid,x.scheduler=()=>Xs(I),et(c,!0),y()},O=(c,u,h)=>{u.component=c;const _=c.vnode.props;c.vnode=u,c.next=null,dl(c,u.props,_,h),ml(c,u.children,h),Ve(),an(c),Ue()},d=(c,u,h,_,v,b,S,w,x=!1)=>{const y=c&&c.children,I=c?c.shapeFlag:0,T=u.children,{patchFlag:A,shapeFlag:M}=u;if(A>0){if(A&128){ue(y,T,h,_,v,b,S,w,x);return}else if(A&256){D(y,T,h,_,v,b,S,w,x);return}}M&8?(I&16&&vt(y,v,b),T!==y&&a(h,T)):I&16?M&16?ue(y,T,h,_,v,b,S,w,x):vt(y,v,b,!0):(I&8&&a(h,""),M&16&&X(T,h,_,v,b,S,w,x))},D=(c,u,h,_,v,b,S,w,x)=>{c=c||ut,u=u||ut;const y=c.length,I=u.length,T=Math.min(y,I);let A;for(A=0;AI?vt(c,v,b,!0,!1,T):X(u,h,_,v,b,S,w,x,T)},ue=(c,u,h,_,v,b,S,w,x)=>{let y=0;const I=u.length;let T=c.length-1,A=I-1;for(;y<=T&&y<=A;){const M=c[y],$=u[y]=x?qe(u[y]):Re(u[y]);if(xt(M,$))L(M,$,h,null,v,b,S,w,x);else break;y++}for(;y<=T&&y<=A;){const M=c[T],$=u[A]=x?qe(u[A]):Re(u[A]);if(xt(M,$))L(M,$,h,null,v,b,S,w,x);else break;T--,A--}if(y>T){if(y<=A){const M=A+1,$=MA)for(;y<=T;)Te(c[y],v,b,!0),y++;else{const M=y,$=y,W=new Map;for(y=$;y<=A;y++){const ae=u[y]=x?qe(u[y]):Re(u[y]);ae.key!=null&&W.set(ae.key,y)}let U,oe=0;const ce=A-$+1;let Ce=!1,Oe=0;const _t=new Array(ce);for(y=0;y=ce){Te(ae,v,b,!0);continue}let Ae;if(ae.key!=null)Ae=W.get(ae.key);else for(U=$;U<=A;U++)if(_t[U-$]===0&&xt(ae,u[U])){Ae=U;break}Ae===void 0?Te(ae,v,b,!0):(_t[Ae-$]=y+1,Ae>=Oe?Oe=Ae:Ce=!0,L(ae,u[Ae],h,null,v,b,S,w,x),oe++)}const rn=Ce?yl(_t):ut;for(U=rn.length-1,y=ce-1;y>=0;y--){const ae=$+y,Ae=u[ae],ln=u[ae+1],on=ae+1{const{el:b,type:S,transition:w,children:x,shapeFlag:y}=c;if(y&6){Ze(c.component.subTree,u,h,_);return}if(y&128){c.suspense.move(u,h,_);return}if(y&64){S.move(c,u,h,bt);return}if(S===he){n(b,u,h);for(let T=0;Tw.enter(b),v);else{const{leave:T,delayLeave:A,afterLeave:M}=w,$=()=>{c.ctx.isUnmounted?r(b):n(b,u,h)},W=()=>{b._isLeaving&&b[Ki](!0),T(b,()=>{$(),M&&M()})};A?A(b,$,W):W()}else n(b,u,h)},Te=(c,u,h,_=!1,v=!1)=>{const{type:b,props:S,ref:w,children:x,dynamicChildren:y,shapeFlag:I,patchFlag:T,dirs:A,cacheIndex:M}=c;if(T===-2&&(v=!1),w!=null&&(Ve(),Ct(w,null,h,c,!0),Ue()),M!=null&&(u.renderCache[M]=void 0),I&256){u.ctx.deactivate(c);return}const $=I&1&&A,W=!Ot(c);let U;if(W&&(U=S&&S.onVnodeBeforeUnmount)&&Pe(U,u,c),I&6)Gr(c.component,h,_);else{if(I&128){c.suspense.unmount(h,_);return}$&&Qe(c,null,u,"beforeUnmount"),I&64?c.type.remove(c,u,h,bt,_):y&&!y.hasOnce&&(b!==he||T>0&&T&64)?vt(y,u,h,!1,!0):(b===he&&T&384||!v&&I&16)&&vt(x,u,h),_&&sn(c)}(W&&(U=S&&S.onVnodeUnmounted)||$)&&de(()=>{U&&Pe(U,u,c),$&&Qe(c,null,u,"unmounted")},h)},sn=c=>{const{type:u,el:h,anchor:_,transition:v}=c;if(u===he){Jr(h,_);return}if(u===Gt){P(c);return}const b=()=>{r(h),v&&!v.persisted&&v.afterLeave&&v.afterLeave()};if(c.shapeFlag&1&&v&&!v.persisted){const{leave:S,delayLeave:w}=v,x=()=>S(h,b);w?w(c.el,b,x):x()}else b()},Jr=(c,u)=>{let h;for(;c!==u;)h=E(c),r(c),c=h;r(u)},Gr=(c,u,h)=>{const{bum:_,scope:v,job:b,subTree:S,um:w,m:x,a:y}=c;_n(x),_n(y),_&&kt(_),v.stop(),b&&(b.flags|=8,Te(S,c,u,h)),w&&de(w,u),de(()=>{c.isUnmounted=!0},u)},vt=(c,u,h,_=!1,v=!1,b=0)=>{for(let S=b;S{if(c.shapeFlag&6)return Ht(c.component.subTree);if(c.shapeFlag&128)return c.suspense.next();const u=E(c.anchor||c.el),h=u&&u[Ui];return h?E(h):u};let ms=!1;const nn=(c,u,h)=>{c==null?u._vnode&&Te(u._vnode,null,null,!0):L(u._vnode||null,c,u,null,null,null,h),u._vnode=c,ms||(ms=!0,an(),gr(),ms=!1)},bt={p:L,um:Te,m:Ze,r:sn,mt,mc:X,pc:d,pbc:me,n:Ht,o:e};return{render:nn,hydrate:void 0,createApp:fl(nn)}}function Ss({type:e,props:t},s){return s==="svg"&&e==="foreignObject"||s==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:s}function et({effect:e,job:t},s){s?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function _l(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function Nr(e,t,s=!1){const n=e.children,r=t.children;if(R(n)&&R(r))for(let i=0;i>1,e[s[o]]0&&(t[n]=s[i-1]),s[i]=n)}}for(i=s.length,l=s[i-1];i-- >0;)s[i]=l,l=t[l];return s}function Lr(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Lr(t)}function _n(e){if(e)for(let t=0;tJt(xl);function Es(e,t,s){return $r(e,t,s)}function $r(e,t,s=K){const{immediate:n,deep:r,flush:i,once:l}=s,o=ne({},s),f=t&&n||!t&&i!=="post";let p;if(Dt){if(i==="sync"){const C=wl();p=C.__watcherHandles||(C.__watcherHandles=[])}else if(!f){const C=()=>{};return C.stop=Fe,C.resume=Fe,C.pause=Fe,C}}const a=le;o.call=(C,N,L)=>Ne(C,a,N,L);let m=!1;i==="post"?o.scheduler=C=>{de(C,a&&a.suspense)}:i!=="sync"&&(m=!0,o.scheduler=(C,N)=>{N?C():Xs(C)}),o.augmentJob=C=>{t&&(C.flags|=4),m&&(C.flags|=2,a&&(C.id=a.uid,C.i=a))};const E=Ni(e,t,o);return Dt&&(p?p.push(E):f&&E()),E}function Sl(e,t,s){const n=this.proxy,r=Y(e)?e.includes(".")?Hr(n,e):()=>n[e]:e.bind(n,n);let i;F(t)?i=t:(i=t.handler,s=t);const l=Lt(this),o=$r(r,i.bind(n),s);return l(),o}function Hr(e,t){const s=t.split(".");return()=>{let n=e;for(let r=0;rt==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${Je(t)}Modifiers`]||e[`${rt(t)}Modifiers`];function Tl(e,t,...s){if(e.isUnmounted)return;const n=e.vnode.props||K;let r=s;const i=t.startsWith("update:"),l=i&&El(n,t.slice(7));l&&(l.trim&&(r=s.map(a=>Y(a)?a.trim():a)),l.number&&(r=s.map(As)));let o,f=n[o=vs(t)]||n[o=vs(Je(t))];!f&&i&&(f=n[o=vs(rt(t))]),f&&Ne(f,e,6,r);const p=n[o+"Once"];if(p){if(!e.emitted)e.emitted={};else if(e.emitted[o])return;e.emitted[o]=!0,Ne(p,e,6,r)}}const Cl=new WeakMap;function jr(e,t,s=!1){const n=s?Cl:t.emitsCache,r=n.get(e);if(r!==void 0)return r;const i=e.emits;let l={},o=!1;if(!F(e)){const f=p=>{const a=jr(p,t,!0);a&&(o=!0,ne(l,a))};!s&&t.mixins.length&&t.mixins.forEach(f),e.extends&&f(e.extends),e.mixins&&e.mixins.forEach(f)}return!i&&!o?(J(e)&&n.set(e,null),null):(R(i)?i.forEach(f=>l[f]=null):ne(l,i),J(e)&&n.set(e,l),l)}function hs(e,t){return!e||!is(t)?!1:(t=t.slice(2).replace(/Once$/,""),V(e,t[0].toLowerCase()+t.slice(1))||V(e,rt(t))||V(e,t))}function yn(e){const{type:t,vnode:s,proxy:n,withProxy:r,propsOptions:[i],slots:l,attrs:o,emit:f,render:p,renderCache:a,props:m,data:E,setupState:C,ctx:N,inheritAttrs:L}=e,Z=ts(e);let H,B;try{if(s.shapeFlag&4){const P=r||n,z=P;H=Re(p.call(z,P,a,m,C,E,N)),B=o}else{const P=t;H=Re(P.length>1?P(m,{attrs:o,slots:l,emit:f}):P(m,null)),B=t.props?o:Ol(o)}}catch(P){Pt.length=0,as(P,e,1),H=De(ze)}let k=H;if(B&&L!==!1){const P=Object.keys(B),{shapeFlag:z}=k;P.length&&z&7&&(i&&P.some(Vs)&&(B=Al(B,i)),k=gt(k,B,!1,!0))}return s.dirs&&(k=gt(k,null,!1,!0),k.dirs=k.dirs?k.dirs.concat(s.dirs):s.dirs),s.transition&&Zs(k,s.transition),H=k,ts(Z),H}const Ol=e=>{let t;for(const s in e)(s==="class"||s==="style"||is(s))&&((t||(t={}))[s]=e[s]);return t},Al=(e,t)=>{const s={};for(const n in e)(!Vs(n)||!(n.slice(9)in t))&&(s[n]=e[n]);return s};function Pl(e,t,s){const{props:n,children:r,component:i}=e,{props:l,children:o,patchFlag:f}=t,p=i.emitsOptions;if(t.dirs||t.transition)return!0;if(s&&f>=0){if(f&1024)return!0;if(f&16)return n?xn(n,l,p):!!l;if(f&8){const a=t.dynamicProps;for(let m=0;me.__isSuspense;function Ml(e,t){t&&t.pendingBranch?R(e)?t.effects.push(...e):t.effects.push(e):ji(e)}const he=Symbol.for("v-fgt"),ps=Symbol.for("v-txt"),ze=Symbol.for("v-cmt"),Gt=Symbol.for("v-stc"),Pt=[];let pe=null;function be(e=!1){Pt.push(pe=e?null:[])}function Rl(){Pt.pop(),pe=Pt[Pt.length-1]||null}let Ft=1;function wn(e,t=!1){Ft+=e,e<0&&pe&&t&&(pe.hasOnce=!0)}function Ur(e){return e.dynamicChildren=Ft>0?pe||ut:null,Rl(),Ft>0&&pe&&pe.push(e),e}function xe(e,t,s,n,r,i){return Ur(g(e,t,s,n,r,i,!0))}function Fl(e,t,s,n,r){return Ur(De(e,t,s,n,r,!0))}function Br(e){return e?e.__v_isVNode===!0:!1}function xt(e,t){return e.type===t.type&&e.key===t.key}const Kr=({key:e})=>e??null,zt=({ref:e,ref_key:t,ref_for:s})=>(typeof e=="number"&&(e=""+e),e!=null?Y(e)||se(e)||F(e)?{i:_e,r:e,k:t,f:!!s}:e:null);function g(e,t=null,s=null,n=0,r=null,i=e===he?0:1,l=!1,o=!1){const f={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Kr(t),ref:t&&zt(t),scopeId:vr,slotScopeIds:null,children:s,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:n,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:_e};return o?(tn(f,s),i&128&&e.normalize(f)):s&&(f.shapeFlag|=Y(s)?8:16),Ft>0&&!l&&pe&&(f.patchFlag>0||i&6)&&f.patchFlag!==32&&pe.push(f),f}const De=Dl;function Dl(e,t=null,s=null,n=0,r=null,i=!1){if((!e||e===tl)&&(e=ze),Br(e)){const o=gt(e,t,!0);return s&&tn(o,s),Ft>0&&!i&&pe&&(o.shapeFlag&6?pe[pe.indexOf(e)]=o:pe.push(o)),o.patchFlag=-2,o}if(kl(e)&&(e=e.__vccOpts),t){t=Nl(t);let{class:o,style:f}=t;o&&!Y(o)&&(t.class=st(o)),J(f)&&(Ys(f)&&!R(f)&&(f=ne({},f)),t.style=Ks(f))}const l=Y(e)?1:Vr(e)?128:Bi(e)?64:J(e)?4:F(e)?2:0;return g(e,t,s,n,r,l,i,!0)}function Nl(e){return e?Ys(e)||Pr(e)?ne({},e):e:null}function gt(e,t,s=!1,n=!1){const{props:r,ref:i,patchFlag:l,children:o,transition:f}=e,p=t?$l(r||{},t):r,a={__v_isVNode:!0,__v_skip:!0,type:e.type,props:p,key:p&&Kr(p),ref:t&&t.ref?s&&i?R(i)?i.concat(zt(t)):[i,zt(t)]:zt(t):i,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:o,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==he?l===-1?16:l|16:l,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:f,component:e.component,suspense:e.suspense,ssContent:e.ssContent&>(e.ssContent),ssFallback:e.ssFallback&>(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return f&&n&&Zs(a,f.clone(a)),a}function Ie(e=" ",t=0){return De(ps,null,e,t)}function Ll(e,t){const s=De(Gt,null,e);return s.staticCount=t,s}function Wt(e="",t=!1){return t?(be(),Fl(ze,null,e)):De(ze,null,e)}function Re(e){return e==null||typeof e=="boolean"?De(ze):R(e)?De(he,null,e.slice()):Br(e)?qe(e):De(ps,null,String(e))}function qe(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:gt(e)}function tn(e,t){let s=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(R(t))s=16;else if(typeof t=="object")if(n&65){const r=t.default;r&&(r._c&&(r._d=!1),tn(e,r()),r._c&&(r._d=!0));return}else{s=32;const r=t._;!r&&!Pr(t)?t._ctx=_e:r===3&&_e&&(_e.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else F(t)?(t={default:t,_ctx:_e},s=32):(t=String(t),n&64?(s=16,t=[Ie(t)]):s=8);e.children=t,e.shapeFlag|=s}function $l(...e){const t={};for(let s=0;sle||_e;let rs,$s;{const e=cs(),t=(s,n)=>{let r;return(r=e[s])||(r=e[s]=[]),r.push(n),i=>{r.length>1?r.forEach(l=>l(i)):r[0](i)}};rs=t("__VUE_INSTANCE_SETTERS__",s=>le=s),$s=t("__VUE_SSR_SETTERS__",s=>Dt=s)}const Lt=e=>{const t=le;return rs(e),e.scope.on(),()=>{e.scope.off(),rs(t)}},Sn=()=>{le&&le.scope.off(),rs(null)};function Wr(e){return e.vnode.shapeFlag&4}let Dt=!1;function Bl(e,t=!1,s=!1){t&&$s(t);const{props:n,children:r}=e.vnode,i=Wr(e);al(e,n,i,t),gl(e,r,s||t);const l=i?Kl(e,t):void 0;return t&&$s(!1),l}function Kl(e,t){const s=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,sl);const{setup:n}=s;if(n){Ve();const r=e.setupContext=n.length>1?ql(e):null,i=Lt(e),l=Nt(n,e,0,[e.props,r]),o=Bn(l);if(Ue(),i(),(o||e.sp)&&!Ot(e)&&br(e),o){if(l.then(Sn,Sn),t)return l.then(f=>{En(e,f)}).catch(f=>{as(f,e,0)});e.asyncDep=l}else En(e,l)}else qr(e)}function En(e,t,s){F(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:J(t)&&(e.setupState=dr(t)),qr(e)}function qr(e,t,s){const n=e.type;e.render||(e.render=n.render||Fe);{const r=Lt(e);Ve();try{nl(e)}finally{Ue(),r()}}}const Wl={get(e,t){return te(e,"get",""),e[t]}};function ql(e){const t=s=>{e.exposed=s||{}};return{attrs:new Proxy(e.attrs,Wl),slots:e.slots,emit:e.emit,expose:t}}function gs(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(dr(Ai(e.exposed)),{get(t,s){if(s in t)return t[s];if(s in At)return At[s](e)},has(t,s){return s in t||s in At}})):e.proxy}function kl(e){return F(e)&&"__vccOpts"in e}const Yt=(e,t)=>Fi(e,t,Dt),Jl="3.5.21";/** +* @vue/runtime-dom v3.5.21 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Hs;const Tn=typeof window<"u"&&window.trustedTypes;if(Tn)try{Hs=Tn.createPolicy("vue",{createHTML:e=>e})}catch{}const kr=Hs?e=>Hs.createHTML(e):e=>e,Gl="http://www.w3.org/2000/svg",zl="http://www.w3.org/1998/Math/MathML",$e=typeof document<"u"?document:null,Cn=$e&&$e.createElement("template"),Yl={insert:(e,t,s)=>{t.insertBefore(e,s||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,s,n)=>{const r=t==="svg"?$e.createElementNS(Gl,e):t==="mathml"?$e.createElementNS(zl,e):s?$e.createElement(e,{is:s}):$e.createElement(e);return e==="select"&&n&&n.multiple!=null&&r.setAttribute("multiple",n.multiple),r},createText:e=>$e.createTextNode(e),createComment:e=>$e.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>$e.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,s,n,r,i){const l=s?s.previousSibling:t.lastChild;if(r&&(r===i||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),s),!(r===i||!(r=r.nextSibling)););else{Cn.innerHTML=kr(n==="svg"?`${e}`:n==="mathml"?`${e}`:e);const o=Cn.content;if(n==="svg"||n==="mathml"){const f=o.firstChild;for(;f.firstChild;)o.appendChild(f.firstChild);o.removeChild(f)}t.insertBefore(o,s)}return[l?l.nextSibling:t.firstChild,s?s.previousSibling:t.lastChild]}},Xl=Symbol("_vtc");function Zl(e,t,s){const n=e[Xl];n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):s?e.setAttribute("class",t):e.className=t}const On=Symbol("_vod"),Ql=Symbol("_vsh"),eo=Symbol(""),to=/(?:^|;)\s*display\s*:/;function so(e,t,s){const n=e.style,r=Y(s);let i=!1;if(s&&!r){if(t)if(Y(t))for(const l of t.split(";")){const o=l.slice(0,l.indexOf(":")).trim();s[o]==null&&Xt(n,o,"")}else for(const l in t)s[l]==null&&Xt(n,l,"");for(const l in s)l==="display"&&(i=!0),Xt(n,l,s[l])}else if(r){if(t!==s){const l=n[eo];l&&(s+=";"+l),n.cssText=s,i=to.test(s)}}else t&&e.removeAttribute("style");On in e&&(e[On]=i?n.display:"",e[Ql]&&(n.display="none"))}const An=/\s*!important$/;function Xt(e,t,s){if(R(s))s.forEach(n=>Xt(e,t,n));else if(s==null&&(s=""),t.startsWith("--"))e.setProperty(t,s);else{const n=no(e,t);An.test(s)?e.setProperty(rt(n),s.replace(An,""),"important"):e[n]=s}}const Pn=["Webkit","Moz","ms"],Ts={};function no(e,t){const s=Ts[t];if(s)return s;let n=Je(t);if(n!=="filter"&&n in e)return Ts[t]=n;n=qn(n);for(let r=0;rCs||(oo.then(()=>Cs=0),Cs=Date.now());function fo(e,t){const s=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=s.attached)return;Ne(uo(n,s.value),t,5,[n])};return s.value=e,s.attached=co(),s}function uo(e,t){if(R(t)){const s=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{s.call(e),e._stopped=!0},t.map(n=>r=>!r._stopped&&n&&n(r))}else return t}const Nn=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,ao=(e,t,s,n,r,i)=>{const l=r==="svg";t==="class"?Zl(e,n,l):t==="style"?so(e,s,n):is(t)?Vs(t)||io(e,t,s,n,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):ho(e,t,n,l))?(Rn(e,t,n),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&Mn(e,t,n,l,i,t!=="value")):e._isVueCE&&(/[A-Z]/.test(t)||!Y(n))?Rn(e,Je(t),n,i,t):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Mn(e,t,n,l))};function ho(e,t,s,n){if(n)return!!(t==="innerHTML"||t==="textContent"||t in e&&Nn(t)&&F(s));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const r=e.tagName;if(r==="IMG"||r==="VIDEO"||r==="CANVAS"||r==="SOURCE")return!1}return Nn(t)&&Y(s)?!1:t in e}const Ln=e=>{const t=e.props["onUpdate:modelValue"]||!1;return R(t)?s=>kt(t,s):t};function po(e){e.target.composing=!0}function $n(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const Os=Symbol("_assign"),qt={created(e,{modifiers:{lazy:t,trim:s,number:n}},r){e[Os]=Ln(r);const i=n||r.props&&r.props.type==="number";ft(e,t?"change":"input",l=>{if(l.target.composing)return;let o=e.value;s&&(o=o.trim()),i&&(o=As(o)),e[Os](o)}),s&&ft(e,"change",()=>{e.value=e.value.trim()}),t||(ft(e,"compositionstart",po),ft(e,"compositionend",$n),ft(e,"change",$n))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,oldValue:s,modifiers:{lazy:n,trim:r,number:i}},l){if(e[Os]=Ln(l),e.composing)return;const o=(i||e.type==="number")&&!/^0\d/.test(e.value)?As(e.value):e.value,f=t??"";o!==f&&(document.activeElement===e&&e.type!=="range"&&(n&&t===s||r&&e.value.trim()===f)||(e.value=f))}},go=["ctrl","shift","alt","meta"],mo={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>go.some(s=>e[`${s}Key`]&&!t.includes(s))},Hn=(e,t)=>{const s=e._withMods||(e._withMods={}),n=t.join(".");return s[n]||(s[n]=(r,...i)=>{for(let l=0;l{const t=bo().createApp(...e),{mount:s}=t;return t.mount=n=>{const r=xo(n);if(!r)return;const i=t._component;!F(i)&&!i.render&&!i.template&&(i.template=r.innerHTML),r.nodeType===1&&(r.textContent="");const l=s(r,!1,yo(r));return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),l},t};function yo(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function xo(e){return Y(e)?document.querySelector(e):e}async function ot(e){try{return await e.json()}catch{return null}}class wo{constructor(){jt(this,"access","");jt(this,"refresh","");jt(this,"deviceId","")}get signedIn(){return!!this.access}async raw(t,s={},n=!0){const r=new Headers(s.headers);return s.body&&!r.has("Content-Type")&&r.set("Content-Type","application/json"),n&&this.access&&r.set("Authorization",`Bearer ${this.access}`),fetch(t,{...s,headers:r,cache:"no-store",credentials:"same-origin"})}async rotate(){if(!this.refresh)return!1;const t=await this.raw("/sync/v1/auth/refresh",{method:"POST",body:JSON.stringify({refresh_token:this.refresh})},!1);if(!t.ok)return!1;const s=await ot(t);return s?(this.accept(s),!0):!1}async request(t,s={},n=!0){var l;let r=await this.raw(t,s);if(r.status===401&&n&&await this.rotate()&&(r=await this.raw(t,s)),!r.ok){const o=await ot(r);throw r.status===401&&this.clear(),new Error(((l=o==null?void 0:o.error)==null?void 0:l.code)??`HTTP_${r.status}`)}if(r.status===204)return;const i=await ot(r);if(i===null)throw new Error("INVALID_RESPONSE");return i}accept(t){this.access=t.access_token,this.refresh=t.refresh_token,this.deviceId=t.device_id}async status(){const[t,s,n]=await Promise.allSettled([this.raw("/health",{},!1),this.raw("/ready",{},!1),this.raw("/sync/v1/handshake?protocol=1",{},!1)]);let r=1,i=100*1024*1024;if(n.status==="fulfilled"&&n.value.ok){const l=await ot(n.value);r=(l==null?void 0:l.protocol)??r,i=(l==null?void 0:l.max_object_size)??i}return{health:t.status==="fulfilled"&&t.value.ok,ready:s.status==="fulfilled"&&s.value.ok,protocol:r,maxObjectSize:i}}async login(t,s,n){var l;const r=await this.raw("/sync/v1/auth/sessions",{method:"POST",body:JSON.stringify({username:t,password:s,device_name:n})},!1);if(!r.ok){const o=await ot(r);throw new Error(((l=o==null?void 0:o.error)==null?void 0:l.code)??`HTTP_${r.status}`)}const i=await ot(r);if(!i)throw new Error("INVALID_RESPONSE");this.accept(i)}vaults(){return this.request("/sync/v1/vaults")}devices(){return this.request("/sync/v1/devices")}createVault(t){return this.request("/sync/v1/vaults",{method:"POST",body:JSON.stringify({name:t})})}revokeDevice(t){return this.request(`/sync/v1/devices/${encodeURIComponent(t)}`,{method:"DELETE"})}async logout(){try{this.access&&await this.request("/sync/v1/auth/sessions",{method:"DELETE"},!1)}finally{this.clear()}}clear(){this.access="",this.refresh="",this.deviceId=""}}const So={class:"topbar"},Eo={class:"service-strip","aria-label":"服务状态"},To=["disabled"],Co={key:0,class:"hero"},Oo={class:"hero-copy"},Ao={class:"protocol-grid","aria-label":"协议能力"},Po={class:"login-card","aria-labelledby":"login-title"},Io=["disabled"],Mo={key:1,class:"console-view"},Ro={class:"console-heading"},Fo=["disabled"],Do={class:"metric-grid"},No={class:"content-grid"},Lo={class:"surface"},$o={class:"surface-heading"},Ho=["disabled"],jo=["disabled"],Vo={class:"vault-list"},Uo={key:0,class:"empty-state"},Bo={class:"item-copy"},Ko=["value"],Wo={class:"item-side"},qo={class:"surface"},ko={class:"device-list"},Jo={class:"device-symbol"},Go={class:"item-copy"},zo=["disabled","onClick"],Yo=Wi({__name:"App",setup(e){const t=new wo,s=us({health:!1,ready:!1,protocol:1,maxObjectSize:100*1024*1024}),n=ve(!0),r=ve(!1),i=ve(!1),l=ve(""),o=ve(""),f=ve("OpenNexus Web Console"),p=ve(""),a=ve(""),m=ve([]),E=ve([]),C=ve(""),N=ve(!1);let L,Z;const H=Yt(()=>m.value.reduce((O,d)=>O+Number(d.used||0),0)),B=Yt(()=>m.value.reduce((O,d)=>O+Number(d.quota||0),0)),k=Yt(()=>E.value.filter(O=>!O.revoked).length);function P(O){const d=["B","KiB","MiB","GiB","TiB"];let D=Number(O)||0,ue=0;for(;D>=1024&&ue=10||ue===0?D.toFixed(0):D.toFixed(1)} ${d[ue]}`}function z(O){return`${O.slice(0,12)}…`}function Se(O){return Math.min(100,O.quota?O.used/O.quota*100:0)}function ge(O,d){return O.trim().slice(0,1).toUpperCase()||d}function X(O,d=!1){C.value=O,N.value=d,window.clearTimeout(L),L=window.setTimeout(()=>{C.value=""},4800)}async function Ke(){n.value=!0;try{Object.assign(s,await t.status())}finally{n.value=!1}}async function me(){const[O,d]=await Promise.all([t.vaults(),t.devices()]);m.value=O.items,E.value=d.items}async function Xe(){if(!i.value){i.value=!0;try{await me(),X("账户数据已刷新")}catch(O){t.signedIn||Ee(),X(O instanceof Error?O.message:"REFRESH_FAILED",!0)}finally{i.value=!1}}}function Ee(){t.clear(),r.value=!1,o.value="",m.value=[],E.value=[]}async function it(){if(i.value)return;i.value=!0;const O=o.value,d=l.value.trim(),D=f.value.trim();o.value="";try{await t.login(d,O,D),p.value=`${d} · ${D}`,r.value=!0,await me(),X("设备会话已建立")}catch(ue){Ee(),X(ue instanceof Error?ue.message:"LOGIN_FAILED",!0)}finally{i.value=!1}}async function mt(){const O=a.value.trim();if(!(!O||i.value)){i.value=!0;try{await t.createVault(O),a.value="",await me(),X(`已创建 Vault:${O}`)}catch(d){X(d instanceof Error?d.message:"CREATE_VAULT_FAILED",!0)}finally{i.value=!1}}}async function $t(O){if(window.confirm(`撤销设备“${O.name}”?该设备的下一次请求会被拒绝。`)){i.value=!0;try{await t.revokeDevice(O.id),O.id===t.deviceId?(Ee(),X("当前设备已撤销")):(await me(),X(`已撤销设备:${O.name}`))}catch(d){X(d instanceof Error?d.message:"REVOKE_FAILED",!0)}finally{i.value=!1}}}async function Q(){if(!i.value){i.value=!0;try{await t.logout()}finally{Ee(),i.value=!1,X("会话已退出")}}}return xr(()=>{Ke(),Z=window.setInterval(()=>{Ke()},15e3)}),wr(()=>{window.clearInterval(Z),window.clearTimeout(L),Ee()}),(O,d)=>(be(),xe(he,null,[d[36]||(d[36]=g("div",{class:"ambient ambient-one","aria-hidden":"true"},null,-1)),d[37]||(d[37]=g("div",{class:"ambient ambient-two","aria-hidden":"true"},null,-1)),g("header",So,[d[9]||(d[9]=g("a",{class:"brand",href:"/console","aria-label":"OpenNexus Sync Console"},[g("span",{class:"brand-mark","aria-hidden":"true"},[g("i"),g("i"),g("i")]),g("span",null,[g("strong",null,"OpenNexus"),g("small",null,"Sync Console")])],-1)),g("div",Eo,[g("span",{class:st(["status-chip",{ok:s.health,bad:!n.value&&!s.health}])},[d[4]||(d[4]=g("i",null,null,-1)),d[5]||(d[5]=g("b",null,"服务",-1)),g("em",null,G(n.value?"检测中":s.health?"在线":"离线"),1)],2),g("span",{class:st(["status-chip",{ok:s.ready,bad:!n.value&&!s.ready}])},[d[6]||(d[6]=g("i",null,null,-1)),d[7]||(d[7]=g("b",null,"依赖",-1)),g("em",null,G(n.value?"检测中":s.ready?"就绪":"不可用"),1)],2),g("button",{class:"icon-button",type:"button",disabled:n.value,"aria-label":"刷新服务状态",title:"刷新服务状态",onClick:Ke},[...d[8]||(d[8]=[g("svg",{viewBox:"0 0 24 24","aria-hidden":"true"},[g("path",{d:"M20 11a8 8 0 1 0-2.3 5.7M20 4v7h-7"})],-1)])],8,To)])]),g("main",null,[r.value?(be(),xe("section",Mo,[g("div",Ro,[g("div",null,[d[22]||(d[22]=g("div",{class:"eyebrow"},[g("span"),Ie(" Connected workspace")],-1)),d[23]||(d[23]=g("h1",null,"同步空间",-1)),g("p",null,G(p.value),1)]),g("button",{class:"secondary-button",type:"button",disabled:i.value,onClick:Q},"退出登录",8,Fo)]),g("div",Do,[g("article",null,[d[24]||(d[24]=g("span",null,"远端 Vault",-1)),g("strong",null,G(m.value.length),1),d[25]||(d[25]=g("small",null,"当前账户可访问",-1))]),g("article",null,[d[26]||(d[26]=g("span",null,"已使用空间",-1)),g("strong",null,G(P(H.value)),1),g("small",null,"总配额 "+G(P(B.value)),1)]),g("article",null,[d[27]||(d[27]=g("span",null,"设备",-1)),g("strong",null,G(E.value.length),1),g("small",null,G(k.value)+" 台可访问",1)]),g("article",null,[d[28]||(d[28]=g("span",null,"协议",-1)),g("strong",null,"v"+G(s.protocol),1),d[29]||(d[29]=g("small",null,"历史长期保留",-1))])]),g("div",No,[g("section",Lo,[g("div",$o,[d[31]||(d[31]=g("div",null,[g("p",null,"Vaults"),g("h2",null,"远端知识库")],-1)),g("button",{class:"icon-button",type:"button",disabled:i.value,"aria-label":"刷新账户数据",title:"刷新账户数据",onClick:Xe},[...d[30]||(d[30]=[g("svg",{viewBox:"0 0 24 24","aria-hidden":"true"},[g("path",{d:"M20 11a8 8 0 1 0-2.3 5.7M20 4v7h-7"})],-1)])],8,Ho)]),g("form",{class:"create-form",onSubmit:Hn(mt,["prevent"])},[d[32]||(d[32]=g("label",null,"创建新 Vault",-1)),g("div",null,[Kt(g("input",{"onUpdate:modelValue":d[3]||(d[3]=D=>a.value=D),maxlength:"120",required:"",placeholder:"例如:产品知识库"},null,512),[[qt,a.value]]),g("button",{type:"submit",disabled:i.value||!a.value.trim()},"创建",8,jo)])],32),g("div",Vo,[m.value.length?Wt("",!0):(be(),xe("div",Uo,"还没有远端 Vault")),(be(!0),xe(he,null,hn(m.value,D=>(be(),xe("article",{key:D.id,class:"vault-item"},[d[33]||(d[33]=g("div",{class:"vault-symbol"},"N",-1)),g("div",Bo,[g("strong",null,G(D.name),1),g("small",null,G(z(D.id))+" · revision "+G(D.sequence),1),g("progress",{value:Se(D),max:"100"},null,8,Ko)]),g("div",Wo,[g("strong",null,G(P(D.used)),1),g("small",null,"of "+G(P(D.quota)),1)])]))),128))])]),g("section",qo,[d[34]||(d[34]=g("div",{class:"surface-heading"},[g("div",null,[g("p",null,"Devices"),g("h2",null,"设备会话")]),g("span",{class:"secure-badge"},"逐次鉴权")],-1)),d[35]||(d[35]=g("p",{class:"surface-intro"},"撤销会让该设备的下一次请求立即失败。Vault 内容和本地文件不会因此删除。",-1)),g("div",ko,[(be(!0),xe(he,null,hn(E.value,D=>(be(),xe("article",{key:D.id,class:st(["device-item",{revoked:D.revoked}])},[g("div",Jo,G(ge(D.name,"D")),1),g("div",Go,[g("strong",null,G(D.name),1),g("small",null,[Ie(G(D.revoked?"已撤销":"可访问"),1),D.id===ar(t).deviceId?(be(),xe(he,{key:0},[Ie(" · 当前设备")],64)):Wt("",!0)])]),D.revoked?Wt("",!0):(be(),xe("button",{key:0,class:"danger-button",type:"button",disabled:i.value,onClick:ue=>$t(D)},"撤销",8,zo))],2))),128))])])])])):(be(),xe("section",Co,[g("div",Oo,[d[13]||(d[13]=g("div",{class:"eyebrow"},[g("span"),Ie(" Self-hosted workspace sync")],-1)),d[14]||(d[14]=g("h1",null,[Ie("让知识在设备之间"),g("br"),g("em",null,"安静地抵达。")],-1)),d[15]||(d[15]=g("p",null,"OpenNexus Sync 为每个 Vault 保留完整修订历史、附件完整性校验和设备级撤销。此控制台只连接当前服务器,不在浏览器持久保存密码或令牌。",-1)),g("div",Ao,[g("article",null,[g("strong",null,"v"+G(s.protocol),1),d[10]||(d[10]=g("span",null,"同步协议",-1))]),g("article",null,[g("strong",null,G(P(s.maxObjectSize)),1),d[11]||(d[11]=g("span",null,"单对象上限",-1))]),d[12]||(d[12]=g("article",null,[g("strong",null,"SHA-256"),g("span",null,"内容完整性")],-1))])]),g("section",Po,[d[20]||(d[20]=Ll('

设备会话

登录 Sync Server

',2)),g("form",{onSubmit:Hn(it,["prevent"])},[g("label",null,[d[16]||(d[16]=Ie("账户",-1)),Kt(g("input",{"onUpdate:modelValue":d[0]||(d[0]=D=>l.value=D),autocomplete:"username",maxlength:"80",required:"",placeholder:"输入 Sync 账户"},null,512),[[qt,l.value]])]),g("label",null,[d[17]||(d[17]=Ie("密码",-1)),Kt(g("input",{"onUpdate:modelValue":d[1]||(d[1]=D=>o.value=D),type:"password",autocomplete:"current-password",minlength:"12",maxlength:"256",required:"",placeholder:"至少 12 个字符"},null,512),[[qt,o.value]])]),g("label",null,[d[18]||(d[18]=Ie("设备名称",-1)),Kt(g("input",{"onUpdate:modelValue":d[2]||(d[2]=D=>f.value=D),autocomplete:"off",maxlength:"120",required:""},null,512),[[qt,f.value]])]),g("button",{class:"primary-button",type:"submit",disabled:i.value||!o.value},[g("span",null,G(i.value?"正在建立会话…":"建立安全会话"),1),d[19]||(d[19]=g("svg",{viewBox:"0 0 24 24","aria-hidden":"true"},[g("path",{d:"m9 18 6-6-6-6"})],-1))],8,Io)],32),d[21]||(d[21]=g("p",{class:"privacy-note"},[g("span",null,"●"),Ie("密码在发出请求前即从输入框清除;会话令牌只保存在当前页面内存。")],-1))])]))]),d[38]||(d[38]=g("footer",null,[g("span",null,"OpenNexus Sync v1"),g("span",null,"Transport encryption · Device revocation · Immutable history")],-1)),C.value?(be(),xe("div",{key:0,class:st(["toast",{error:N.value}]),role:"status","aria-live":"polite"},G(C.value),3)):Wt("",!0)],64))}});_o(Yo).mount("#app"); diff --git a/server sync/sync_server/static/index.html b/server sync/sync_server/static/index.html new file mode 100644 index 0000000..9e737fc --- /dev/null +++ b/server sync/sync_server/static/index.html @@ -0,0 +1,15 @@ + + + + + + + + OpenNexus Sync Console + + + + +
+ + diff --git a/server sync/tests/test_console.py b/server sync/tests/test_console.py new file mode 100644 index 0000000..8f8a66c --- /dev/null +++ b/server sync/tests/test_console.py @@ -0,0 +1,63 @@ +"""Same-origin Vue console delivery and its public account workflow.""" + +import re + +from fastapi.testclient import TestClient + +from sync_server.app import create_app +from sync_server.database import Database +from sync_server.storage import DiskObjects + + +def test_vue_console_is_hardened_and_uses_the_public_sync_api(tmp_path): + database = Database("sqlite:///" + str(tmp_path / "sync.db")) + app = create_app( + database, + DiskObjects(tmp_path / "objects"), + tmp_path / "staging", + ) + database.add_user("demo", "controlled-demo-password") + with TestClient(app) as client: + root = client.get("/") + console = client.get("/console/") + assert root.status_code == console.status_code == 200 + assert "OpenNexus Sync Console" in console.text + assert "