Compare commits

..

16 Commits
v1.12.1 ... dev

Author SHA1 Message Date
dependabot[bot]
f0f5c3c15c Chore(deps-dev): Bump @tailwindcss/forms from 0.5.10 to 0.5.11 (#6500)
Some checks are pending
Docker CI / Docker Build & Push (push) Waiting to run
Lint / Linting Checks (push) Waiting to run
Release Drafter / Update Release Draft (push) Waiting to run
Release Drafter / Auto Label PR (push) Waiting to run
Tests / vitest (1) (push) Waiting to run
Tests / vitest (2) (push) Waiting to run
Tests / vitest (3) (push) Waiting to run
Tests / vitest (4) (push) Waiting to run
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-01 22:02:55 +00:00
dependabot[bot]
2ed1da4411 Chore(deps): Bump i18next from 25.8.0 to 25.10.9 (#6501)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-01 21:58:21 +00:00
dependabot[bot]
e1023466b1 Chore(deps-dev): Bump @eslint/compat from 2.0.2 to 2.0.3 (#6498)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-01 21:53:25 +00:00
dependabot[bot]
9fe5ad62f1 Chore(deps-dev): Bump postcss from 8.5.6 to 8.5.8 (#6499)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-01 21:47:23 +00:00
dependabot[bot]
173883000f Chore(deps): Bump dockerode from 4.0.7 to 4.0.10 (#6497)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-01 14:42:40 -07:00
shamoon
d6e7e7e790 1.12.3
Some checks are pending
Docker CI / Docker Build & Push (push) Waiting to run
Docs / Test Build Docs (push) Waiting to run
Docs / Build & Deploy Docs (push) Waiting to run
Lint / Linting Checks (push) Waiting to run
Tests / vitest (1) (push) Waiting to run
Tests / vitest (2) (push) Waiting to run
Tests / vitest (3) (push) Waiting to run
Tests / vitest (4) (push) Waiting to run
2026-04-01 08:02:58 -07:00
shamoon
24cb274e03 Fix glances regex 2026-04-01 08:02:03 -07:00
shamoon
af852e748a Normalize widget version in URLs 2026-04-01 08:00:20 -07:00
shamoon
0ea5c3fb68 Bump package version to 1.12.2
Some checks failed
Crowdin Action / Crowdin Sync (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Docs / Test Build Docs (push) Has been cancelled
Docs / Build & Deploy Docs (push) Has been cancelled
Lint / Linting Checks (push) Has been cancelled
Tests / vitest (1) (push) Has been cancelled
Tests / vitest (2) (push) Has been cancelled
Tests / vitest (3) (push) Has been cancelled
Tests / vitest (4) (push) Has been cancelled
Repository Maintenance / Stale (push) Has been cancelled
Repository Maintenance / Lock Old Threads (push) Has been cancelled
Repository Maintenance / Close Answered Discussions (push) Has been cancelled
Repository Maintenance / Close Outdated Discussions (push) Has been cancelled
Repository Maintenance / Close Unsupported Feature Requests (push) Has been cancelled
2026-03-31 07:35:55 -07:00
shamoon
5ede96d6ce Merge branch 'dev' 2026-03-31 07:35:43 -07:00
github-actions[bot]
c50bc8601d New Crowdin translations by GitHub Action (#6470)
Some checks failed
Docker CI / Docker Build & Push (push) Has been cancelled
Lint / Linting Checks (push) Has been cancelled
Release Drafter / Update Release Draft (push) Has been cancelled
Release Drafter / Auto Label PR (push) Has been cancelled
Tests / vitest (1) (push) Has been cancelled
Tests / vitest (2) (push) Has been cancelled
Tests / vitest (3) (push) Has been cancelled
Tests / vitest (4) (push) Has been cancelled
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-03-31 07:34:56 -07:00
shamoon
463bb4e306 Chore: move lint checks to separate workflow (#6481)
Some checks failed
Docker CI / Docker Build & Push (push) Has been cancelled
Lint / Linting Checks (push) Has been cancelled
Release Drafter / Update Release Draft (push) Has been cancelled
Release Drafter / Auto Label PR (push) Has been cancelled
Tests / vitest (1) (push) Has been cancelled
Tests / vitest (2) (push) Has been cancelled
Tests / vitest (3) (push) Has been cancelled
Tests / vitest (4) (push) Has been cancelled
2026-03-29 13:18:45 -07:00
shamoon
4c3c4805c8 Security: pin GitHub Actions to specific SHAs (#6480) 2026-03-29 13:04:10 -07:00
Alex
a81ac47be9 Fix: fix compatibility with flood changes (#6477) 2026-03-29 13:48:43 +00:00
dependabot[bot]
36b909d4a4 Chore(deps): Bump brace-expansion from 1.1.12 to 1.1.13 in the npm_and_yarn group across 1 directory (#6478)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-29 06:15:45 -07:00
shamoon
a7fe80a399 Documentation: fix UniFi admonitions
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Docs / Linting Checks (push) Has been cancelled
Docs / Test Build Docs (push) Has been cancelled
Docs / Build & Deploy Docs (push) Has been cancelled
Tests / vitest (3) (push) Has been cancelled
Tests / vitest (4) (push) Has been cancelled
Tests / vitest (1) (push) Has been cancelled
Tests / vitest (2) (push) Has been cancelled
Crowdin Action / Crowdin Sync (push) Has been cancelled
2026-03-28 21:05:28 -07:00
36 changed files with 334 additions and 255 deletions

View File

@@ -17,9 +17,9 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: crowdin action - name: crowdin action
uses: crowdin/github-action@v2 uses: crowdin/github-action@7ca9c452bfe9197d3bb7fa83a4d7e2b0c9ae835d # v2
with: with:
upload_translations: false upload_translations: false
download_translations: true download_translations: true

View File

@@ -17,44 +17,12 @@ env:
IMAGE_NAME: ${{ github.repository }} IMAGE_NAME: ${{ github.repository }}
jobs: jobs:
pre-commit:
name: Linting Checks
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install python
uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Check files
uses: pre-commit/action@v3.0.1
- name: Install pnpm
uses: pnpm/action-setup@v5
with:
version: 10
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Lint frontend
run: pnpm run lint
build: build:
name: Docker Build & Push name: Docker Build & Push
if: github.repository == 'gethomepage/homepage' if: github.repository == 'gethomepage/homepage'
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: [ pre-commit ]
permissions: permissions:
contents: read contents: read
packages: write packages: write
@@ -62,11 +30,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Extract Docker metadata - name: Extract Docker metadata
id: meta id: meta
uses: docker/metadata-action@v6 uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
with: with:
images: | images: |
${{ env.IMAGE_NAME }} ${{ env.IMAGE_NAME }}
@@ -84,7 +52,7 @@ jobs:
latest=auto latest=auto
- name: Next.js build cache - name: Next.js build cache
uses: actions/cache@v5 uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with: with:
path: .next/cache path: .next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx') }} key: nextjs-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx') }}
@@ -92,13 +60,13 @@ jobs:
nextjs-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} nextjs-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v5 uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with: with:
version: 10 version: 10
run_install: false run_install: false
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with: with:
node-version: 24 node-version: 24
cache: 'pnpm' cache: 'pnpm'
@@ -115,7 +83,7 @@ jobs:
- name: Log into registry ${{ env.REGISTRY }} - name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v4 uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -123,20 +91,20 @@ jobs:
- name: Login to Docker Hub - name: Login to Docker Hub
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: docker/login-action@v4 uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@v4.0.0 uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- name: Setup Docker buildx - name: Setup Docker buildx
uses: docker/setup-buildx-action@v4 uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
- name: Build and push Docker image - name: Build and push Docker image
id: build-and-push id: build-and-push
uses: docker/build-push-action@v7 uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7
with: with:
context: . context: .
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}

View File

@@ -14,32 +14,18 @@ permissions:
id-token: write id-token: write
jobs: jobs:
pre-commit:
name: Linting Checks
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install python
uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Check files
uses: pre-commit/action@v3.0.1
test: test:
name: Test Build Docs name: Test Build Docs
if: github.repository == 'gethomepage/homepage' && github.event_name == 'pull_request' if: github.repository == 'gethomepage/homepage' && github.event_name == 'pull_request'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- pre-commit
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with: with:
python-version-file: ".python-version" python-version-file: ".python-version"
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v7 uses: astral-sh/setup-uv@94527f2e458b27549849d47d273a16bec83a01e9 # v7
- run: sudo apt-get install pngquant - run: sudo apt-get install pngquant
- name: Test Docs Build - name: Test Docs Build
run: uv run --frozen zensical build --clean run: uv run --frozen zensical build --clean
@@ -50,21 +36,19 @@ jobs:
environment: environment:
name: github-pages name: github-pages
url: ${{ steps.deployment.outputs.page_url }} url: ${{ steps.deployment.outputs.page_url }}
needs:
- pre-commit
steps: steps:
- uses: actions/configure-pages@v5 - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
- uses: actions/checkout@v6 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with: with:
python-version-file: ".python-version" python-version-file: ".python-version"
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v7 uses: astral-sh/setup-uv@94527f2e458b27549849d47d273a16bec83a01e9 # v7
- run: sudo apt-get install pngquant - run: sudo apt-get install pngquant
- name: Build Docs - name: Build Docs
run: uv run --frozen zensical build --clean run: uv run --frozen zensical build --clean
- uses: actions/upload-pages-artifact@v4 - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
with: with:
path: site path: site
- uses: actions/deploy-pages@v4 - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
id: deployment id: deployment

41
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Lint
on:
pull_request:
push:
workflow_dispatch:
merge_group:
jobs:
lint:
name: Linting Checks
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: 3.x
- name: Check files
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
- name: Install pnpm
uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with:
version: 10
run_install: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Lint frontend
run: pnpm run lint

View File

@@ -13,6 +13,6 @@ jobs:
anti-slop: anti-slop:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: peakoss/anti-slop@v0 - uses: peakoss/anti-slop@a5a4b2440c9de6f65b64f0718a0136a1fdb04f6f # v0
with: with:
max-failures: 4 max-failures: 4

View File

@@ -15,4 +15,4 @@ jobs:
action: action:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/reaction-comments@v4 - uses: dessant/reaction-comments@e86d247c12bd5c043eec379a1a4453f20cadf913 # v4

View File

@@ -26,14 +26,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- if: github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' - if: github.event_name == 'workflow_dispatch' && github.event.inputs.version != ''
uses: release-drafter/release-drafter@v7 uses: release-drafter/release-drafter@a6acf82562eee06318b77ab8cb0b11ed81c677a7 # v7
with: with:
config-name: release-drafter.yml config-name: release-drafter.yml
version: ${{ github.event.inputs.version }} version: ${{ github.event.inputs.version }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- if: github.event_name != 'workflow_dispatch' || github.event.inputs.version == '' - if: github.event_name != 'workflow_dispatch' || github.event.inputs.version == ''
uses: release-drafter/release-drafter@v7 uses: release-drafter/release-drafter@a6acf82562eee06318b77ab8cb0b11ed81c677a7 # v7
with: with:
config-name: release-drafter.yml config-name: release-drafter.yml
env: env:

View File

@@ -18,7 +18,7 @@ jobs:
name: 'Stale' name: 'Stale'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v10 - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10
with: with:
days-before-stale: 7 days-before-stale: 7
days-before-close: 14 days-before-close: 14
@@ -32,7 +32,7 @@ jobs:
name: 'Lock Old Threads' name: 'Lock Old Threads'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v6 - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6
with: with:
issue-inactive-days: '30' issue-inactive-days: '30'
pr-inactive-days: '30' pr-inactive-days: '30'
@@ -57,7 +57,7 @@ jobs:
name: 'Close Answered Discussions' name: 'Close Answered Discussions'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v8 - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with: with:
script: | script: |
function sleep(ms) { function sleep(ms) {
@@ -113,7 +113,7 @@ jobs:
name: 'Close Outdated Discussions' name: 'Close Outdated Discussions'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v8 - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with: with:
script: | script: |
function sleep(ms) { function sleep(ms) {
@@ -204,7 +204,7 @@ jobs:
name: 'Close Unsupported Feature Requests' name: 'Close Unsupported Feature Requests'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v8 - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with: with:
script: | script: |
function sleep(ms) { function sleep(ms) {

View File

@@ -13,13 +13,13 @@ jobs:
matrix: matrix:
shard: [1, 2, 3, 4] shard: [1, 2, 3, 4]
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: pnpm/action-setup@v5 - uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with: with:
version: 9 version: 9
- uses: actions/setup-node@v6 - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with: with:
node-version: 20 node-version: 20
cache: pnpm cache: pnpm
@@ -28,7 +28,7 @@ jobs:
# Run Vitest directly so `--shard` is parsed as an option # Run Vitest directly so `--shard` is parsed as an option
- run: pnpm -s exec vitest run --coverage --shard ${{ matrix.shard }}/4 --pool forks - run: pnpm -s exec vitest run --coverage --shard ${{ matrix.shard }}/4 --pool forks
- name: Upload coverage reports to Codecov - name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info files: ./coverage/lcov.info

View File

@@ -13,7 +13,7 @@ You can display general connectivity status from your Unifi (Network) Controller
An optional 'site' parameter can be supplied, if it is not the widget will use the default site for the controller. An optional 'site' parameter can be supplied, if it is not the widget will use the default site for the controller.
!!! hint !!! tip
If you enter e.g. incorrect credentials and receive an "API Error", you may need to recreate the container to clear the cache. If you enter e.g. incorrect credentials and receive an "API Error", you may need to recreate the container to clear the cache.

View File

@@ -17,7 +17,7 @@ An optional 'site' parameter can be supplied, if it is not the widget will use t
Allowed fields: `["uptime", "wan", "lan", "lan_users", "lan_devices", "wlan", "wlan_users", "wlan_devices"]` (maximum of four). Fields unsupported by the unifi device will not be shown. Allowed fields: `["uptime", "wan", "lan", "lan_users", "lan_devices", "wlan", "wlan_users", "wlan_devices"]` (maximum of four). Fields unsupported by the unifi device will not be shown.
!!! hint !!! tip
If you enter e.g. incorrect credentials and receive an "API Error", you may need to recreate the container or restart the service to clear the cache. If you enter e.g. incorrect credentials and receive an "API Error", you may need to recreate the container or restart the service to clear the cache.

View File

@@ -19,6 +19,6 @@ widget:
password: your_password password: your_password
``` ```
!!! hint !!! tip
If you enter incorrect credentials and receive an "API Error", you may need to recreate the container or restart the service to clear the cache. If you enter incorrect credentials and receive an "API Error", you may need to recreate the container or restart the service to clear the cache.

View File

@@ -1,6 +1,6 @@
{ {
"name": "homepage", "name": "homepage",
"version": "1.12.0", "version": "1.12.3",
"private": true, "private": true,
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
@@ -18,10 +18,10 @@
"@kubernetes/client-node": "^1.0.0", "@kubernetes/client-node": "^1.0.0",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"compare-versions": "^6.1.1", "compare-versions": "^6.1.1",
"dockerode": "^4.0.7", "dockerode": "^4.0.10",
"follow-redirects": "^1.15.11", "follow-redirects": "^1.15.11",
"gamedig": "^5.3.2", "gamedig": "^5.3.2",
"i18next": "^25.8.0", "i18next": "^25.10.9",
"ical.js": "^2.2.1", "ical.js": "^2.2.1",
"js-yaml": "^4.1.1", "js-yaml": "^4.1.1",
"json-rpc-2.0": "^1.7.0", "json-rpc-2.0": "^1.7.0",
@@ -47,10 +47,10 @@
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"devDependencies": { "devDependencies": {
"@eslint/compat": "^2.0.2", "@eslint/compat": "^2.0.3",
"@eslint/eslintrc": "^3.3.3", "@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.2", "@eslint/js": "^9.39.2",
"@tailwindcss/forms": "^0.5.10", "@tailwindcss/forms": "^0.5.11",
"@tailwindcss/postcss": "^4.1.18", "@tailwindcss/postcss": "^4.1.18",
"@testing-library/jest-dom": "^6.8.0", "@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^16.3.0", "@testing-library/react": "^16.3.0",
@@ -64,7 +64,7 @@
"eslint-plugin-react": "^7.37.4", "eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"jsdom": "^28.1.0", "jsdom": "^28.1.0",
"postcss": "^8.5.6", "postcss": "^8.5.8",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"prettier-plugin-organize-imports": "^4.3.0", "prettier-plugin-organize-imports": "^4.3.0",
"tailwind-scrollbar": "^4.0.2", "tailwind-scrollbar": "^4.0.2",

228
pnpm-lock.yaml generated
View File

@@ -21,8 +21,8 @@ importers:
specifier: ^6.1.1 specifier: ^6.1.1
version: 6.1.1 version: 6.1.1
dockerode: dockerode:
specifier: ^4.0.7 specifier: ^4.0.10
version: 4.0.7 version: 4.0.10
follow-redirects: follow-redirects:
specifier: ^1.15.11 specifier: ^1.15.11
version: 1.15.11 version: 1.15.11
@@ -30,8 +30,8 @@ importers:
specifier: ^5.3.2 specifier: ^5.3.2
version: 5.3.2 version: 5.3.2
i18next: i18next:
specifier: ^25.8.0 specifier: ^25.10.9
version: 25.8.0(typescript@5.7.3) version: 25.10.9(typescript@5.7.3)
ical.js: ical.js:
specifier: ^2.2.1 specifier: ^2.2.1
version: 2.2.1 version: 2.2.1
@@ -55,7 +55,7 @@ importers:
version: 16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
next-i18next: next-i18next:
specifier: ^15.4.3 specifier: ^15.4.3
version: 15.4.3(@types/react@19.0.10)(i18next@25.8.0(typescript@5.7.3))(next@16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-i18next@15.5.3(i18next@25.8.0(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3))(react@19.2.4) version: 15.4.3(@types/react@19.0.10)(i18next@25.10.9(typescript@5.7.3))(next@16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-i18next@15.5.3(i18next@25.10.9(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3))(react@19.2.4)
ping: ping:
specifier: ^0.4.4 specifier: ^0.4.4
version: 0.4.4 version: 0.4.4
@@ -73,7 +73,7 @@ importers:
version: 19.2.4(react@19.2.4) version: 19.2.4(react@19.2.4)
react-i18next: react-i18next:
specifier: ^15.5.3 specifier: ^15.5.3
version: 15.5.3(i18next@25.8.0(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3) version: 15.5.3(i18next@25.10.9(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3)
react-icons: react-icons:
specifier: ^5.6.0 specifier: ^5.6.0
version: 5.6.0(react@19.2.4) version: 5.6.0(react@19.2.4)
@@ -103,8 +103,8 @@ importers:
version: 1.6.11 version: 1.6.11
devDependencies: devDependencies:
'@eslint/compat': '@eslint/compat':
specifier: ^2.0.2 specifier: ^2.0.3
version: 2.0.2(eslint@9.25.1(jiti@2.6.1)) version: 2.0.3(eslint@9.25.1(jiti@2.6.1))
'@eslint/eslintrc': '@eslint/eslintrc':
specifier: ^3.3.3 specifier: ^3.3.3
version: 3.3.3 version: 3.3.3
@@ -112,8 +112,8 @@ importers:
specifier: ^9.39.2 specifier: ^9.39.2
version: 9.39.2 version: 9.39.2
'@tailwindcss/forms': '@tailwindcss/forms':
specifier: ^0.5.10 specifier: ^0.5.11
version: 0.5.10(tailwindcss@4.1.18) version: 0.5.11(tailwindcss@4.1.18)
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4.1.18 specifier: ^4.1.18
version: 4.1.18 version: 4.1.18
@@ -125,7 +125,7 @@ importers:
version: 16.3.2(@testing-library/dom@10.4.1)(@types/react@19.0.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 16.3.2(@testing-library/dom@10.4.1)(@types/react@19.0.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: ^3.2.4 specifier: ^3.2.4
version: 3.2.4(vitest@3.2.4(@types/node@24.1.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)) version: 3.2.4(vitest@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2))
eslint: eslint:
specifier: ^9.25.1 specifier: ^9.25.1
version: 9.25.1(jiti@2.6.1) version: 9.25.1(jiti@2.6.1)
@@ -154,8 +154,8 @@ importers:
specifier: ^28.1.0 specifier: ^28.1.0
version: 28.1.0 version: 28.1.0
postcss: postcss:
specifier: ^8.5.6 specifier: ^8.5.8
version: 8.5.6 version: 8.5.8
prettier: prettier:
specifier: ^3.8.1 specifier: ^3.8.1
version: 3.8.1 version: 3.8.1
@@ -173,7 +173,7 @@ importers:
version: 5.7.3 version: 5.7.3
vitest: vitest:
specifier: ^3.2.4 specifier: ^3.2.4
version: 3.2.4(@types/node@24.1.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2) version: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)
optionalDependencies: optionalDependencies:
osx-temperature-sensor: osx-temperature-sensor:
specifier: ^1.0.8 specifier: ^1.0.8
@@ -462,8 +462,8 @@ packages:
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint/compat@2.0.2': '@eslint/compat@2.0.3':
resolution: {integrity: sha512-pR1DoD0h3HfF675QZx0xsyrsU8q70Z/plx7880NOhS02NuWLgBCOMDL787nUeQ7EWLkxv3bPQJaarjcPQb2Dwg==} resolution: {integrity: sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
peerDependencies: peerDependencies:
eslint: ^8.40 || 9 || 10 eslint: ^8.40 || 9 || 10
@@ -483,8 +483,8 @@ packages:
resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==} resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/core@1.1.0': '@eslint/core@1.1.1':
resolution: {integrity: sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==} resolution: {integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
'@eslint/eslintrc@3.3.3': '@eslint/eslintrc@3.3.3':
@@ -537,8 +537,8 @@ packages:
'@floating-ui/utils@0.2.10': '@floating-ui/utils@0.2.10':
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
'@grpc/grpc-js@1.13.4': '@grpc/grpc-js@1.14.3':
resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==}
engines: {node: '>=12.10.0'} engines: {node: '>=12.10.0'}
'@grpc/proto-loader@0.7.15': '@grpc/proto-loader@0.7.15':
@@ -546,6 +546,11 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
hasBin: true hasBin: true
'@grpc/proto-loader@0.8.0':
resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==}
engines: {node: '>=6'}
hasBin: true
'@headlessui/react@2.2.9': '@headlessui/react@2.2.9':
resolution: {integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==} resolution: {integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -1069,8 +1074,8 @@ packages:
resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==}
engines: {node: '>=14.16'} engines: {node: '>=14.16'}
'@tailwindcss/forms@0.5.10': '@tailwindcss/forms@0.5.11':
resolution: {integrity: sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==} resolution: {integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==}
peerDependencies: peerDependencies:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1' tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
@@ -1262,8 +1267,8 @@ packages:
'@types/node@22.13.4': '@types/node@22.13.4':
resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==} resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==}
'@types/node@24.1.0': '@types/node@25.5.0':
resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==} resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
'@types/prismjs@1.26.5': '@types/prismjs@1.26.5':
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
@@ -1593,11 +1598,11 @@ packages:
bl@4.1.0: bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
brace-expansion@1.1.12: brace-expansion@1.1.13:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==}
brace-expansion@2.0.2: brace-expansion@2.0.3:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==}
braces@3.0.3: braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
@@ -1606,8 +1611,8 @@ packages:
buffer@5.7.1: buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
buildcheck@0.0.6: buildcheck@0.0.7:
resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} resolution: {integrity: sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
bytes@3.1.2: bytes@3.1.2:
@@ -1890,12 +1895,12 @@ packages:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
docker-modem@5.0.6: docker-modem@5.0.7:
resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} resolution: {integrity: sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==}
engines: {node: '>= 8.0'} engines: {node: '>= 8.0'}
dockerode@4.0.7: dockerode@4.0.10:
resolution: {integrity: sha512-R+rgrSRTRdU5mH14PZTCPZtW/zw3HDWNTS/1ZAQpL/5Upe/ye5K9WQkIysu4wBoiMwKynsz0a8qWuGsHgEvSAA==} resolution: {integrity: sha512-8L/P9JynLBiG7/coiA4FlQXegHltRqS0a+KqI44P1zgQh8QLHTg7FKOwhkBgSJwZTeHsq30WRoVFLuwkfK0YFg==}
engines: {node: '>= 8.0'} engines: {node: '>= 8.0'}
doctrine@2.1.0: doctrine@2.1.0:
@@ -2388,10 +2393,10 @@ packages:
i18next-fs-backend@2.6.1: i18next-fs-backend@2.6.1:
resolution: {integrity: sha512-eYWTX7QT7kJ0sZyCPK6x1q+R63zvNKv2D6UdbMf15A8vNb2ZLyw4NNNZxPFwXlIv/U+oUtg8SakW6ZgJZcoqHQ==} resolution: {integrity: sha512-eYWTX7QT7kJ0sZyCPK6x1q+R63zvNKv2D6UdbMf15A8vNb2ZLyw4NNNZxPFwXlIv/U+oUtg8SakW6ZgJZcoqHQ==}
i18next@25.8.0: i18next@25.10.9:
resolution: {integrity: sha512-urrg4HMFFMQZ2bbKRK7IZ8/CTE7D8H4JRlAwqA2ZwDRFfdd0K/4cdbNNLgfn9mo+I/h9wJu61qJzH7jCFAhUZQ==} resolution: {integrity: sha512-hQY9/bFoQKGlSKMlaCuLR8w1h5JjieqrsnZvEmj1Ja6Ec7fbyc4cTrCsY9mb9Sd8YQ/swsrKz1S9M8AcvVI70w==}
peerDependencies: peerDependencies:
typescript: ^5 typescript: ^5 || ^6
peerDependenciesMeta: peerDependenciesMeta:
typescript: typescript:
optional: true optional: true
@@ -2862,8 +2867,8 @@ packages:
ms@2.1.3: ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nan@2.23.0: nan@2.26.2:
resolution: {integrity: sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==} resolution: {integrity: sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==}
nanoid@3.3.11: nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
@@ -3043,8 +3048,8 @@ packages:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
postcss@8.5.6: postcss@8.5.8:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1: prelude-ls@1.2.1:
@@ -3089,12 +3094,12 @@ packages:
prop-types@15.8.1: prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
protobufjs@7.5.3: protobufjs@7.5.4:
resolution: {integrity: sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==} resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}
pump@3.0.3: pump@3.0.4:
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
punycode@2.3.1: punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
@@ -3357,8 +3362,8 @@ packages:
split-ca@1.0.1: split-ca@1.0.1:
resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==}
ssh2@1.16.0: ssh2@1.17.0:
resolution: {integrity: sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==} resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==}
engines: {node: '>=10.16.0'} engines: {node: '>=10.16.0'}
stable-hash@0.0.5: stable-hash@0.0.5:
@@ -3512,8 +3517,8 @@ packages:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'} engines: {node: '>=6'}
tar-fs@2.1.3: tar-fs@2.1.4:
resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
tar-stream@2.2.0: tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
@@ -3646,8 +3651,8 @@ packages:
undici-types@6.20.0: undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
undici-types@7.8.0: undici-types@7.18.2:
resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
undici@7.24.4: undici@7.24.4:
resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==}
@@ -4087,9 +4092,9 @@ snapshots:
'@eslint-community/regexpp@4.12.1': {} '@eslint-community/regexpp@4.12.1': {}
'@eslint/compat@2.0.2(eslint@9.25.1(jiti@2.6.1))': '@eslint/compat@2.0.3(eslint@9.25.1(jiti@2.6.1))':
dependencies: dependencies:
'@eslint/core': 1.1.0 '@eslint/core': 1.1.1
optionalDependencies: optionalDependencies:
eslint: 9.25.1(jiti@2.6.1) eslint: 9.25.1(jiti@2.6.1)
@@ -4107,7 +4112,7 @@ snapshots:
dependencies: dependencies:
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
'@eslint/core@1.1.0': '@eslint/core@1.1.1':
dependencies: dependencies:
'@types/json-schema': 7.0.15 '@types/json-schema': 7.0.15
@@ -4163,16 +4168,23 @@ snapshots:
'@floating-ui/utils@0.2.10': {} '@floating-ui/utils@0.2.10': {}
'@grpc/grpc-js@1.13.4': '@grpc/grpc-js@1.14.3':
dependencies: dependencies:
'@grpc/proto-loader': 0.7.15 '@grpc/proto-loader': 0.8.0
'@js-sdsl/ordered-map': 4.4.2 '@js-sdsl/ordered-map': 4.4.2
'@grpc/proto-loader@0.7.15': '@grpc/proto-loader@0.7.15':
dependencies: dependencies:
lodash.camelcase: 4.3.0 lodash.camelcase: 4.3.0
long: 5.3.2 long: 5.3.2
protobufjs: 7.5.3 protobufjs: 7.5.4
yargs: 17.7.2
'@grpc/proto-loader@0.8.0':
dependencies:
lodash.camelcase: 4.3.0
long: 5.3.2
protobufjs: 7.5.4
yargs: 17.7.2 yargs: 17.7.2
'@headlessui/react@2.2.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@headlessui/react@2.2.9(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
@@ -4606,7 +4618,7 @@ snapshots:
dependencies: dependencies:
defer-to-connect: 2.0.1 defer-to-connect: 2.0.1
'@tailwindcss/forms@0.5.10(tailwindcss@4.1.18)': '@tailwindcss/forms@0.5.11(tailwindcss@4.1.18)':
dependencies: dependencies:
mini-svg-data-uri: 1.4.4 mini-svg-data-uri: 1.4.4
tailwindcss: 4.1.18 tailwindcss: 4.1.18
@@ -4677,7 +4689,7 @@ snapshots:
'@alloc/quick-lru': 5.2.0 '@alloc/quick-lru': 5.2.0
'@tailwindcss/node': 4.1.18 '@tailwindcss/node': 4.1.18
'@tailwindcss/oxide': 4.1.18 '@tailwindcss/oxide': 4.1.18
postcss: 8.5.6 postcss: 8.5.8
tailwindcss: 4.1.18 tailwindcss: 4.1.18
'@tanstack/react-virtual@3.13.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': '@tanstack/react-virtual@3.13.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
@@ -4781,9 +4793,9 @@ snapshots:
dependencies: dependencies:
undici-types: 6.20.0 undici-types: 6.20.0
'@types/node@24.1.0': '@types/node@25.5.0':
dependencies: dependencies:
undici-types: 7.8.0 undici-types: 7.18.2
'@types/prismjs@1.26.5': {} '@types/prismjs@1.26.5': {}
@@ -4859,7 +4871,7 @@ snapshots:
dependencies: dependencies:
'@typescript-eslint/types': 8.29.0 '@typescript-eslint/types': 8.29.0
'@typescript-eslint/visitor-keys': 8.29.0 '@typescript-eslint/visitor-keys': 8.29.0
debug: 4.4.3 debug: 4.4.1
fast-glob: 3.3.3 fast-glob: 3.3.3
is-glob: 4.0.3 is-glob: 4.0.3
minimatch: 9.0.5 minimatch: 9.0.5
@@ -4932,7 +4944,7 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.3.3': '@unrs/resolver-binding-win32-x64-msvc@1.3.3':
optional: true optional: true
'@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@24.1.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2))': '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2))':
dependencies: dependencies:
'@ampproject/remapping': 2.3.0 '@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2 '@bcoe/v8-coverage': 1.0.2
@@ -4947,7 +4959,7 @@ snapshots:
std-env: 3.10.0 std-env: 3.10.0
test-exclude: 7.0.1 test-exclude: 7.0.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vitest: 3.2.4(@types/node@24.1.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2) vitest: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -4959,13 +4971,13 @@ snapshots:
chai: 5.3.3 chai: 5.3.3
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
'@vitest/mocker@3.2.4(vite@7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2))': '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2))':
dependencies: dependencies:
'@vitest/spy': 3.2.4 '@vitest/spy': 3.2.4
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.21 magic-string: 0.30.21
optionalDependencies: optionalDependencies:
vite: 7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2) vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2)
'@vitest/pretty-format@3.2.4': '@vitest/pretty-format@3.2.4':
dependencies: dependencies:
@@ -5162,12 +5174,12 @@ snapshots:
inherits: 2.0.4 inherits: 2.0.4
readable-stream: 3.6.2 readable-stream: 3.6.2
brace-expansion@1.1.12: brace-expansion@1.1.13:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
concat-map: 0.0.1 concat-map: 0.0.1
brace-expansion@2.0.2: brace-expansion@2.0.3:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
@@ -5180,7 +5192,7 @@ snapshots:
base64-js: 1.5.1 base64-js: 1.5.1
ieee754: 1.2.1 ieee754: 1.2.1
buildcheck@0.0.6: buildcheck@0.0.7:
optional: true optional: true
bytes@3.1.2: {} bytes@3.1.2: {}
@@ -5293,8 +5305,8 @@ snapshots:
cpu-features@0.0.10: cpu-features@0.0.10:
dependencies: dependencies:
buildcheck: 0.0.6 buildcheck: 0.0.7
nan: 2.23.0 nan: 2.26.2
optional: true optional: true
cross-spawn@7.0.6: cross-spawn@7.0.6:
@@ -5434,23 +5446,23 @@ snapshots:
detect-libc@2.1.2: {} detect-libc@2.1.2: {}
docker-modem@5.0.6: docker-modem@5.0.7:
dependencies: dependencies:
debug: 4.4.1 debug: 4.4.3
readable-stream: 3.6.2 readable-stream: 3.6.2
split-ca: 1.0.1 split-ca: 1.0.1
ssh2: 1.16.0 ssh2: 1.17.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dockerode@4.0.7: dockerode@4.0.10:
dependencies: dependencies:
'@balena/dockerignore': 1.0.2 '@balena/dockerignore': 1.0.2
'@grpc/grpc-js': 1.13.4 '@grpc/grpc-js': 1.14.3
'@grpc/proto-loader': 0.7.15 '@grpc/proto-loader': 0.7.15
docker-modem: 5.0.6 docker-modem: 5.0.7
protobufjs: 7.5.3 protobufjs: 7.5.4
tar-fs: 2.1.3 tar-fs: 2.1.4
uuid: 10.0.0 uuid: 10.0.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -6182,9 +6194,9 @@ snapshots:
i18next-fs-backend@2.6.1: {} i18next-fs-backend@2.6.1: {}
i18next@25.8.0(typescript@5.7.3): i18next@25.10.9(typescript@5.7.3):
dependencies: dependencies:
'@babel/runtime': 7.28.6 '@babel/runtime': 7.29.2
optionalDependencies: optionalDependencies:
typescript: 5.7.3 typescript: 5.7.3
@@ -6598,11 +6610,11 @@ snapshots:
minimatch@3.1.2: minimatch@3.1.2:
dependencies: dependencies:
brace-expansion: 1.1.12 brace-expansion: 1.1.13
minimatch@9.0.5: minimatch@9.0.5:
dependencies: dependencies:
brace-expansion: 2.0.2 brace-expansion: 2.0.3
minimist@1.2.8: {} minimist@1.2.8: {}
@@ -6618,7 +6630,7 @@ snapshots:
ms@2.1.3: {} ms@2.1.3: {}
nan@2.23.0: nan@2.26.2:
optional: true optional: true
nanoid@3.3.11: {} nanoid@3.3.11: {}
@@ -6627,17 +6639,17 @@ snapshots:
net@1.0.2: {} net@1.0.2: {}
next-i18next@15.4.3(@types/react@19.0.10)(i18next@25.8.0(typescript@5.7.3))(next@16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-i18next@15.5.3(i18next@25.8.0(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3))(react@19.2.4): next-i18next@15.4.3(@types/react@19.0.10)(i18next@25.10.9(typescript@5.7.3))(next@16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-i18next@15.5.3(i18next@25.10.9(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3))(react@19.2.4):
dependencies: dependencies:
'@babel/runtime': 7.28.6 '@babel/runtime': 7.28.6
'@types/hoist-non-react-statics': 3.3.7(@types/react@19.0.10) '@types/hoist-non-react-statics': 3.3.7(@types/react@19.0.10)
core-js: 3.48.0 core-js: 3.48.0
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
i18next: 25.8.0(typescript@5.7.3) i18next: 25.10.9(typescript@5.7.3)
i18next-fs-backend: 2.6.1 i18next-fs-backend: 2.6.1
next: 16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: 16.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
react: 19.2.4 react: 19.2.4
react-i18next: 15.5.3(i18next@25.8.0(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3) react-i18next: 15.5.3(i18next@25.10.9(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/react' - '@types/react'
@@ -6796,7 +6808,7 @@ snapshots:
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
postcss@8.5.6: postcss@8.5.8:
dependencies: dependencies:
nanoid: 3.3.11 nanoid: 3.3.11
picocolors: 1.1.1 picocolors: 1.1.1
@@ -6837,7 +6849,7 @@ snapshots:
object-assign: 4.1.1 object-assign: 4.1.1
react-is: 16.13.1 react-is: 16.13.1
protobufjs@7.5.3: protobufjs@7.5.4:
dependencies: dependencies:
'@protobufjs/aspromise': 1.1.2 '@protobufjs/aspromise': 1.1.2
'@protobufjs/base64': 1.1.2 '@protobufjs/base64': 1.1.2
@@ -6849,10 +6861,10 @@ snapshots:
'@protobufjs/path': 1.1.2 '@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0 '@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0 '@protobufjs/utf8': 1.1.0
'@types/node': 24.1.0 '@types/node': 25.5.0
long: 5.3.2 long: 5.3.2
pump@3.0.3: pump@3.0.4:
dependencies: dependencies:
end-of-stream: 1.4.5 end-of-stream: 1.4.5
once: 1.4.0 once: 1.4.0
@@ -6875,11 +6887,11 @@ snapshots:
react: 19.2.4 react: 19.2.4
scheduler: 0.27.0 scheduler: 0.27.0
react-i18next@15.5.3(i18next@25.8.0(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3): react-i18next@15.5.3(i18next@25.10.9(typescript@5.7.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.7.3):
dependencies: dependencies:
'@babel/runtime': 7.27.6 '@babel/runtime': 7.27.6
html-parse-stringify: 3.0.1 html-parse-stringify: 3.0.1
i18next: 25.8.0(typescript@5.7.3) i18next: 25.10.9(typescript@5.7.3)
react: 19.2.4 react: 19.2.4
optionalDependencies: optionalDependencies:
react-dom: 19.2.4(react@19.2.4) react-dom: 19.2.4(react@19.2.4)
@@ -7190,13 +7202,13 @@ snapshots:
split-ca@1.0.1: {} split-ca@1.0.1: {}
ssh2@1.16.0: ssh2@1.17.0:
dependencies: dependencies:
asn1: 0.2.6 asn1: 0.2.6
bcrypt-pbkdf: 1.0.2 bcrypt-pbkdf: 1.0.2
optionalDependencies: optionalDependencies:
cpu-features: 0.0.10 cpu-features: 0.0.10
nan: 2.23.0 nan: 2.26.2
stable-hash@0.0.5: {} stable-hash@0.0.5: {}
@@ -7356,11 +7368,11 @@ snapshots:
tapable@2.3.0: {} tapable@2.3.0: {}
tar-fs@2.1.3: tar-fs@2.1.4:
dependencies: dependencies:
chownr: 1.1.4 chownr: 1.1.4
mkdirp-classic: 0.5.3 mkdirp-classic: 0.5.3
pump: 3.0.3 pump: 3.0.4
tar-stream: 2.2.0 tar-stream: 2.2.0
tar-stream@2.2.0: tar-stream@2.2.0:
@@ -7507,7 +7519,7 @@ snapshots:
undici-types@6.20.0: {} undici-types@6.20.0: {}
undici-types@7.8.0: {} undici-types@7.18.2: {}
undici@7.24.4: {} undici@7.24.4: {}
@@ -7570,13 +7582,13 @@ snapshots:
d3-time: 3.1.0 d3-time: 3.1.0
d3-timer: 3.0.1 d3-timer: 3.0.1
vite-node@3.2.4(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2): vite-node@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2):
dependencies: dependencies:
cac: 6.7.14 cac: 6.7.14
debug: 4.4.1 debug: 4.4.1
es-module-lexer: 1.7.0 es-module-lexer: 1.7.0
pathe: 2.0.3 pathe: 2.0.3
vite: 7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2) vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@@ -7591,25 +7603,25 @@ snapshots:
- tsx - tsx
- yaml - yaml
vite@7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2): vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2):
dependencies: dependencies:
esbuild: 0.27.2 esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.4) fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4 picomatch: 4.0.4
postcss: 8.5.6 postcss: 8.5.8
rollup: 4.59.0 rollup: 4.59.0
tinyglobby: 0.2.15 tinyglobby: 0.2.15
optionalDependencies: optionalDependencies:
'@types/node': 24.1.0 '@types/node': 25.5.0
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.6.1 jiti: 2.6.1
lightningcss: 1.30.2 lightningcss: 1.30.2
vitest@3.2.4(@types/node@24.1.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2): vitest@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2):
dependencies: dependencies:
'@types/chai': 5.2.3 '@types/chai': 5.2.3
'@vitest/expect': 3.2.4 '@vitest/expect': 3.2.4
'@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2)) '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2))
'@vitest/pretty-format': 3.2.4 '@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4 '@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4 '@vitest/snapshot': 3.2.4
@@ -7627,11 +7639,11 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tinypool: 1.1.1 tinypool: 1.1.1
tinyrainbow: 2.0.0 tinyrainbow: 2.0.0
vite: 7.3.1(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2) vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2)
vite-node: 3.2.4(@types/node@24.1.0)(jiti@2.6.1)(lightningcss@1.30.2) vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.30.2)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 24.1.0 '@types/node': 25.5.0
jsdom: 28.1.0 jsdom: 28.1.0
transitivePeerDependencies: transitivePeerDependencies:
- jiti - jiti

View File

@@ -39,7 +39,7 @@
"placeholder": "Hledat…" "placeholder": "Hledat…"
}, },
"resources": { "resources": {
"cpu": "Zatížení procesoru", "cpu": "Využití procesoru",
"mem": "Využití paměti", "mem": "Využití paměti",
"total": "Celkem", "total": "Celkem",
"free": "Volné", "free": "Volné",
@@ -67,15 +67,15 @@
"empty_data": "Stav podsystému neznámý" "empty_data": "Stav podsystému neznámý"
}, },
"unifi_drive": { "unifi_drive": {
"healthy": "Healthy", "healthy": "Zdravý",
"degraded": "Degraded", "degraded": "Degradováno",
"no_data": "No storage data available" "no_data": "Nejsou k dispozici žádná data úložiště"
}, },
"docker": { "docker": {
"rx": "RX", "rx": "RX",
"tx": "TX", "tx": "TX",
"mem": "Využití paměti", "mem": "Využití paměti",
"cpu": "Zatížení procesoru", "cpu": "Využití procesoru",
"running": "Běží", "running": "Běží",
"offline": "Offline", "offline": "Offline",
"error": "Chyba", "error": "Chyba",
@@ -237,7 +237,7 @@
"seed": "Seedované" "seed": "Seedované"
}, },
"qnap": { "qnap": {
"cpuUsage": "Zatížení procesoru", "cpuUsage": "Využití procesoru",
"memUsage": "Využití paměti", "memUsage": "Využití paměti",
"systemTempC": "Teplota systému", "systemTempC": "Teplota systému",
"poolUsage": "Využití fondu", "poolUsage": "Využití fondu",
@@ -450,12 +450,12 @@
}, },
"proxmox": { "proxmox": {
"mem": "Využití paměti", "mem": "Využití paměti",
"cpu": "Zatížení procesoru", "cpu": "Využití procesoru",
"lxc": "LXC", "lxc": "LXC",
"vms": "Virtuální Stroje" "vms": "Virtuální Stroje"
}, },
"glances": { "glances": {
"cpu": "Zatížení procesoru", "cpu": "Využití procesoru",
"load": "Zatížení", "load": "Zatížení",
"wait": "Čekejte prosím", "wait": "Čekejte prosím",
"temp": "TEPLOTA", "temp": "TEPLOTA",
@@ -640,7 +640,7 @@
"no_devices": "Žádná přijatá data zařízení" "no_devices": "Žádná přijatá data zařízení"
}, },
"mikrotik": { "mikrotik": {
"cpuLoad": "Zatížení procesoru", "cpuLoad": "Využití procesoru",
"memoryUsed": "Využití paměti", "memoryUsed": "Využití paměti",
"uptime": "Doba provozu", "uptime": "Doba provozu",
"numberOfLeases": "Pronájmy" "numberOfLeases": "Pronájmy"
@@ -691,7 +691,7 @@
"proxmoxbackupserver": { "proxmoxbackupserver": {
"datastore_usage": "Datové úložiště", "datastore_usage": "Datové úložiště",
"failed_tasks_24h": "Neúspěšné úlohy 24h", "failed_tasks_24h": "Neúspěšné úlohy 24h",
"cpu_usage": "Zatížení procesoru", "cpu_usage": "Využití procesoru",
"memory_usage": "Využití paměti" "memory_usage": "Využití paměti"
}, },
"immich": { "immich": {
@@ -755,7 +755,7 @@
"alertstriggered": "Spuštěné výstrahy" "alertstriggered": "Spuštěné výstrahy"
}, },
"nextcloud": { "nextcloud": {
"cpuload": "Zatížení procesoru", "cpuload": "Využití procesoru",
"memoryusage": "Využití paměti", "memoryusage": "Využití paměti",
"freespace": "Volný prostor", "freespace": "Volný prostor",
"activeusers": "Aktivní uživatelé", "activeusers": "Aktivní uživatelé",
@@ -878,7 +878,7 @@
}, },
"openwrt": { "openwrt": {
"uptime": "Doba provozu", "uptime": "Doba provozu",
"cpuLoad": "Prům. zatížení procesoru (5m)", "cpuLoad": "Prům. využití procesoru (5m)",
"up": "Běží", "up": "Běží",
"down": "Výpadek", "down": "Výpadek",
"bytesTx": "Přeneseno", "bytesTx": "Přeneseno",
@@ -1042,7 +1042,7 @@
"pending": "Čekající", "pending": "Čekající",
"status": "Stav", "status": "Stav",
"updated": "Aktualizováno", "updated": "Aktualizováno",
"cpu": "Zatížení procesoru", "cpu": "Využití procesoru",
"memory": "Využití paměti", "memory": "Využití paměti",
"disk": "Disk", "disk": "Disk",
"network": "Síť" "network": "Síť"
@@ -1138,7 +1138,7 @@
"NO_DATA_DISKS": "Žádné datové disky", "NO_DATA_DISKS": "Žádné datové disky",
"notifications": "Upozornění", "notifications": "Upozornění",
"status": "Stav", "status": "Stav",
"cpu": "Zatížení procesoru", "cpu": "Využití procesoru",
"memoryUsed": "Využití paměti", "memoryUsed": "Využití paměti",
"memoryAvailable": "Volná paměť", "memoryAvailable": "Volná paměť",
"arrayUsed": "Využito pole", "arrayUsed": "Využito pole",
@@ -1169,7 +1169,7 @@
"dockhand": { "dockhand": {
"running": "Běží", "running": "Běží",
"stopped": "Zastaveno", "stopped": "Zastaveno",
"cpu": "Zatížení procesoru", "cpu": "Využití procesoru",
"memory": "Využití paměti", "memory": "Využití paměti",
"images": "Obrazy", "images": "Obrazy",
"volumes": "Úložiště", "volumes": "Úložiště",

View File

@@ -67,9 +67,9 @@
"empty_data": "Subsystem-Status unbekannt" "empty_data": "Subsystem-Status unbekannt"
}, },
"unifi_drive": { "unifi_drive": {
"healthy": "Healthy", "healthy": "Gesund",
"degraded": "Degraded", "degraded": "Beeinträchtigt",
"no_data": "No storage data available" "no_data": "Keine Speicherdaten verfügbar"
}, },
"docker": { "docker": {
"rx": "RX", "rx": "RX",
@@ -299,8 +299,8 @@
"approved": "Bestätigt", "approved": "Bestätigt",
"available": "Verfügbar", "available": "Verfügbar",
"completed": "Abgeschlossen", "completed": "Abgeschlossen",
"processing": "Processing", "processing": "Wird verarbeitet",
"issues": "Open Issues" "issues": "Offene Probleme"
}, },
"netalertx": { "netalertx": {
"total": "Total", "total": "Total",
@@ -620,7 +620,7 @@
}, },
"pangolin": { "pangolin": {
"orgs": "Orgs", "orgs": "Orgs",
"sites": "Sites", "sites": "Seiten",
"resources": "Ressourcen", "resources": "Ressourcen",
"targets": "Ziele", "targets": "Ziele",
"traffic": "Traffic", "traffic": "Traffic",
@@ -724,7 +724,7 @@
"volumeAvailable": "Verfügbar" "volumeAvailable": "Verfügbar"
}, },
"dispatcharr": { "dispatcharr": {
"channels": "Channels", "channels": "Kanäle",
"streams": "Streams" "streams": "Streams"
}, },
"mylar": { "mylar": {
@@ -818,8 +818,8 @@
"booklore": { "booklore": {
"libraries": "Bibliotheken", "libraries": "Bibliotheken",
"books": "Bücher", "books": "Bücher",
"reading": "Reading", "reading": "Am Lesen",
"finished": "Finished" "finished": "Fertig"
}, },
"jdownloader": { "jdownloader": {
"downloadCount": "Warteschlange", "downloadCount": "Warteschlange",
@@ -1160,11 +1160,11 @@
"artists": "Künstler" "artists": "Künstler"
}, },
"arcane": { "arcane": {
"containers": "Containers", "containers": "Container",
"images": "Images", "images": "Images",
"image_updates": "Image Updates", "image_updates": "Image-Updates",
"images_unused": "Unused", "images_unused": "Ungenutzt",
"environment_required": "Environment ID Required" "environment_required": "Umgebungs-ID erforderlich"
}, },
"dockhand": { "dockhand": {
"running": "Wird ausgeführt", "running": "Wird ausgeführt",
@@ -1182,8 +1182,8 @@
}, },
"sparkyfitness": { "sparkyfitness": {
"eaten": "", "eaten": "",
"burned": "Burned", "burned": "Verbrannt",
"remaining": "Remaining", "remaining": "Verbleibend",
"steps": "Steps" "steps": "Schritte"
} }
} }

View File

@@ -92,6 +92,23 @@ describe("pages/api/widgets/glances", () => {
expect(res.statusCode).toBe(200); expect(res.statusCode).toBe(200);
}); });
it("falls back to version 3 when version is invalid", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });
httpProxy
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ total: 1 }))])
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ avg: 2 }))])
.mockResolvedValueOnce([200, null, Buffer.from(JSON.stringify({ available: 3 }))]);
const req = { query: { index: "0", version: "3/../../secret-endpoint" } };
const res = createMockRes();
await handler(req, res);
expect(httpProxy).toHaveBeenCalledWith("http://glances/api/3/cpu", expect.any(Object));
expect(res.statusCode).toBe(200);
});
it("returns 400 when glances returns 401", async () => { it("returns 400 when glances returns 401", async () => {
getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" }); getPrivateWidgetOptions.mockResolvedValueOnce({ url: "http://glances" });
httpProxy.mockResolvedValueOnce([401, null, Buffer.from("nope")]); httpProxy.mockResolvedValueOnce([401, null, Buffer.from("nope")]);

View File

@@ -1,5 +1,6 @@
import { getPrivateWidgetOptions } from "utils/config/widget-helpers"; import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
import createLogger from "utils/logger"; import createLogger from "utils/logger";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import { httpProxy } from "utils/proxy/http"; import { httpProxy } from "utils/proxy/http";
const logger = createLogger("glances"); const logger = createLogger("glances");
@@ -45,7 +46,7 @@ export default async function handler(req, res) {
const { index, cputemp: includeCpuTemp, uptime: includeUptime, disk: includeDisks, version } = req.query; const { index, cputemp: includeCpuTemp, uptime: includeUptime, disk: includeDisks, version } = req.query;
const privateWidgetOptions = await getPrivateWidgetOptions("glances", index); const privateWidgetOptions = await getPrivateWidgetOptions("glances", index);
privateWidgetOptions.version = version ?? 3; privateWidgetOptions.version = parseVersionForUrl(version, 3);
try { try {
const cpuData = await retrieveFromGlancesAPI(privateWidgetOptions, "cpu"); const cpuData = await retrieveFromGlancesAPI(privateWidgetOptions, "cpu");

View File

@@ -10,6 +10,7 @@ import { getKubeConfig } from "utils/config/kubernetes";
import * as shvl from "utils/config/shvl"; import * as shvl from "utils/config/shvl";
import kubernetes from "utils/kubernetes/export"; import kubernetes from "utils/kubernetes/export";
import createLogger from "utils/logger"; import createLogger from "utils/logger";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
const logger = createLogger("service-helpers"); const logger = createLogger("service-helpers");
@@ -113,7 +114,7 @@ export async function servicesFromDocker() {
} }
let substitutedVal = substituteEnvironmentVars(containerLabels[label]); let substitutedVal = substituteEnvironmentVars(containerLabels[label]);
if (value === "widget.version" || /^widgets\[\d+\]\.version$/.test(value)) { if (value === "widget.version" || /^widgets\[\d+\]\.version$/.test(value)) {
substitutedVal = parseInt(substitutedVal, 10); substitutedVal = parseVersionForUrl(substitutedVal);
} }
shvl.set(constructedService, value, substitutedVal); shvl.set(constructedService, value, substitutedVal);
} }
@@ -590,7 +591,7 @@ export function cleanServiceGroups(groups) {
"vikunja", "vikunja",
].includes(type) ].includes(type)
) { ) {
if (version) widget.version = parseInt(version, 10); widget.version = parseVersionForUrl(version);
} }
if (type === "glances") { if (type === "glances") {
if (metric) widget.metric = metric; if (metric) widget.metric = metric;

View File

@@ -12,6 +12,22 @@ export function formatApiCall(url, args) {
return url.replace(find, replace).replace(find, replace); return url.replace(find, replace).replace(find, replace);
} }
export function parseVersionForUrl(version, defaultValue = null) {
if (version === undefined || version === null || version === "") {
return defaultValue;
}
if (typeof version === "number") {
return Number.isInteger(version) && version >= 0 ? version : defaultValue;
}
if (typeof version === "string" && /^\d+$/.test(version)) {
return Number(version);
}
return defaultValue;
}
export function getURLSearchParams(widget, endpoint) { export function getURLSearchParams(widget, endpoint) {
const params = new URLSearchParams({ const params = new URLSearchParams({
group: widget.service_group, group: widget.service_group,

View File

@@ -7,6 +7,7 @@ import {
getURLSearchParams, getURLSearchParams,
jsonArrayFilter, jsonArrayFilter,
jsonArrayTransform, jsonArrayTransform,
parseVersionForUrl,
sanitizeErrorURL, sanitizeErrorURL,
} from "./api-helpers"; } from "./api-helpers";
@@ -21,6 +22,20 @@ describe("utils/proxy/api-helpers", () => {
expect(formatApiCall("{a}-{a}-{missing}", { a: "x" })).toBe("x-x-"); expect(formatApiCall("{a}-{a}-{missing}", { a: "x" })).toBe("x-x-");
}); });
it("parseVersionForUrl accepts canonical non-negative integers", () => {
expect(parseVersionForUrl("3")).toBe(3);
expect(parseVersionForUrl(4)).toBe(4);
expect(parseVersionForUrl(undefined, 3)).toBe(3);
});
it("parseVersionForUrl rejects non-canonical values", () => {
expect(parseVersionForUrl("3/../../path", 3)).toBe(3);
expect(parseVersionForUrl("1e2", 3)).toBe(3);
expect(parseVersionForUrl("0x10", 3)).toBe(3);
expect(parseVersionForUrl(-1, 3)).toBe(3);
expect(parseVersionForUrl(Number.NaN, 3)).toBe(3);
});
it("getURLSearchParams includes group/service/index and optionally endpoint", () => { it("getURLSearchParams includes group/service/index and optionally endpoint", () => {
const widget = { service_group: "g", service_name: "s", index: "0" }; const widget = { service_group: "g", service_name: "s", index: "0" };

View File

@@ -2,7 +2,7 @@ import cache from "memory-cache";
import getServiceWidget from "utils/config/service-helpers"; import getServiceWidget from "utils/config/service-helpers";
import createLogger from "utils/logger"; import createLogger from "utils/logger";
import { asJson, formatApiCall } from "utils/proxy/api-helpers"; import { asJson, formatApiCall, parseVersionForUrl } from "utils/proxy/api-helpers";
import { httpProxy } from "utils/proxy/http"; import { httpProxy } from "utils/proxy/http";
import widgets from "widgets/widgets"; import widgets from "widgets/widgets";
@@ -56,7 +56,7 @@ async function getApiInfo(serviceWidget, apiName, serviceName) {
const json = asJson(data); const json = asJson(data);
if (json?.data?.[apiName]) { if (json?.data?.[apiName]) {
cgiPath = json.data[apiName].path; cgiPath = json.data[apiName].path;
maxVersion = json.data[apiName].maxVersion; maxVersion = parseVersionForUrl(json.data[apiName].maxVersion);
logger.debug( logger.debug(
`Detected ${serviceWidget.type}: apiName '${apiName}', cgiPath '${cgiPath}', and maxVersion ${maxVersion}`, `Detected ${serviceWidget.type}: apiName '${apiName}', cgiPath '${cgiPath}', and maxVersion ${maxVersion}`,
); );

View File

@@ -12,7 +12,7 @@ async function login(widget) {
const loginParams = { const loginParams = {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: null, body: "{}",
}; };
if (widget.username && widget.password) { if (widget.username && widget.password) {

View File

@@ -45,7 +45,7 @@ describe("widgets/flood/proxy", () => {
expect(httpProxy).toHaveBeenCalledTimes(3); expect(httpProxy).toHaveBeenCalledTimes(3);
expect(httpProxy.mock.calls[0][0].toString()).toBe("http://flood/api/stats"); expect(httpProxy.mock.calls[0][0].toString()).toBe("http://flood/api/stats");
expect(httpProxy.mock.calls[1][0]).toBe("http://flood/api/auth/authenticate"); expect(httpProxy.mock.calls[1][0]).toBe("http://flood/api/auth/authenticate");
expect(httpProxy.mock.calls[1][1].body).toBeNull(); expect(httpProxy.mock.calls[1][1].body).toBe("{}");
expect(httpProxy.mock.calls[2][0].toString()).toBe("http://flood/api/stats"); expect(httpProxy.mock.calls[2][0].toString()).toBe("http://flood/api/stats");
expect(res.statusCode).toBe(200); expect(res.statusCode).toBe(200);
expect(res.body).toEqual(Buffer.from("data")); expect(res.body).toEqual(Buffer.from("data"));

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "next-i18next";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const statusMap = { const statusMap = {
@@ -19,11 +20,12 @@ export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const idKey = version === 3 ? "Id" : "id"; const idKey = apiVersion === 3 ? "Id" : "id";
const statusKey = version === 3 ? "Status" : "status"; const statusKey = apiVersion === 3 ? "Status" : "status";
const { data, error } = useWidgetAPI(service.widget, `${version}/containers`, { const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/containers`, {
refreshInterval: Math.max(defaultInterval, refreshInterval), refreshInterval: Math.max(defaultInterval, refreshInterval),
}); });

View File

@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const Chart = dynamic(() => import("../components/chart"), { ssr: false }); const Chart = dynamic(() => import("../components/chart"), { ssr: false });
@@ -16,14 +17,15 @@ export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit)); const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
const { data, error } = useWidgetAPI(service.widget, `${version}/cpu`, { const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/cpu`, {
refreshInterval: Math.max(defaultInterval, refreshInterval), refreshInterval: Math.max(defaultInterval, refreshInterval),
}); });
const { data: quicklookData, error: quicklookError } = useWidgetAPI(service.widget, `${version}/quicklook`); const { data: quicklookData, error: quicklookError } = useWidgetAPI(service.widget, `${apiVersion}/quicklook`);
useEffect(() => { useEffect(() => {
if (data) { if (data) {

View File

@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false }); const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false });
@@ -16,6 +17,7 @@ export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const [, diskName] = widget.metric.split(":"); const [, diskName] = widget.metric.split(":");
const [dataPoints, setDataPoints] = useState( const [dataPoints, setDataPoints] = useState(
@@ -23,7 +25,7 @@ export default function Component({ service }) {
); );
const [ratePoints, setRatePoints] = useState(new Array(pointsLimit).fill({ a: 0, b: 0 }, 0, pointsLimit)); const [ratePoints, setRatePoints] = useState(new Array(pointsLimit).fill({ a: 0, b: 0 }, 0, pointsLimit));
const { data, error } = useWidgetAPI(service.widget, `${version}/diskio`, { const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/diskio`, {
refreshInterval: Math.max(defaultInterval, refreshInterval), refreshInterval: Math.max(defaultInterval, refreshInterval),
}); });

View File

@@ -3,6 +3,7 @@ import { useTranslation } from "next-i18next";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const defaultInterval = 1000; const defaultInterval = 1000;
@@ -11,10 +12,11 @@ export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const [, fsName] = widget.metric.split("fs:"); const [, fsName] = widget.metric.split("fs:");
const diskUnits = widget.diskUnits === "bbytes" ? "common.bbytes" : "common.bytes"; const diskUnits = widget.diskUnits === "bbytes" ? "common.bbytes" : "common.bytes";
const { data, error } = useWidgetAPI(widget, `${version}/fs`, { const { data, error } = useWidgetAPI(widget, `${apiVersion}/fs`, {
refreshInterval: Math.max(defaultInterval, refreshInterval), refreshInterval: Math.max(defaultInterval, refreshInterval),
}); });

View File

@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false }); const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false });
@@ -16,11 +17,12 @@ export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const [, gpuName] = widget.metric.split(":"); const [, gpuName] = widget.metric.split(":");
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ a: 0, b: 0 }, 0, pointsLimit)); const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ a: 0, b: 0 }, 0, pointsLimit));
const { data, error } = useWidgetAPI(widget, `${version}/gpu`, { const { data, error } = useWidgetAPI(widget, `${apiVersion}/gpu`, {
refreshInterval: Math.max(defaultInterval, refreshInterval), refreshInterval: Math.max(defaultInterval, refreshInterval),
}); });

View File

@@ -3,6 +3,7 @@ import { useTranslation } from "next-i18next";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
function Swap({ quicklookData, className = "" }) { function Swap({ quicklookData, className = "" }) {
@@ -75,12 +76,13 @@ const defaultSystemInterval = 30000; // This data (OS, hostname, distribution) i
export default function Component({ service }) { export default function Component({ service }) {
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const { data: quicklookData, errorL: quicklookError } = useWidgetAPI(service.widget, `${version}/quicklook`, { const { data: quicklookData, errorL: quicklookError } = useWidgetAPI(service.widget, `${apiVersion}/quicklook`, {
refreshInterval, refreshInterval,
}); });
const { data: systemData, errorL: systemError } = useWidgetAPI(service.widget, `${version}/system`, { const { data: systemData, errorL: systemError } = useWidgetAPI(service.widget, `${apiVersion}/system`, {
refreshInterval: defaultSystemInterval, refreshInterval: defaultSystemInterval,
}); });

View File

@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false }); const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false });
@@ -17,10 +18,11 @@ export default function Component({ service }) {
const { widget } = service; const { widget } = service;
const { chart } = widget; const { chart } = widget;
const { refreshInterval = defaultInterval(chart), pointsLimit = defaultPointsLimit, version = 3 } = widget; const { refreshInterval = defaultInterval(chart), pointsLimit = defaultPointsLimit, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit)); const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
const { data, error } = useWidgetAPI(service.widget, `${version}/mem`, { const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/mem`, {
refreshInterval: Math.max(defaultInterval(chart), refreshInterval), refreshInterval: Math.max(defaultInterval(chart), refreshInterval),
}); });

View File

@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false }); const ChartDual = dynamic(() => import("../components/chart_dual"), { ssr: false });
@@ -17,15 +18,16 @@ export default function Component({ service }) {
const { widget } = service; const { widget } = service;
const { chart, metric } = widget; const { chart, metric } = widget;
const { refreshInterval = defaultInterval(chart), pointsLimit = defaultPointsLimit, version = 3 } = widget; const { refreshInterval = defaultInterval(chart), pointsLimit = defaultPointsLimit, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const rxKey = version === 3 ? "rx" : "bytes_recv"; const rxKey = apiVersion === 3 ? "rx" : "bytes_recv";
const txKey = version === 3 ? "tx" : "bytes_sent"; const txKey = apiVersion === 3 ? "tx" : "bytes_sent";
const [, interfaceName] = metric.split(":"); const [, interfaceName] = metric.split(":");
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit)); const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
const { data, error } = useWidgetAPI(widget, `${version}/network`, { const { data, error } = useWidgetAPI(widget, `${apiVersion}/network`, {
refreshInterval: Math.max(defaultInterval(chart), refreshInterval), refreshInterval: Math.max(defaultInterval(chart), refreshInterval),
}); });

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "next-i18next";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const statusMap = { const statusMap = {
@@ -22,10 +23,11 @@ export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const memoryInfoKey = version === 3 ? 0 : "rss"; const memoryInfoKey = apiVersion === 3 ? 0 : "rss";
const { data, error } = useWidgetAPI(service.widget, `${version}/processlist`, { const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/processlist`, {
refreshInterval: Math.max(defaultInterval, refreshInterval), refreshInterval: Math.max(defaultInterval, refreshInterval),
}); });

View File

@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import Block from "../components/block"; import Block from "../components/block";
import Container from "../components/container"; import Container from "../components/container";
import { parseVersionForUrl } from "utils/proxy/api-helpers";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
const Chart = dynamic(() => import("../components/chart"), { ssr: false }); const Chart = dynamic(() => import("../components/chart"), { ssr: false });
@@ -16,11 +17,12 @@ export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget; const { chart, refreshInterval = defaultInterval, pointsLimit = defaultPointsLimit, version = 3 } = widget;
const apiVersion = parseVersionForUrl(version, 3);
const [, sensorName] = widget.metric.split(":"); const [, sensorName] = widget.metric.split(":");
const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit)); const [dataPoints, setDataPoints] = useState(new Array(pointsLimit).fill({ value: 0 }, 0, pointsLimit));
const { data, error } = useWidgetAPI(service.widget, `${version}/sensors`, { const { data, error } = useWidgetAPI(service.widget, `${apiVersion}/sensors`, {
refreshInterval: Math.max(defaultInterval, refreshInterval), refreshInterval: Math.max(defaultInterval, refreshInterval),
}); });

View File

@@ -3,7 +3,7 @@ import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = { const widget = {
api: "{url}/api/{endpoint}", api: "{url}/api/{endpoint}",
proxyHandler: credentialedProxyHandler, proxyHandler: credentialedProxyHandler,
allowedEndpoints: /\d\/quicklook|diskio|cpu|fs|gpu|system|mem|network|processlist|sensors|containers/, allowedEndpoints: /^\d+\/(quicklook|diskio|cpu|fs|gpu|system|mem|network|processlist|sensors|containers)$/,
}; };
export default widget; export default widget;

View File

@@ -8,6 +8,10 @@ describe("glances widget config", () => {
it("exports a valid widget config", () => { it("exports a valid widget config", () => {
expectWidgetConfigShape(widget); expectWidgetConfigShape(widget);
expect(widget.allowedEndpoints?.test("3/quicklook")).toBe(true); expect(widget.allowedEndpoints?.test("3/quicklook")).toBe(true);
expect(widget.allowedEndpoints?.test("12/cpu")).toBe(true);
expect(widget.allowedEndpoints?.test("unknown")).toBe(false); expect(widget.allowedEndpoints?.test("unknown")).toBe(false);
expect(widget.allowedEndpoints?.test("xxcpuyy")).toBe(false);
expect(widget.allowedEndpoints?.test("3/cpu/extra")).toBe(false);
expect(widget.allowedEndpoints?.test("membrane")).toBe(false);
}); });
}); });