mirror of
https://github.com/gethomepage/homepage.git
synced 2026-01-19 08:52:10 +08:00
Compare commits
1 Commits
dev
...
feature/se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1e5a56b7a |
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
@@ -84,7 +84,7 @@ jobs:
|
|||||||
latest=auto
|
latest=auto
|
||||||
|
|
||||||
- name: Next.js build cache
|
- name: Next.js build cache
|
||||||
uses: actions/cache@v5
|
uses: actions/cache@v4
|
||||||
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') }}
|
||||||
|
|||||||
4
.github/workflows/docs-publish.yml
vendored
4
.github/workflows/docs-publish.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
key: mkdocs-material-${{ env.cache_id }}
|
key: mkdocs-material-${{ env.cache_id }}
|
||||||
path: .cache
|
path: .cache
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.x
|
||||||
- run: echo "cache_id=${{github.sha}}" >> $GITHUB_ENV
|
- run: echo "cache_id=${{github.sha}}" >> $GITHUB_ENV
|
||||||
- uses: actions/cache@v5
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
key: mkdocs-material-${{ env.cache_id }}
|
key: mkdocs-material-${{ env.cache_id }}
|
||||||
path: .cache
|
path: .cache
|
||||||
|
|||||||
2
.github/workflows/repo-maintenance.yml
vendored
2
.github/workflows/repo-maintenance.yml
vendored
@@ -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@v5
|
||||||
with:
|
with:
|
||||||
issue-inactive-days: '30'
|
issue-inactive-days: '30'
|
||||||
pr-inactive-days: '30'
|
pr-inactive-days: '30'
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ For configuration options, examples and more, [please check out the homepage doc
|
|||||||
|
|
||||||
## Security Notice 🔒
|
## Security Notice 🔒
|
||||||
|
|
||||||
Please note that when using features such as widgets, Homepage can access personal information (for example from your home automation system) and Homepage currently does not (and is not planned to) include any authentication layer itself. If Homepage is reachable from any untrusted network, it **must** sit behind a reverse proxy (and/or VPN) that enforces authentication, TLS, and strictly validates Host headers. The built-in host check in Homepage is a best-effort guard and should not be treated as security when exposed publicly.
|
Please note that when using features such as widgets, Homepage can access personal information (for example from your home automation system) and Homepage currently does not (and is not planned to) include any authentication layer itself. Thus, we recommend homepage be deployed behind a reverse proxy including authentication, SSL etc, and / or behind a VPN.
|
||||||
|
|
||||||
## With Docker
|
## With Docker
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ if [ "$HOSTNAME" = "::" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check ownership before chown
|
# Check ownership before chown
|
||||||
if [ "$PUID" = "0" ]; then
|
if [ -e /app/config ]; then
|
||||||
echo "Skipping ownership changes for /app/config"
|
|
||||||
elif [ -e /app/config ]; then
|
|
||||||
CURRENT_UID=$(stat -c %u /app/config)
|
CURRENT_UID=$(stat -c %u /app/config)
|
||||||
CURRENT_GID=$(stat -c %g /app/config)
|
CURRENT_GID=$(stat -c %g /app/config)
|
||||||
|
|
||||||
@@ -41,9 +39,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure /app/config/logs exists and is owned
|
# Ensure /app/config/logs exists and is owned
|
||||||
if [ "$PUID" = "0" ]; then
|
if [ -n "$PUID" ] && [ -n "$PGID" ]; then
|
||||||
echo "Skipping ownership changes for /app/config/logs"
|
|
||||||
elif [ -n "$PUID" ] && [ -n "$PGID" ]; then
|
|
||||||
mkdir -p /app/config/logs 2>/dev/null || true
|
mkdir -p /app/config/logs 2>/dev/null || true
|
||||||
if [ -d /app/config/logs ]; then
|
if [ -d /app/config/logs ]; then
|
||||||
LOG_UID=$(stat -c %u /app/config/logs)
|
LOG_UID=$(stat -c %u /app/config/logs)
|
||||||
|
|||||||
@@ -189,8 +189,6 @@ labels: ...
|
|||||||
- homepage.widgets[1].slug=youreventslughere
|
- homepage.widgets[1].slug=youreventslughere
|
||||||
```
|
```
|
||||||
|
|
||||||
To pass custom HTTP headers with a widget request when using labels, use the same dot-notation: `homepage.widget.headers.X-Auth-Key=secret` (or `homepage.widgets[0].headers.X-Auth-Key=secret` when multiple widgets are present).
|
|
||||||
|
|
||||||
You can add specify fields for e.g. the [CustomAPI](../widgets/services/customapi.md) widget by using array-style dot notation:
|
You can add specify fields for e.g. the [CustomAPI](../widgets/services/customapi.md) widget by using array-style dot notation:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ metadata:
|
|||||||
gethomepage.dev/name: Emby
|
gethomepage.dev/name: Emby
|
||||||
gethomepage.dev/widget.type: "emby"
|
gethomepage.dev/widget.type: "emby"
|
||||||
gethomepage.dev/widget.url: "https://emby.example.com"
|
gethomepage.dev/widget.url: "https://emby.example.com"
|
||||||
gethomepage.dev/widget.headers.X-Auth-Key: "your-secret-here"
|
|
||||||
gethomepage.dev/pod-selector: ""
|
gethomepage.dev/pod-selector: ""
|
||||||
gethomepage.dev/weight: 10 # optional
|
gethomepage.dev/weight: 10 # optional
|
||||||
gethomepage.dev/instance: "public" # optional
|
gethomepage.dev/instance: "public" # optional
|
||||||
|
|||||||
@@ -101,25 +101,6 @@ Each service can have multiple widgets attached to it, for example:
|
|||||||
|
|
||||||
Multiple widgets per service are not yet supported with Kubernetes ingress annotations.
|
Multiple widgets per service are not yet supported with Kubernetes ingress annotations.
|
||||||
|
|
||||||
#### Custom HTTP headers
|
|
||||||
|
|
||||||
Widgets that make HTTP calls support extra request headers via `headers`. This is useful when a reverse proxy expects a secret header.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- UptimeRobot:
|
|
||||||
icon: uptimekuma.png
|
|
||||||
href: https://uptimerobot.com/
|
|
||||||
widget:
|
|
||||||
type: uptimerobot
|
|
||||||
url: https://api.uptimerobot.com
|
|
||||||
key: ${UPTIMEROBOT_API_KEY}
|
|
||||||
headers:
|
|
||||||
User-Agent: homepage
|
|
||||||
X-Auth-Key: your-secret-here
|
|
||||||
```
|
|
||||||
|
|
||||||
If you define services via Docker labels or Kubernetes annotations, use the same key with dot-notation (for example `homepage.widget.headers.X-Auth-Key=secret` or `gethomepage.dev/widget.headers.X-Auth-Key: "secret"`).
|
|
||||||
|
|
||||||
#### Field Visibility
|
#### Field Visibility
|
||||||
|
|
||||||
Each widget can optionally provide a list of which fields should be visible via the `fields` widget property. If no fields are specified, then all fields will be displayed. The `fields` property must be a valid YAML array of strings. As an example, here is the entry for Sonarr showing only a couple of fields.
|
Each widget can optionally provide a list of which fields should be visible via the `fields` widget property. If no fields are specified, then all fields will be displayed. The `fields` property must be a valid YAML array of strings. As an example, here is the entry for Sonarr showing only a couple of fields.
|
||||||
|
|||||||
@@ -37,4 +37,4 @@ The value is a comma-separated (no spaces) list of allowed hosts (sometimes with
|
|||||||
|
|
||||||
If you are seeing errors about host validation, check the homepage logs and ensure that the host exactly as output in the logs is in the `HOMEPAGE_ALLOWED_HOSTS` list.
|
If you are seeing errors about host validation, check the homepage logs and ensure that the host exactly as output in the logs is in the `HOMEPAGE_ALLOWED_HOSTS` list.
|
||||||
|
|
||||||
This can be disabled by setting `HOMEPAGE_ALLOWED_HOSTS` to `*` but this is not recommended. Public deployments must rely on a reverse proxy (and/or VPN) that enforces authentication, TLS, and unexpected Host headers; the built-in host check is a best-effort guard for local setups and is not a substitute for edge protections.
|
This can be disabled by setting `HOMEPAGE_ALLOWED_HOSTS` to `*` but this is not recommended.
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ title: Gatus
|
|||||||
description: Gatus Widget Configuration
|
description: Gatus Widget Configuration
|
||||||
---
|
---
|
||||||
|
|
||||||
Learn more about [Gatus](https://github.com/TwiN/gatus).
|
|
||||||
|
|
||||||
Allowed fields: `["up", "down", "uptime"]`.
|
Allowed fields: `["up", "down", "uptime"]`.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -9,17 +9,11 @@ _Note that the project was renamed from PiAlert to NetAlertX._
|
|||||||
|
|
||||||
Allowed fields: `["total", "connected", "new_devices", "down_alerts"]`.
|
Allowed fields: `["total", "connected", "new_devices", "down_alerts"]`.
|
||||||
|
|
||||||
Provide the `API_TOKEN` (f.k.a. `SYNC_api_token`) as the `key` in your config.
|
If you have enabled a password on your NetAlertX instance, you will need to provide the `SYNC_api_token` as the `key` in your config.
|
||||||
|
|
||||||
| NetAlertX Version | Homepage Widget Version |
|
|
||||||
| ----------------- | ----------------------- |
|
|
||||||
| < v26.1.17 | 1 (default) |
|
|
||||||
| > v26.1.17 | 2 |
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
widget:
|
widget:
|
||||||
type: netalertx
|
type: netalertx
|
||||||
url: http://ip:port
|
url: http://ip:port
|
||||||
key: yournetalertxapitoken
|
key: netalertxsyncapitoken # optional, only if password is enabled
|
||||||
version: 2 # optional, default is 1
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
---
|
---
|
||||||
title: Jellyseerr
|
title: Seerr Widget
|
||||||
description: Jellyseerr Widget Configuration
|
description: Seerr Widget Configuration
|
||||||
---
|
---
|
||||||
|
|
||||||
Learn more about [Jellyseerr](https://github.com/Fallenbagel/jellyseerr).
|
Learn more about [Seerr](https://github.com/seerr-team/seerr).
|
||||||
|
|
||||||
Find your API key under `Settings > General > API Key`.
|
Find your API key under `Settings > General > API Key`.
|
||||||
|
|
||||||
|
_Note that Jellyseerr was merged with Overseerr and renamed Seerr._
|
||||||
|
|
||||||
Allowed fields: `["pending", "approved", "available", "issues"]`.
|
Allowed fields: `["pending", "approved", "available", "issues"]`.
|
||||||
Default fields: `["pending", "approved", "available"]`.
|
Default fields: `["pending", "approved", "available"]`.
|
||||||
|
|
||||||
@@ -5,11 +5,6 @@ description: TrueNas Scale Widget Configuration
|
|||||||
|
|
||||||
Learn more about [TrueNas](https://www.truenas.com/).
|
Learn more about [TrueNas](https://www.truenas.com/).
|
||||||
|
|
||||||
| TrueNAS Version | Homepage widget version |
|
|
||||||
| ----------------------- | ----------------------- |
|
|
||||||
| < 26.04 (REST API) | 1 (default) |
|
|
||||||
| > 25.04 (Websocket API) | 2 |
|
|
||||||
|
|
||||||
Allowed fields: `["load", "uptime", "alerts"]`.
|
Allowed fields: `["load", "uptime", "alerts"]`.
|
||||||
|
|
||||||
To create an API Key, follow [the official TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/toptoolbar/managingapikeys/).
|
To create an API Key, follow [the official TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/toptoolbar/managingapikeys/).
|
||||||
@@ -22,7 +17,6 @@ To use the `enablePools` option with TrueNAS Core, the `nasType` parameter is re
|
|||||||
widget:
|
widget:
|
||||||
type: truenas
|
type: truenas
|
||||||
url: http://truenas.host.or.ip
|
url: http://truenas.host.or.ip
|
||||||
version: 2 # optional, defaults to 1
|
|
||||||
username: user # not required if using api key
|
username: user # not required if using api key
|
||||||
password: pass # not required if using api key
|
password: pass # not required if using api key
|
||||||
key: yourtruenasapikey # not required if using username / password
|
key: yourtruenasapikey # not required if using username / password
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ nav:
|
|||||||
- widgets/services/jackett.md
|
- widgets/services/jackett.md
|
||||||
- widgets/services/jdownloader.md
|
- widgets/services/jdownloader.md
|
||||||
- widgets/services/jellyfin.md
|
- widgets/services/jellyfin.md
|
||||||
- widgets/services/jellyseerr.md
|
|
||||||
- widgets/services/jellystat.md
|
- widgets/services/jellystat.md
|
||||||
- widgets/services/kavita.md
|
- widgets/services/kavita.md
|
||||||
- widgets/services/komga.md
|
- widgets/services/komga.md
|
||||||
@@ -147,6 +146,7 @@ nav:
|
|||||||
- widgets/services/rutorrent.md
|
- widgets/services/rutorrent.md
|
||||||
- widgets/services/sabnzbd.md
|
- widgets/services/sabnzbd.md
|
||||||
- widgets/services/scrutiny.md
|
- widgets/services/scrutiny.md
|
||||||
|
- widgets/services/seerr.md
|
||||||
- widgets/services/slskd.md
|
- widgets/services/slskd.md
|
||||||
- widgets/services/sonarr.md
|
- widgets/services/sonarr.md
|
||||||
- widgets/services/speedtest-tracker.md
|
- widgets/services/speedtest-tracker.md
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -20,12 +20,12 @@
|
|||||||
"gamedig": "^5.3.2",
|
"gamedig": "^5.3.2",
|
||||||
"i18next": "^25.5.3",
|
"i18next": "^25.5.3",
|
||||||
"ical.js": "^2.1.0",
|
"ical.js": "^2.1.0",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.0",
|
||||||
"json-rpc-2.0": "^1.7.0",
|
"json-rpc-2.0": "^1.7.0",
|
||||||
"luxon": "^3.6.1",
|
"luxon": "^3.6.1",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"minecraftstatuspinger": "^1.2.2",
|
"minecraftstatuspinger": "^1.2.2",
|
||||||
"next": "^15.5.9",
|
"next": "^15.5.7",
|
||||||
"next-i18next": "^12.1.0",
|
"next-i18next": "^12.1.0",
|
||||||
"ping": "^0.4.4",
|
"ping": "^0.4.4",
|
||||||
"pretty-bytes": "^7.1.0",
|
"pretty-bytes": "^7.1.0",
|
||||||
@@ -33,19 +33,18 @@
|
|||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-i18next": "^15.5.3",
|
"react-i18next": "^15.5.3",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.4.0",
|
||||||
"recharts": "^3.1.2",
|
"recharts": "^3.1.2",
|
||||||
"swr": "^2.3.3",
|
"swr": "^2.3.3",
|
||||||
"systeminformation": "^5.27.11",
|
"systeminformation": "^5.27.11",
|
||||||
"tough-cookie": "^6.0.0",
|
"tough-cookie": "^6.0.0",
|
||||||
"urbackup-server-api": "^0.91.0",
|
"urbackup-server-api": "^0.8.9",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"ws": "^8.18.3",
|
|
||||||
"xml-js": "^1.6.11"
|
"xml-js": "^1.6.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.10",
|
"@tailwindcss/forms": "^0.5.10",
|
||||||
"@tailwindcss/postcss": "^4.1.18",
|
"@tailwindcss/postcss": "^4.1.14",
|
||||||
"eslint": "^9.25.1",
|
"eslint": "^9.25.1",
|
||||||
"eslint-config-next": "^15.2.4",
|
"eslint-config-next": "^15.2.4",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
@@ -66,7 +65,6 @@
|
|||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@tailwindcss/oxide",
|
|
||||||
"osx-temperature-sensor",
|
"osx-temperature-sensor",
|
||||||
"sharp"
|
"sharp"
|
||||||
]
|
]
|
||||||
|
|||||||
382
pnpm-lock.yaml
generated
382
pnpm-lock.yaml
generated
@@ -36,8 +36,8 @@ importers:
|
|||||||
specifier: ^2.1.0
|
specifier: ^2.1.0
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
js-yaml:
|
js-yaml:
|
||||||
specifier: ^4.1.1
|
specifier: ^4.1.0
|
||||||
version: 4.1.1
|
version: 4.1.0
|
||||||
json-rpc-2.0:
|
json-rpc-2.0:
|
||||||
specifier: ^1.7.0
|
specifier: ^1.7.0
|
||||||
version: 1.7.0
|
version: 1.7.0
|
||||||
@@ -51,11 +51,11 @@ importers:
|
|||||||
specifier: ^1.2.2
|
specifier: ^1.2.2
|
||||||
version: 1.2.2
|
version: 1.2.2
|
||||||
next:
|
next:
|
||||||
specifier: ^15.5.9
|
specifier: ^15.5.7
|
||||||
version: 15.5.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 15.5.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
next-i18next:
|
next-i18next:
|
||||||
specifier: ^12.1.0
|
specifier: ^12.1.0
|
||||||
version: 12.1.0(next@15.5.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 12.1.0(next@15.5.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
ping:
|
ping:
|
||||||
specifier: ^0.4.4
|
specifier: ^0.4.4
|
||||||
version: 0.4.4
|
version: 0.4.4
|
||||||
@@ -75,8 +75,8 @@ importers:
|
|||||||
specifier: ^15.5.3
|
specifier: ^15.5.3
|
||||||
version: 15.5.3(i18next@25.5.3(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)
|
version: 15.5.3(i18next@25.5.3(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)
|
||||||
react-icons:
|
react-icons:
|
||||||
specifier: ^5.5.0
|
specifier: ^5.4.0
|
||||||
version: 5.5.0(react@18.3.1)
|
version: 5.4.0(react@18.3.1)
|
||||||
recharts:
|
recharts:
|
||||||
specifier: ^3.1.2
|
specifier: ^3.1.2
|
||||||
version: 3.1.2(@types/react@19.0.10)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(redux@5.0.1)
|
version: 3.1.2(@types/react@19.0.10)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(redux@5.0.1)
|
||||||
@@ -90,14 +90,11 @@ importers:
|
|||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
urbackup-server-api:
|
urbackup-server-api:
|
||||||
specifier: ^0.91.0
|
specifier: ^0.8.9
|
||||||
version: 0.91.0
|
version: 0.8.9
|
||||||
winston:
|
winston:
|
||||||
specifier: ^3.17.0
|
specifier: ^3.17.0
|
||||||
version: 3.17.0
|
version: 3.17.0
|
||||||
ws:
|
|
||||||
specifier: ^8.18.3
|
|
||||||
version: 8.18.3
|
|
||||||
xml-js:
|
xml-js:
|
||||||
specifier: ^1.6.11
|
specifier: ^1.6.11
|
||||||
version: 1.6.11
|
version: 1.6.11
|
||||||
@@ -106,8 +103,8 @@ importers:
|
|||||||
specifier: ^0.5.10
|
specifier: ^0.5.10
|
||||||
version: 0.5.10(tailwindcss@4.0.9)
|
version: 0.5.10(tailwindcss@4.0.9)
|
||||||
'@tailwindcss/postcss':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4.1.18
|
specifier: ^4.1.14
|
||||||
version: 4.1.18
|
version: 4.1.14
|
||||||
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)
|
||||||
@@ -469,8 +466,8 @@ packages:
|
|||||||
'@napi-rs/wasm-runtime@0.2.8':
|
'@napi-rs/wasm-runtime@0.2.8':
|
||||||
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
|
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
|
||||||
|
|
||||||
'@next/env@15.5.9':
|
'@next/env@15.5.7':
|
||||||
resolution: {integrity: sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==}
|
resolution: {integrity: sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.2.4':
|
'@next/eslint-plugin-next@15.2.4':
|
||||||
resolution: {integrity: sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ==}
|
resolution: {integrity: sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ==}
|
||||||
@@ -656,65 +653,65 @@ packages:
|
|||||||
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'
|
||||||
|
|
||||||
'@tailwindcss/node@4.1.18':
|
'@tailwindcss/node@4.1.14':
|
||||||
resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==}
|
resolution: {integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==}
|
||||||
|
|
||||||
'@tailwindcss/oxide-android-arm64@4.1.18':
|
'@tailwindcss/oxide-android-arm64@4.1.14':
|
||||||
resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==}
|
resolution: {integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-arm64@4.1.18':
|
'@tailwindcss/oxide-darwin-arm64@4.1.14':
|
||||||
resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==}
|
resolution: {integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-x64@4.1.18':
|
'@tailwindcss/oxide-darwin-x64@4.1.14':
|
||||||
resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==}
|
resolution: {integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
'@tailwindcss/oxide-freebsd-x64@4.1.18':
|
'@tailwindcss/oxide-freebsd-x64@4.1.14':
|
||||||
resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==}
|
resolution: {integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
|
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
|
||||||
resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==}
|
resolution: {integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
|
'@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
|
||||||
resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==}
|
resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
|
'@tailwindcss/oxide-linux-arm64-musl@4.1.14':
|
||||||
resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
|
resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
|
'@tailwindcss/oxide-linux-x64-gnu@4.1.14':
|
||||||
resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
|
resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
|
'@tailwindcss/oxide-linux-x64-musl@4.1.14':
|
||||||
resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
|
resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
|
'@tailwindcss/oxide-wasm32-wasi@4.1.14':
|
||||||
resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
|
resolution: {integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
cpu: [wasm32]
|
cpu: [wasm32]
|
||||||
bundledDependencies:
|
bundledDependencies:
|
||||||
@@ -725,24 +722,24 @@ packages:
|
|||||||
- '@emnapi/wasi-threads'
|
- '@emnapi/wasi-threads'
|
||||||
- tslib
|
- tslib
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
|
'@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
|
||||||
resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==}
|
resolution: {integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-x64-msvc@4.1.18':
|
'@tailwindcss/oxide-win32-x64-msvc@4.1.14':
|
||||||
resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==}
|
resolution: {integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@tailwindcss/oxide@4.1.18':
|
'@tailwindcss/oxide@4.1.14':
|
||||||
resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
|
resolution: {integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
'@tailwindcss/postcss@4.1.18':
|
'@tailwindcss/postcss@4.1.14':
|
||||||
resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==}
|
resolution: {integrity: sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg==}
|
||||||
|
|
||||||
'@tanstack/react-virtual@3.13.12':
|
'@tanstack/react-virtual@3.13.12':
|
||||||
resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==}
|
resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==}
|
||||||
@@ -1038,8 +1035,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
async-mutex@0.5.0:
|
async-mutex@0.3.2:
|
||||||
resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
|
resolution: {integrity: sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==}
|
||||||
|
|
||||||
async@3.2.6:
|
async@3.2.6:
|
||||||
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
||||||
@@ -1123,8 +1120,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
caniuse-lite@1.0.30001760:
|
caniuse-lite@1.0.30001759:
|
||||||
resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==}
|
resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==}
|
||||||
|
|
||||||
chalk@4.1.2:
|
chalk@4.1.2:
|
||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
@@ -1323,6 +1320,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
detect-libc@2.1.1:
|
||||||
|
resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
detect-libc@2.1.2:
|
detect-libc@2.1.2:
|
||||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -1361,8 +1362,8 @@ packages:
|
|||||||
end-of-stream@1.4.5:
|
end-of-stream@1.4.5:
|
||||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||||
|
|
||||||
enhanced-resolve@5.18.4:
|
enhanced-resolve@5.18.3:
|
||||||
resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==}
|
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
|
|
||||||
es-abstract@1.23.9:
|
es-abstract@1.23.9:
|
||||||
@@ -1965,8 +1966,8 @@ packages:
|
|||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
js-yaml@4.1.1:
|
js-yaml@4.1.0:
|
||||||
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
|
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
jsep@1.4.0:
|
jsep@1.4.0:
|
||||||
@@ -2015,74 +2016,68 @@ packages:
|
|||||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
lightningcss-android-arm64@1.30.2:
|
lightningcss-darwin-arm64@1.30.1:
|
||||||
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
|
resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
|
||||||
engines: {node: '>= 12.0.0'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [android]
|
|
||||||
|
|
||||||
lightningcss-darwin-arm64@1.30.2:
|
|
||||||
resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
|
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
lightningcss-darwin-x64@1.30.2:
|
lightningcss-darwin-x64@1.30.1:
|
||||||
resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
|
resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
|
|
||||||
lightningcss-freebsd-x64@1.30.2:
|
lightningcss-freebsd-x64@1.30.1:
|
||||||
resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
|
resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [freebsd]
|
os: [freebsd]
|
||||||
|
|
||||||
lightningcss-linux-arm-gnueabihf@1.30.2:
|
lightningcss-linux-arm-gnueabihf@1.30.1:
|
||||||
resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
|
resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
lightningcss-linux-arm64-gnu@1.30.2:
|
lightningcss-linux-arm64-gnu@1.30.1:
|
||||||
resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
|
resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
lightningcss-linux-arm64-musl@1.30.2:
|
lightningcss-linux-arm64-musl@1.30.1:
|
||||||
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
|
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
lightningcss-linux-x64-gnu@1.30.2:
|
lightningcss-linux-x64-gnu@1.30.1:
|
||||||
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
|
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
lightningcss-linux-x64-musl@1.30.2:
|
lightningcss-linux-x64-musl@1.30.1:
|
||||||
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
|
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
lightningcss-win32-arm64-msvc@1.30.2:
|
lightningcss-win32-arm64-msvc@1.30.1:
|
||||||
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
|
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
lightningcss-win32-x64-msvc@1.30.2:
|
lightningcss-win32-x64-msvc@1.30.1:
|
||||||
resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
|
resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
lightningcss@1.30.2:
|
lightningcss@1.30.1:
|
||||||
resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
|
resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
@@ -2117,8 +2112,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==}
|
resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
magic-string@0.30.21:
|
magic-string@0.30.19:
|
||||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
|
||||||
|
|
||||||
math-intrinsics@1.1.0:
|
math-intrinsics@1.1.0:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
@@ -2181,6 +2176,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
|
resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
|
minizlib@3.1.0:
|
||||||
|
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
mkdirp-classic@0.5.3:
|
mkdirp-classic@0.5.3:
|
||||||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||||
|
|
||||||
@@ -2213,8 +2212,8 @@ packages:
|
|||||||
next: '>= 10.0.0'
|
next: '>= 10.0.0'
|
||||||
react: '>= 16.8.0'
|
react: '>= 16.8.0'
|
||||||
|
|
||||||
next@15.5.9:
|
next@15.5.7:
|
||||||
resolution: {integrity: sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==}
|
resolution: {integrity: sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==}
|
||||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2459,8 +2458,8 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
react-icons@5.5.0:
|
react-icons@5.4.0:
|
||||||
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
|
resolution: {integrity: sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '*'
|
react: '*'
|
||||||
|
|
||||||
@@ -2806,11 +2805,11 @@ packages:
|
|||||||
tailwindcss@4.0.9:
|
tailwindcss@4.0.9:
|
||||||
resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==}
|
resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==}
|
||||||
|
|
||||||
tailwindcss@4.1.18:
|
tailwindcss@4.1.14:
|
||||||
resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
|
resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==}
|
||||||
|
|
||||||
tapable@2.3.0:
|
tapable@2.2.3:
|
||||||
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
|
resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
tar-fs@2.1.3:
|
tar-fs@2.1.3:
|
||||||
@@ -2824,6 +2823,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
|
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
tar@7.5.1:
|
||||||
|
resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
telnet-client@2.2.6:
|
telnet-client@2.2.6:
|
||||||
resolution: {integrity: sha512-ZUYrLsPtQupQww3eSEORDVOb6ztdtKEghya6TVXPo2tg/UQq2pn5rHhvwuUvyYpbnsoqdNY1fyD1GNkXHR8dYA==}
|
resolution: {integrity: sha512-ZUYrLsPtQupQww3eSEORDVOb6ztdtKEghya6TVXPo2tg/UQq2pn5rHhvwuUvyYpbnsoqdNY1fyD1GNkXHR8dYA==}
|
||||||
|
|
||||||
@@ -2927,8 +2930,8 @@ packages:
|
|||||||
unrs-resolver@1.3.3:
|
unrs-resolver@1.3.3:
|
||||||
resolution: {integrity: sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==}
|
resolution: {integrity: sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A==}
|
||||||
|
|
||||||
urbackup-server-api@0.91.0:
|
urbackup-server-api@0.8.9:
|
||||||
resolution: {integrity: sha512-N1CSnGSCSHjwWfGOp6jE56mHYoZor/p++ii8yPsN9P/3cKLBgCvrAZxAbfi+IgK9FZpQEx/kPX1R8OTJRy+x6A==}
|
resolution: {integrity: sha512-Igu6A0xSZeMsiN6PWT7zG4aD+iJR5fXT/j5+xwAvnD/vCNfvVrettIsXv6MftxOajvTmtlgaYu8KDoH1EJQ6DQ==}
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
@@ -3014,8 +3017,8 @@ packages:
|
|||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
ws@8.18.3:
|
ws@8.18.0:
|
||||||
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
|
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
bufferutil: ^4.0.1
|
bufferutil: ^4.0.1
|
||||||
@@ -3125,7 +3128,7 @@ snapshots:
|
|||||||
globals: 14.0.0
|
globals: 14.0.0
|
||||||
ignore: 5.3.2
|
ignore: 5.3.2
|
||||||
import-fresh: 3.3.1
|
import-fresh: 3.3.1
|
||||||
js-yaml: 4.1.1
|
js-yaml: 4.1.0
|
||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
strip-json-comments: 3.1.1
|
strip-json-comments: 3.1.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -3348,8 +3351,8 @@ snapshots:
|
|||||||
'@types/tar': 6.1.13
|
'@types/tar': 6.1.13
|
||||||
'@types/ws': 8.5.14
|
'@types/ws': 8.5.14
|
||||||
form-data: 4.0.2
|
form-data: 4.0.2
|
||||||
isomorphic-ws: 5.0.0(ws@8.18.3)
|
isomorphic-ws: 5.0.0(ws@8.18.0)
|
||||||
js-yaml: 4.1.1
|
js-yaml: 4.1.0
|
||||||
jsonpath-plus: 10.3.0
|
jsonpath-plus: 10.3.0
|
||||||
node-fetch: 2.7.0
|
node-fetch: 2.7.0
|
||||||
openid-client: 6.3.0
|
openid-client: 6.3.0
|
||||||
@@ -3358,7 +3361,7 @@ snapshots:
|
|||||||
tar: 7.4.3
|
tar: 7.4.3
|
||||||
tmp-promise: 3.0.3
|
tmp-promise: 3.0.3
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
ws: 8.18.3
|
ws: 8.18.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- encoding
|
- encoding
|
||||||
@@ -3371,7 +3374,7 @@ snapshots:
|
|||||||
'@tybys/wasm-util': 0.9.0
|
'@tybys/wasm-util': 0.9.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/env@15.5.9': {}
|
'@next/env@15.5.7': {}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.2.4':
|
'@next/eslint-plugin-next@15.2.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3531,74 +3534,77 @@ snapshots:
|
|||||||
mini-svg-data-uri: 1.4.4
|
mini-svg-data-uri: 1.4.4
|
||||||
tailwindcss: 4.0.9
|
tailwindcss: 4.0.9
|
||||||
|
|
||||||
'@tailwindcss/node@4.1.18':
|
'@tailwindcss/node@4.1.14':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/remapping': 2.3.5
|
'@jridgewell/remapping': 2.3.5
|
||||||
enhanced-resolve: 5.18.4
|
enhanced-resolve: 5.18.3
|
||||||
jiti: 2.6.1
|
jiti: 2.6.1
|
||||||
lightningcss: 1.30.2
|
lightningcss: 1.30.1
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.19
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
tailwindcss: 4.1.18
|
tailwindcss: 4.1.14
|
||||||
|
|
||||||
'@tailwindcss/oxide-android-arm64@4.1.18':
|
'@tailwindcss/oxide-android-arm64@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-arm64@4.1.18':
|
'@tailwindcss/oxide-darwin-arm64@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-darwin-x64@4.1.18':
|
'@tailwindcss/oxide-darwin-x64@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-freebsd-x64@4.1.18':
|
'@tailwindcss/oxide-freebsd-x64@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
|
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
|
'@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-arm64-musl@4.1.18':
|
'@tailwindcss/oxide-linux-arm64-musl@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-gnu@4.1.18':
|
'@tailwindcss/oxide-linux-x64-gnu@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-linux-x64-musl@4.1.18':
|
'@tailwindcss/oxide-linux-x64-musl@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-wasm32-wasi@4.1.18':
|
'@tailwindcss/oxide-wasm32-wasi@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
|
'@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide-win32-x64-msvc@4.1.18':
|
'@tailwindcss/oxide-win32-x64-msvc@4.1.14':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tailwindcss/oxide@4.1.18':
|
'@tailwindcss/oxide@4.1.14':
|
||||||
|
dependencies:
|
||||||
|
detect-libc: 2.1.1
|
||||||
|
tar: 7.5.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@tailwindcss/oxide-android-arm64': 4.1.18
|
'@tailwindcss/oxide-android-arm64': 4.1.14
|
||||||
'@tailwindcss/oxide-darwin-arm64': 4.1.18
|
'@tailwindcss/oxide-darwin-arm64': 4.1.14
|
||||||
'@tailwindcss/oxide-darwin-x64': 4.1.18
|
'@tailwindcss/oxide-darwin-x64': 4.1.14
|
||||||
'@tailwindcss/oxide-freebsd-x64': 4.1.18
|
'@tailwindcss/oxide-freebsd-x64': 4.1.14
|
||||||
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18
|
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14
|
||||||
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.18
|
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.14
|
||||||
'@tailwindcss/oxide-linux-arm64-musl': 4.1.18
|
'@tailwindcss/oxide-linux-arm64-musl': 4.1.14
|
||||||
'@tailwindcss/oxide-linux-x64-gnu': 4.1.18
|
'@tailwindcss/oxide-linux-x64-gnu': 4.1.14
|
||||||
'@tailwindcss/oxide-linux-x64-musl': 4.1.18
|
'@tailwindcss/oxide-linux-x64-musl': 4.1.14
|
||||||
'@tailwindcss/oxide-wasm32-wasi': 4.1.18
|
'@tailwindcss/oxide-wasm32-wasi': 4.1.14
|
||||||
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
|
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.14
|
||||||
'@tailwindcss/oxide-win32-x64-msvc': 4.1.18
|
'@tailwindcss/oxide-win32-x64-msvc': 4.1.14
|
||||||
|
|
||||||
'@tailwindcss/postcss@4.1.18':
|
'@tailwindcss/postcss@4.1.14':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@alloc/quick-lru': 5.2.0
|
'@alloc/quick-lru': 5.2.0
|
||||||
'@tailwindcss/node': 4.1.18
|
'@tailwindcss/node': 4.1.14
|
||||||
'@tailwindcss/oxide': 4.1.18
|
'@tailwindcss/oxide': 4.1.14
|
||||||
postcss: 8.5.6
|
postcss: 8.5.6
|
||||||
tailwindcss: 4.1.18
|
tailwindcss: 4.1.14
|
||||||
|
|
||||||
'@tanstack/react-virtual@3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@tanstack/react-virtual@3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3925,7 +3931,7 @@ snapshots:
|
|||||||
|
|
||||||
async-function@1.0.0: {}
|
async-function@1.0.0: {}
|
||||||
|
|
||||||
async-mutex@0.5.0:
|
async-mutex@0.3.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
@@ -4018,7 +4024,7 @@ snapshots:
|
|||||||
|
|
||||||
callsites@3.1.0: {}
|
callsites@3.1.0: {}
|
||||||
|
|
||||||
caniuse-lite@1.0.30001760: {}
|
caniuse-lite@1.0.30001759: {}
|
||||||
|
|
||||||
chalk@4.1.2:
|
chalk@4.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4194,7 +4200,10 @@ snapshots:
|
|||||||
|
|
||||||
dequal@2.0.3: {}
|
dequal@2.0.3: {}
|
||||||
|
|
||||||
detect-libc@2.1.2: {}
|
detect-libc@2.1.1: {}
|
||||||
|
|
||||||
|
detect-libc@2.1.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
docker-modem@5.0.6:
|
docker-modem@5.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4241,10 +4250,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
once: 1.4.0
|
once: 1.4.0
|
||||||
|
|
||||||
enhanced-resolve@5.18.4:
|
enhanced-resolve@5.18.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
tapable: 2.3.0
|
tapable: 2.2.3
|
||||||
|
|
||||||
es-abstract@1.23.9:
|
es-abstract@1.23.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5036,9 +5045,9 @@ snapshots:
|
|||||||
|
|
||||||
isexe@2.0.0: {}
|
isexe@2.0.0: {}
|
||||||
|
|
||||||
isomorphic-ws@5.0.0(ws@8.18.3):
|
isomorphic-ws@5.0.0(ws@8.18.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
ws: 8.18.3
|
ws: 8.18.0
|
||||||
|
|
||||||
iterator.prototype@1.1.5:
|
iterator.prototype@1.1.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5061,7 +5070,7 @@ snapshots:
|
|||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
js-yaml@4.1.1:
|
js-yaml@4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
|
|
||||||
@@ -5109,54 +5118,50 @@ snapshots:
|
|||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
|
|
||||||
lightningcss-android-arm64@1.30.2:
|
lightningcss-darwin-arm64@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-darwin-arm64@1.30.2:
|
lightningcss-darwin-x64@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-darwin-x64@1.30.2:
|
lightningcss-freebsd-x64@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-freebsd-x64@1.30.2:
|
lightningcss-linux-arm-gnueabihf@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-linux-arm-gnueabihf@1.30.2:
|
lightningcss-linux-arm64-gnu@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-linux-arm64-gnu@1.30.2:
|
lightningcss-linux-arm64-musl@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-linux-arm64-musl@1.30.2:
|
lightningcss-linux-x64-gnu@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-linux-x64-gnu@1.30.2:
|
lightningcss-linux-x64-musl@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-linux-x64-musl@1.30.2:
|
lightningcss-win32-arm64-msvc@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-win32-arm64-msvc@1.30.2:
|
lightningcss-win32-x64-msvc@1.30.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
lightningcss-win32-x64-msvc@1.30.2:
|
lightningcss@1.30.1:
|
||||||
optional: true
|
|
||||||
|
|
||||||
lightningcss@1.30.2:
|
|
||||||
dependencies:
|
dependencies:
|
||||||
detect-libc: 2.1.2
|
detect-libc: 2.1.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
lightningcss-android-arm64: 1.30.2
|
lightningcss-darwin-arm64: 1.30.1
|
||||||
lightningcss-darwin-arm64: 1.30.2
|
lightningcss-darwin-x64: 1.30.1
|
||||||
lightningcss-darwin-x64: 1.30.2
|
lightningcss-freebsd-x64: 1.30.1
|
||||||
lightningcss-freebsd-x64: 1.30.2
|
lightningcss-linux-arm-gnueabihf: 1.30.1
|
||||||
lightningcss-linux-arm-gnueabihf: 1.30.2
|
lightningcss-linux-arm64-gnu: 1.30.1
|
||||||
lightningcss-linux-arm64-gnu: 1.30.2
|
lightningcss-linux-arm64-musl: 1.30.1
|
||||||
lightningcss-linux-arm64-musl: 1.30.2
|
lightningcss-linux-x64-gnu: 1.30.1
|
||||||
lightningcss-linux-x64-gnu: 1.30.2
|
lightningcss-linux-x64-musl: 1.30.1
|
||||||
lightningcss-linux-x64-musl: 1.30.2
|
lightningcss-win32-arm64-msvc: 1.30.1
|
||||||
lightningcss-win32-arm64-msvc: 1.30.2
|
lightningcss-win32-x64-msvc: 1.30.1
|
||||||
lightningcss-win32-x64-msvc: 1.30.2
|
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5187,7 +5192,7 @@ snapshots:
|
|||||||
|
|
||||||
luxon@3.6.1: {}
|
luxon@3.6.1: {}
|
||||||
|
|
||||||
magic-string@0.30.21:
|
magic-string@0.30.19:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
@@ -5235,6 +5240,10 @@ snapshots:
|
|||||||
minipass: 7.1.2
|
minipass: 7.1.2
|
||||||
rimraf: 5.0.10
|
rimraf: 5.0.10
|
||||||
|
|
||||||
|
minizlib@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
minipass: 7.1.2
|
||||||
|
|
||||||
mkdirp-classic@0.5.3: {}
|
mkdirp-classic@0.5.3: {}
|
||||||
|
|
||||||
mkdirp@3.0.1: {}
|
mkdirp@3.0.1: {}
|
||||||
@@ -5250,7 +5259,7 @@ snapshots:
|
|||||||
|
|
||||||
net@1.0.2: {}
|
net@1.0.2: {}
|
||||||
|
|
||||||
next-i18next@12.1.0(next@15.5.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
next-i18next@12.1.0(next@15.5.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.26.9
|
'@babel/runtime': 7.26.9
|
||||||
'@types/hoist-non-react-statics': 3.3.6
|
'@types/hoist-non-react-statics': 3.3.6
|
||||||
@@ -5258,18 +5267,18 @@ snapshots:
|
|||||||
hoist-non-react-statics: 3.3.2
|
hoist-non-react-statics: 3.3.2
|
||||||
i18next: 21.10.0
|
i18next: 21.10.0
|
||||||
i18next-fs-backend: 1.2.0
|
i18next-fs-backend: 1.2.0
|
||||||
next: 15.5.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
next: 15.5.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-i18next: 11.18.6(i18next@21.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
react-i18next: 11.18.6(i18next@21.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- react-dom
|
- react-dom
|
||||||
- react-native
|
- react-native
|
||||||
|
|
||||||
next@15.5.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
next@15.5.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.5.9
|
'@next/env': 15.5.7
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
caniuse-lite: 1.0.30001760
|
caniuse-lite: 1.0.30001759
|
||||||
postcss: 8.4.31
|
postcss: 8.4.31
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
@@ -5504,7 +5513,7 @@ snapshots:
|
|||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
typescript: 5.7.3
|
typescript: 5.7.3
|
||||||
|
|
||||||
react-icons@5.5.0(react@18.3.1):
|
react-icons@5.4.0(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
@@ -5927,9 +5936,9 @@ snapshots:
|
|||||||
|
|
||||||
tailwindcss@4.0.9: {}
|
tailwindcss@4.0.9: {}
|
||||||
|
|
||||||
tailwindcss@4.1.18: {}
|
tailwindcss@4.1.14: {}
|
||||||
|
|
||||||
tapable@2.3.0: {}
|
tapable@2.2.3: {}
|
||||||
|
|
||||||
tar-fs@2.1.3:
|
tar-fs@2.1.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5955,6 +5964,14 @@ snapshots:
|
|||||||
mkdirp: 3.0.1
|
mkdirp: 3.0.1
|
||||||
yallist: 5.0.0
|
yallist: 5.0.0
|
||||||
|
|
||||||
|
tar@7.5.1:
|
||||||
|
dependencies:
|
||||||
|
'@isaacs/fs-minipass': 4.0.1
|
||||||
|
chownr: 3.0.0
|
||||||
|
minipass: 7.1.2
|
||||||
|
minizlib: 3.1.0
|
||||||
|
yallist: 5.0.0
|
||||||
|
|
||||||
telnet-client@2.2.6:
|
telnet-client@2.2.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
net: 1.0.2
|
net: 1.0.2
|
||||||
@@ -6080,9 +6097,12 @@ snapshots:
|
|||||||
'@unrs/resolver-binding-win32-ia32-msvc': 1.3.3
|
'@unrs/resolver-binding-win32-ia32-msvc': 1.3.3
|
||||||
'@unrs/resolver-binding-win32-x64-msvc': 1.3.3
|
'@unrs/resolver-binding-win32-x64-msvc': 1.3.3
|
||||||
|
|
||||||
urbackup-server-api@0.91.0:
|
urbackup-server-api@0.8.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
async-mutex: 0.5.0
|
async-mutex: 0.3.2
|
||||||
|
node-fetch: 2.7.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6218,7 +6238,7 @@ snapshots:
|
|||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
ws@8.18.3: {}
|
ws@8.18.0: {}
|
||||||
|
|
||||||
xml-js@1.6.11:
|
xml-js@1.6.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -272,7 +272,7 @@
|
|||||||
"approved": "Approved",
|
"approved": "Approved",
|
||||||
"available": "Available"
|
"available": "Available"
|
||||||
},
|
},
|
||||||
"jellyseerr": {
|
"seerr": {
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"approved": "Approved",
|
"approved": "Approved",
|
||||||
"available": "Available",
|
"available": "Available",
|
||||||
@@ -532,8 +532,7 @@
|
|||||||
"child_bridges_status": "{{ok}}/{{total}}",
|
"child_bridges_status": "{{ok}}/{{total}}",
|
||||||
"up": "Up",
|
"up": "Up",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"down": "Down",
|
"down": "Down"
|
||||||
"ok": "Ok"
|
|
||||||
},
|
},
|
||||||
"healthchecks": {
|
"healthchecks": {
|
||||||
"new": "New",
|
"new": "New",
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ export default function Resource({
|
|||||||
wide ? " min-w-[120px]" : "min-w-[85px]"
|
wide ? " min-w-[120px]" : "min-w-[85px]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between gap-2">
|
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
<div className="pl-0.5">{value}</div>
|
<div className="pl-0.5">{value}</div>
|
||||||
<div className="pr-1">{label}</div>
|
<div className="pr-1">{label}</div>
|
||||||
</div>
|
</div>
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between gap-2">
|
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
<div className="pl-0.5">{expandedValue}</div>
|
<div className="pl-0.5">{expandedValue}</div>
|
||||||
<div className="pr-1">{expandedLabel}</div>
|
<div className="pr-1">{expandedLabel}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default async function handler(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type === "network") {
|
if (type === "network") {
|
||||||
let networkData = await si.networkStats("*");
|
let networkData = await si.networkStats();
|
||||||
let interfaceDefault;
|
let interfaceDefault;
|
||||||
logger.debug("networkData:", JSON.stringify(networkData));
|
logger.debug("networkData:", JSON.stringify(networkData));
|
||||||
if (interfaceName && interfaceName !== "default") {
|
if (interfaceName && interfaceName !== "default") {
|
||||||
|
|||||||
@@ -554,38 +554,48 @@ export default function Wrapper({ initialSettings, fallback }) {
|
|||||||
html.classList.add(desiredThemeClass);
|
html.classList.add(desiredThemeClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any previously applied inline styles
|
if (backgroundImage) {
|
||||||
body.style.backgroundImage = "";
|
const safeBackgroundImage = backgroundImage.replace(/'/g, "\\'");
|
||||||
body.style.backgroundColor = "";
|
body.style.backgroundImage = `linear-gradient(rgb(var(--bg-color) / ${opacity}), rgb(var(--bg-color) / ${opacity})), url('${safeBackgroundImage}')`;
|
||||||
body.style.backgroundAttachment = "";
|
body.style.backgroundSize = "cover";
|
||||||
|
body.style.backgroundPosition = "center";
|
||||||
|
body.style.backgroundAttachment = "fixed";
|
||||||
|
body.style.backgroundRepeat = "no-repeat";
|
||||||
|
body.style.backgroundColor = "";
|
||||||
|
} else {
|
||||||
|
body.style.backgroundImage = "none";
|
||||||
|
body.style.backgroundColor = "rgb(var(--bg-color))";
|
||||||
|
body.style.backgroundSize = "";
|
||||||
|
body.style.backgroundPosition = "";
|
||||||
|
body.style.backgroundAttachment = "";
|
||||||
|
body.style.backgroundRepeat = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
body.style.backgroundImage = "";
|
||||||
|
body.style.backgroundColor = "";
|
||||||
|
body.style.backgroundSize = "";
|
||||||
|
body.style.backgroundPosition = "";
|
||||||
|
body.style.backgroundAttachment = "";
|
||||||
|
body.style.backgroundRepeat = "";
|
||||||
|
};
|
||||||
}, [backgroundImage, opacity, theme, color, initialSettings.color]);
|
}, [backgroundImage, opacity, theme, color, initialSettings.color]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div id="page_wrapper" className="relative min-h-screen">
|
||||||
{backgroundImage && (
|
<div
|
||||||
<div
|
id="inner_wrapper"
|
||||||
id="background"
|
tabIndex="-1"
|
||||||
aria-hidden="true"
|
className={classNames(
|
||||||
style={{
|
"w-full min-h-screen overflow-auto",
|
||||||
backgroundImage: `linear-gradient(rgb(var(--bg-color) / ${opacity}), rgb(var(--bg-color) / ${opacity})), url('${backgroundImage}')`,
|
backgroundBlur &&
|
||||||
}}
|
`backdrop-blur${initialSettings.background.blur?.length ? `-${initialSettings.background.blur}` : ""}`,
|
||||||
/>
|
backgroundSaturate && `backdrop-saturate-${initialSettings.background.saturate}`,
|
||||||
)}
|
backgroundBrightness && `backdrop-brightness-${initialSettings.background.brightness}`,
|
||||||
<div id="page_wrapper" className="relative h-full">
|
)}
|
||||||
<div
|
>
|
||||||
id="inner_wrapper"
|
<Index initialSettings={initialSettings} fallback={fallback} />
|
||||||
tabIndex="-1"
|
|
||||||
className={classNames(
|
|
||||||
"w-full h-full overflow-auto",
|
|
||||||
backgroundBlur &&
|
|
||||||
`backdrop-blur${initialSettings.background.blur?.length ? `-${initialSettings.background.blur}` : ""}`,
|
|
||||||
backgroundSaturate && `backdrop-saturate-${initialSettings.background.saturate}`,
|
|
||||||
backgroundBrightness && `backdrop-brightness-${initialSettings.background.brightness}`,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Index initialSettings={initialSettings} fallback={fallback} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,18 +30,6 @@ body,
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: rgb(var(--bg-color));
|
|
||||||
}
|
|
||||||
|
|
||||||
#background {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
z-index: 0;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-attachment: scroll;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function parseServicesToGroups(services) {
|
|||||||
serviceGroupServices.push({
|
serviceGroupServices.push({
|
||||||
name: entryName,
|
name: entryName,
|
||||||
...entries[entryName],
|
...entries[entryName],
|
||||||
weight: entries[entryName].weight ?? (serviceGroupServices.length + 1) * 100, // default weight
|
weight: entries[entryName].weight || serviceGroupServices.length * 100, // default weight
|
||||||
type: "service",
|
type: "service",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,6 @@ export async function servicesFromDocker() {
|
|||||||
constructedService = {
|
constructedService = {
|
||||||
container: containerName.replace(/^\//, ""),
|
container: containerName.replace(/^\//, ""),
|
||||||
server: serverName,
|
server: serverName,
|
||||||
weight: 0,
|
|
||||||
type: "service",
|
type: "service",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -314,7 +313,7 @@ export function cleanServiceGroups(groups) {
|
|||||||
// gamedig
|
// gamedig
|
||||||
gameToken,
|
gameToken,
|
||||||
|
|
||||||
// authentik, beszel, glances, immich, komga, mealie, netalertx, pihole, pfsense, speedtest
|
// authentik, beszel, glances, immich, komga, mealie, pihole, pfsense, speedtest
|
||||||
version,
|
version,
|
||||||
|
|
||||||
// glances
|
// glances
|
||||||
@@ -559,7 +558,6 @@ export function cleanServiceGroups(groups) {
|
|||||||
"immich",
|
"immich",
|
||||||
"komga",
|
"komga",
|
||||||
"mealie",
|
"mealie",
|
||||||
"netalertx",
|
|
||||||
"pfsense",
|
"pfsense",
|
||||||
"pihole",
|
"pihole",
|
||||||
"speedtest",
|
"speedtest",
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ export default async function credentialedProxyHandler(req, res, map) {
|
|||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
...(widgets[widget.type].headers ?? {}),
|
|
||||||
...(widget.headers ?? {}),
|
|
||||||
...(req.extraHeaders ?? {}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (widget.type === "stocks") {
|
if (widget.type === "stocks") {
|
||||||
|
|||||||
@@ -25,11 +25,7 @@ export default async function genericProxyHandler(req, res, map) {
|
|||||||
}
|
}
|
||||||
const url = new URL(urlString);
|
const url = new URL(urlString);
|
||||||
|
|
||||||
const headers = {
|
const headers = req.extraHeaders ?? widget.headers ?? widgets[widget.type].headers ?? {};
|
||||||
...(widgets[widget.type].headers ?? {}),
|
|
||||||
...(widget.headers ?? {}),
|
|
||||||
...(req.extraHeaders ?? {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (widget.username && widget.password) {
|
if (widget.username && widget.password) {
|
||||||
headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`;
|
headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const components = {
|
|||||||
jackett: dynamic(() => import("./jackett/component")),
|
jackett: dynamic(() => import("./jackett/component")),
|
||||||
jdownloader: dynamic(() => import("./jdownloader/component")),
|
jdownloader: dynamic(() => import("./jdownloader/component")),
|
||||||
jellyfin: dynamic(() => import("./emby/component")),
|
jellyfin: dynamic(() => import("./emby/component")),
|
||||||
jellyseerr: dynamic(() => import("./jellyseerr/component")),
|
jellyseerr: dynamic(() => import("./seerr/component")),
|
||||||
jellystat: dynamic(() => import("./jellystat/component")),
|
jellystat: dynamic(() => import("./jellystat/component")),
|
||||||
kavita: dynamic(() => import("./kavita/component")),
|
kavita: dynamic(() => import("./kavita/component")),
|
||||||
komga: dynamic(() => import("./komga/component")),
|
komga: dynamic(() => import("./komga/component")),
|
||||||
@@ -120,6 +120,7 @@ const components = {
|
|||||||
rutorrent: dynamic(() => import("./rutorrent/component")),
|
rutorrent: dynamic(() => import("./rutorrent/component")),
|
||||||
sabnzbd: dynamic(() => import("./sabnzbd/component")),
|
sabnzbd: dynamic(() => import("./sabnzbd/component")),
|
||||||
scrutiny: dynamic(() => import("./scrutiny/component")),
|
scrutiny: dynamic(() => import("./scrutiny/component")),
|
||||||
|
seerr: dynamic(() => import("./seerr/component")),
|
||||||
slskd: dynamic(() => import("./slskd/component")),
|
slskd: dynamic(() => import("./slskd/component")),
|
||||||
sonarr: dynamic(() => import("./sonarr/component")),
|
sonarr: dynamic(() => import("./sonarr/component")),
|
||||||
speedtest: dynamic(() => import("./speedtest/component")),
|
speedtest: dynamic(() => import("./speedtest/component")),
|
||||||
|
|||||||
@@ -85,9 +85,6 @@ export default async function fritzboxProxyHandler(req, res) {
|
|||||||
requestExternalIPv6Prefix ? requestEndpoint(apiBaseUrl, "WANIPConnection", "X_AVM_DE_GetIPv6Prefix") : null,
|
requestExternalIPv6Prefix ? requestEndpoint(apiBaseUrl, "WANIPConnection", "X_AVM_DE_GetIPv6Prefix") : null,
|
||||||
])
|
])
|
||||||
.then(([statusInfo, linkProperties, addonInfos, externalIPAddress, externalIPv6Address, externalIPv6Prefix]) => {
|
.then(([statusInfo, linkProperties, addonInfos, externalIPAddress, externalIPv6Address, externalIPv6Prefix]) => {
|
||||||
const ipv6Prefix = externalIPv6Prefix?.NewIPv6Prefix;
|
|
||||||
const ipv6Len = externalIPv6Prefix?.NewPrefixLength;
|
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
connectionStatus: statusInfo?.NewConnectionStatus || "Unconfigured",
|
connectionStatus: statusInfo?.NewConnectionStatus || "Unconfigured",
|
||||||
uptime: statusInfo?.NewUptime || 0,
|
uptime: statusInfo?.NewUptime || 0,
|
||||||
@@ -99,7 +96,7 @@ export default async function fritzboxProxyHandler(req, res) {
|
|||||||
sent: addonInfos?.NewX_AVM_DE_TotalBytesSent64 || 0,
|
sent: addonInfos?.NewX_AVM_DE_TotalBytesSent64 || 0,
|
||||||
externalIPAddress: externalIPAddress?.NewExternalIPAddress || null,
|
externalIPAddress: externalIPAddress?.NewExternalIPAddress || null,
|
||||||
externalIPv6Address: externalIPv6Address?.NewExternalIPv6Address || null,
|
externalIPv6Address: externalIPv6Address?.NewExternalIPv6Address || null,
|
||||||
externalIPv6Prefix: ipv6Prefix && ipv6Len != null ? `${ipv6Prefix}/${ipv6Len}` : (ipv6Prefix ?? null),
|
externalIPv6Prefix: externalIPv6Prefix?.NewIPv6Prefix || null,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ export default function Component({ service }) {
|
|||||||
|
|
||||||
const { widget } = service;
|
const { widget } = service;
|
||||||
|
|
||||||
const dataEndpoint = widget?.version > 1 ? "datav2" : "data";
|
const { data: netalertxData, error: netalertxError } = useWidgetAPI(widget, "data");
|
||||||
|
|
||||||
const { data: netalertxData, error: netalertxError } = useWidgetAPI(widget, dataEndpoint);
|
|
||||||
|
|
||||||
if (netalertxError) {
|
if (netalertxError) {
|
||||||
return <Container service={service} error={netalertxError} />;
|
return <Container service={service} error={netalertxError} />;
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||||
|
|
||||||
const widget = {
|
const widget = {
|
||||||
api: "{url}/{endpoint}",
|
api: "{url}/php/server/devices.php?action=getDevicesTotals",
|
||||||
proxyHandler: credentialedProxyHandler,
|
proxyHandler: credentialedProxyHandler,
|
||||||
|
|
||||||
mappings: {
|
mappings: {
|
||||||
data: {
|
data: {
|
||||||
endpoint: "php/server/devices.php?action=getDevicesTotals",
|
endpoint: "data",
|
||||||
},
|
|
||||||
datav2: {
|
|
||||||
endpoint: "devices/totals",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ async function fetchFromPlexAPI(endpoint, widget) {
|
|||||||
export default async function plexProxyHandler(req, res) {
|
export default async function plexProxyHandler(req, res) {
|
||||||
const widget = await getWidget(req);
|
const widget = await getWidget(req);
|
||||||
|
|
||||||
const { service, index } = req.query;
|
const { service } = req.query;
|
||||||
|
|
||||||
if (!widget) {
|
if (!widget) {
|
||||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||||
@@ -85,19 +85,19 @@ export default async function plexProxyHandler(req, res) {
|
|||||||
streams = apiData.MediaContainer._attributes.size;
|
streams = apiData.MediaContainer._attributes.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
let libraries = cache.get(`${librariesCacheKey}.${service}.${index}`);
|
let libraries = cache.get(`${librariesCacheKey}.${service}`);
|
||||||
if (libraries === null) {
|
if (libraries === null) {
|
||||||
logger.debug("Getting libraries from Plex API");
|
logger.debug("Getting libraries from Plex API");
|
||||||
[status, apiData] = await fetchFromPlexAPI("/library/sections", widget);
|
[status, apiData] = await fetchFromPlexAPI("/library/sections", widget);
|
||||||
if (apiData && apiData.MediaContainer) {
|
if (apiData && apiData.MediaContainer) {
|
||||||
libraries = [].concat(apiData.MediaContainer.Directory);
|
libraries = [].concat(apiData.MediaContainer.Directory);
|
||||||
cache.put(`${librariesCacheKey}.${service}.${index}`, libraries, 1000 * 60 * 60 * 6);
|
cache.put(`${librariesCacheKey}.${service}`, libraries, 1000 * 60 * 60 * 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let albums = cache.get(`${albumsCacheKey}.${service}.${index}`);
|
let albums = cache.get(`${albumsCacheKey}.${service}`);
|
||||||
let movies = cache.get(`${moviesCacheKey}.${service}.${index}`);
|
let movies = cache.get(`${moviesCacheKey}.${service}`);
|
||||||
let tv = cache.get(`${tvCacheKey}.${service}.${index}`);
|
let tv = cache.get(`${tvCacheKey}.${service}`);
|
||||||
if (albums === null || movies === null || tv === null) {
|
if (albums === null || movies === null || tv === null) {
|
||||||
albums = 0;
|
albums = 0;
|
||||||
movies = 0;
|
movies = 0;
|
||||||
@@ -123,9 +123,9 @@ export default async function plexProxyHandler(req, res) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
cache.put(`${albumsCacheKey}.${service}.${index}`, albums, 1000 * 60 * 10);
|
cache.put(`${albumsCacheKey}.${service}`, albums, 1000 * 60 * 10);
|
||||||
cache.put(`${tvCacheKey}.${service}.${index}`, tv, 1000 * 60 * 10);
|
cache.put(`${tvCacheKey}.${service}`, tv, 1000 * 60 * 10);
|
||||||
cache.put(`${moviesCacheKey}.${service}.${index}`, movies, 1000 * 60 * 10);
|
cache.put(`${moviesCacheKey}.${service}`, movies, 1000 * 60 * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
|
|||||||
@@ -11,15 +11,6 @@ const logger = createLogger(proxyName);
|
|||||||
const sessionCacheKey = `${proxyName}__sessionId`;
|
const sessionCacheKey = `${proxyName}__sessionId`;
|
||||||
const isNgCacheKey = `${proxyName}__isNg`;
|
const isNgCacheKey = `${proxyName}__isNg`;
|
||||||
|
|
||||||
function parsePyloadResponse(url, data) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(Buffer.from(data).toString());
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(`Error communicating with pyload API at ${url}, returned: ${JSON.stringify(data)}`);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchFromPyloadAPI(url, sessionId, params, service) {
|
async function fetchFromPyloadAPI(url, sessionId, params, service) {
|
||||||
const options = {
|
const options = {
|
||||||
body: params
|
body: params
|
||||||
@@ -42,33 +33,13 @@ async function fetchFromPyloadAPI(url, sessionId, params, service) {
|
|||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const [status, contentType, data, responseHeaders] = await httpProxy(url, options);
|
const [status, contentType, data, responseHeaders] = await httpProxy(url, options);
|
||||||
const returnData = parsePyloadResponse(url, data);
|
let returnData;
|
||||||
return [status, returnData, responseHeaders];
|
try {
|
||||||
}
|
returnData = JSON.parse(Buffer.from(data).toString());
|
||||||
|
} catch (e) {
|
||||||
async function fetchFromPyloadAPIBasic(url, params, username, password) {
|
logger.error(`Error communicating with pyload API at ${url}, returned: ${JSON.stringify(data)}`);
|
||||||
const parsedUrl = new URL(url);
|
returnData = data;
|
||||||
const isGetRequest = !params || Object.keys(params).length === 0;
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
method: isGetRequest ? "GET" : "POST",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isGetRequest) {
|
|
||||||
if (params) {
|
|
||||||
Object.keys(params).forEach((key) => parsedUrl.searchParams.append(key, params[key]));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
options.headers["Content-Type"] = "application/json";
|
|
||||||
options.body = JSON.stringify(params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
const [status, contentType, data, responseHeaders] = await httpProxy(parsedUrl, options);
|
|
||||||
const returnData = parsePyloadResponse(parsedUrl, data);
|
|
||||||
return [status, returnData, responseHeaders];
|
return [status, returnData, responseHeaders];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,43 +66,24 @@ async function login(loginUrl, service, username, password = "") {
|
|||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function pyloadProxyHandler(req, res, map = {}) {
|
export default async function pyloadProxyHandler(req, res) {
|
||||||
const { group, service, endpoint, index } = req.query;
|
const { group, service, endpoint, index } = req.query;
|
||||||
const { ngEndpoint } = map;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (group && service) {
|
if (group && service) {
|
||||||
const widget = await getServiceWidget(group, service, index);
|
const widget = await getServiceWidget(group, service, index);
|
||||||
|
|
||||||
if (widget) {
|
if (widget) {
|
||||||
const apiTemplate = widgets[widget.type].api;
|
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||||
const url = new URL(formatApiCall(apiTemplate, { endpoint, ...widget }));
|
|
||||||
const ngUrl = ngEndpoint ? new URL(formatApiCall(apiTemplate, { endpoint: ngEndpoint, ...widget })) : url;
|
|
||||||
const loginUrl = `${widget.url}/api/login`;
|
const loginUrl = `${widget.url}/api/login`;
|
||||||
const hasCredentials = widget.username && widget.password;
|
|
||||||
|
|
||||||
if (hasCredentials) {
|
|
||||||
const [status, data] = await fetchFromPyloadAPIBasic(ngUrl, null, widget.username, widget.password);
|
|
||||||
|
|
||||||
if (status === 200 && !data?.error) {
|
|
||||||
cache.put(`${isNgCacheKey}.${service}`, true);
|
|
||||||
return res.json(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === 401) {
|
|
||||||
return res
|
|
||||||
.status(status)
|
|
||||||
.send({ error: { message: "Invalid credentials communicating with Pyload API", data } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sessionId =
|
let sessionId =
|
||||||
cache.get(`${sessionCacheKey}.${service}`) ??
|
cache.get(`${sessionCacheKey}.${service}`) ??
|
||||||
(await login(loginUrl, service, widget.username, widget.password));
|
(await login(loginUrl, service, widget.username, widget.password));
|
||||||
let [status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
|
let [status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
|
||||||
|
|
||||||
if (status === 403 || status === 401 || (status === 400 && data?.error?.includes("CSRF token"))) {
|
if (status === 403 || status === 401) {
|
||||||
logger.info("Failed to retrieve data from Pyload API with session auth, trying to login again...");
|
logger.info("Failed to retrieve data from Pyload API, trying to login again...");
|
||||||
cache.del(`${sessionCacheKey}.${service}`);
|
cache.del(`${sessionCacheKey}.${service}`);
|
||||||
sessionId = await login(loginUrl, service, widget.username, widget.password);
|
sessionId = await login(loginUrl, service, widget.username, widget.password);
|
||||||
[status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
|
[status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const widget = {
|
|||||||
mappings: {
|
mappings: {
|
||||||
status: {
|
status: {
|
||||||
endpoint: "statusServer",
|
endpoint: "statusServer",
|
||||||
map: { ngEndpoint: "status_server" },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import Container from "components/services/widget/container";
|
|||||||
|
|
||||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||||
|
|
||||||
export const jellyseerrDefaultFields = ["pending", "approved", "available"];
|
export const seerrDefaultFields = ["pending", "approved", "available"];
|
||||||
|
|
||||||
export default function Component({ service }) {
|
export default function Component({ service }) {
|
||||||
const { widget } = service;
|
const { widget } = service;
|
||||||
|
|
||||||
widget.fields = widget?.fields?.length ? widget.fields : jellyseerrDefaultFields;
|
widget.fields = widget?.fields?.length ? widget.fields : seerrDefaultFields;
|
||||||
const isIssueEnabled = widget.fields.includes("issues");
|
const isIssueEnabled = widget.fields.includes("issues");
|
||||||
|
|
||||||
const { data: statsData, error: statsError } = useWidgetAPI(widget, "request/count");
|
const { data: statsData, error: statsError } = useWidgetAPI(widget, "request/count");
|
||||||
@@ -20,20 +20,20 @@ export default function Component({ service }) {
|
|||||||
if (!statsData || (isIssueEnabled && !issueData)) {
|
if (!statsData || (isIssueEnabled && !issueData)) {
|
||||||
return (
|
return (
|
||||||
<Container service={service}>
|
<Container service={service}>
|
||||||
<Block label="jellyseerr.pending" />
|
<Block label="seerr.pending" />
|
||||||
<Block label="jellyseerr.approved" />
|
<Block label="seerr.approved" />
|
||||||
<Block label="jellyseerr.available" />
|
<Block label="seerr.available" />
|
||||||
<Block label="jellyseerr.issues" />
|
<Block label="seerr.issues" />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container service={service}>
|
<Container service={service}>
|
||||||
<Block label="jellyseerr.pending" value={statsData.pending} />
|
<Block label="seerr.pending" value={statsData.pending} />
|
||||||
<Block label="jellyseerr.approved" value={statsData.approved} />
|
<Block label="seerr.approved" value={statsData.approved} />
|
||||||
<Block label="jellyseerr.available" value={statsData.available} />
|
<Block label="seerr.available" value={statsData.available} />
|
||||||
<Block label="jellyseerr.issues" value={`${issueData?.open} / ${issueData?.total}`} />
|
<Block label="seerr.issues" value={`${issueData?.open} / ${issueData?.total}`} />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -12,8 +12,8 @@ export default function Component({ service }) {
|
|||||||
|
|
||||||
const { data: alertData, error: alertError } = useWidgetAPI(widget, "alerts");
|
const { data: alertData, error: alertError } = useWidgetAPI(widget, "alerts");
|
||||||
const { data: statusData, error: statusError } = useWidgetAPI(widget, "status");
|
const { data: statusData, error: statusError } = useWidgetAPI(widget, "status");
|
||||||
const { data: poolsData, error: poolsError } = useWidgetAPI(widget, widget?.enablePools ? "pools" : "");
|
const { data: poolsData, error: poolsError } = useWidgetAPI(widget, widget?.enablePools ? "pools" : null);
|
||||||
const { data: datasetData, error: datasetError } = useWidgetAPI(widget, widget?.enablePools ? "dataset" : "");
|
const { data: datasetData, error: datasetError } = useWidgetAPI(widget, widget?.enablePools ? "dataset" : null);
|
||||||
|
|
||||||
if (alertError || statusError || poolsError) {
|
if (alertError || statusError || poolsError) {
|
||||||
const finalError = alertError ?? statusError ?? poolsError ?? datasetError;
|
const finalError = alertError ?? statusError ?? poolsError ?? datasetError;
|
||||||
|
|||||||
@@ -1,172 +0,0 @@
|
|||||||
import WebSocket from "ws";
|
|
||||||
|
|
||||||
import getServiceWidget from "utils/config/service-helpers";
|
|
||||||
import createLogger from "utils/logger";
|
|
||||||
import { formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers";
|
|
||||||
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
|
||||||
import validateWidgetData from "utils/proxy/validate-widget-data";
|
|
||||||
import widgets from "widgets/widgets";
|
|
||||||
|
|
||||||
const logger = createLogger("truenasProxyHandler");
|
|
||||||
|
|
||||||
function waitForEvent(ws, handler, { event = "message", parseJson = true } = {}) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
cleanup();
|
|
||||||
reject(new Error("TrueNAS websocket wait timed out"));
|
|
||||||
}, 10000);
|
|
||||||
|
|
||||||
const handleEvent = (payload) => {
|
|
||||||
try {
|
|
||||||
let parsed = payload;
|
|
||||||
if (parseJson) {
|
|
||||||
if (Buffer.isBuffer(payload)) {
|
|
||||||
parsed = JSON.parse(payload.toString());
|
|
||||||
} else if (typeof payload === "string") {
|
|
||||||
parsed = JSON.parse(payload);
|
|
||||||
}
|
|
||||||
logger.info("Received TrueNAS websocket message: %o", parsed);
|
|
||||||
} else {
|
|
||||||
logger.info("Received TrueNAS websocket message: %o", payload);
|
|
||||||
}
|
|
||||||
const handlerResult = handler(parsed);
|
|
||||||
if (handlerResult !== undefined) {
|
|
||||||
cleanup();
|
|
||||||
if (handlerResult instanceof Error) {
|
|
||||||
reject(handlerResult);
|
|
||||||
} else {
|
|
||||||
resolve(handlerResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
cleanup();
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleError = (err) => {
|
|
||||||
cleanup();
|
|
||||||
logger.error("TrueNAS websocket error: %s", err?.message ?? err);
|
|
||||||
reject(err);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
cleanup();
|
|
||||||
logger.error("TrueNAS websocket connection closed unexpectedly");
|
|
||||||
reject(new Error("TrueNAS websocket closed the connection"));
|
|
||||||
};
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
ws.off(event, handleEvent);
|
|
||||||
ws.off("error", handleError);
|
|
||||||
ws.off("close", handleClose);
|
|
||||||
}
|
|
||||||
|
|
||||||
ws.on(event, handleEvent);
|
|
||||||
ws.on("error", handleError);
|
|
||||||
ws.on("close", handleClose);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextId = 1;
|
|
||||||
async function sendMethod(ws, method, params = []) {
|
|
||||||
const id = nextId++;
|
|
||||||
const payload = { jsonrpc: "2.0", id, method, params };
|
|
||||||
logger.info("Sending TrueNAS websocket method %s with id %d", method, id);
|
|
||||||
ws.send(JSON.stringify(payload));
|
|
||||||
|
|
||||||
return waitForEvent(ws, (message) => {
|
|
||||||
if (message?.id !== id) return undefined;
|
|
||||||
if (message?.error) {
|
|
||||||
return new Error(message.error?.message || JSON.stringify(message.error));
|
|
||||||
}
|
|
||||||
return message?.result ?? message;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function authenticate(ws, widget) {
|
|
||||||
if (widget?.key) {
|
|
||||||
try {
|
|
||||||
const apiKeyResult = await sendMethod(ws, "auth.login_with_api_key", [widget.key]);
|
|
||||||
if (apiKeyResult === true) return;
|
|
||||||
logger.warn("TrueNAS API key authentication failed, falling back to username/password when available.");
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn("TrueNAS API key authentication failed: %s", err?.message ?? err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget?.username && widget?.password) {
|
|
||||||
const loginResult = await sendMethod(ws, "auth.login", [widget.username, widget.password]);
|
|
||||||
if (loginResult === true) return;
|
|
||||||
logger.warn("TrueNAS username/password authentication failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("TrueNAS authentication failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function truenasProxyHandler(req, res, map) {
|
|
||||||
const { group, service, endpoint, index } = req.query;
|
|
||||||
if (!group || !service) {
|
|
||||||
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
|
|
||||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const widget = await getServiceWidget(group, service, index);
|
|
||||||
|
|
||||||
if (!widget) {
|
|
||||||
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
|
|
||||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!endpoint) {
|
|
||||||
return res.status(204).end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const version = Number(widget.version ?? 1);
|
|
||||||
if (Number.isNaN(version) || version < 2) {
|
|
||||||
// Use legacy REST proxy for version 1
|
|
||||||
return credentialedProxyHandler(req, res, map);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mappingEntry = Object.values(widgets[widget.type].mappings).find((mapping) => mapping.endpoint === endpoint);
|
|
||||||
const wsMethod = mappingEntry.wsMethod;
|
|
||||||
|
|
||||||
if (!wsMethod) {
|
|
||||||
logger.debug("Missing wsMethod mapping for TrueNAS endpoint %s", endpoint);
|
|
||||||
return res.status(500).json({ error: "Missing wsMethod mapping." });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let data;
|
|
||||||
const wsUrl = new URL(formatApiCall(widgets[widget.type].wsAPI, { ...widget }));
|
|
||||||
const useSecure = wsUrl.protocol === "https:" || Boolean(widget.key); // API key requires secure connection
|
|
||||||
if (useSecure && wsUrl.protocol !== "https:")
|
|
||||||
logger.info("Upgrading TrueNAS websocket connection to secure wss://");
|
|
||||||
wsUrl.protocol = useSecure ? "wss:" : "ws:";
|
|
||||||
logger.info("Connecting to TrueNAS websocket at %s", wsUrl);
|
|
||||||
const ws = new WebSocket(wsUrl, { rejectUnauthorized: false });
|
|
||||||
await waitForEvent(ws, () => true, { event: "open", parseJson: false }); // wait for open
|
|
||||||
logger.info("Connected to TrueNAS websocket at %s", wsUrl);
|
|
||||||
try {
|
|
||||||
await authenticate(ws, widget);
|
|
||||||
data = await sendMethod(ws, wsMethod);
|
|
||||||
} finally {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validateWidgetData(widget, endpoint, data)) {
|
|
||||||
return res.status(500).json({ error: { message: "Invalid data", url: sanitizeErrorURL(widget.url), data } });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map) data = map(data);
|
|
||||||
|
|
||||||
return res.status(200).json(data);
|
|
||||||
} catch (err) {
|
|
||||||
if (err?.status) {
|
|
||||||
return res.status(err.status).json({ error: err.message });
|
|
||||||
}
|
|
||||||
logger.warn("Websocket call for TrueNAS failed: %s", err?.message ?? err);
|
|
||||||
return res.status(500).json({ error: err?.message ?? "TrueNAS websocket call failed" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +1,32 @@
|
|||||||
import truenasProxyHandler from "./proxy";
|
|
||||||
|
|
||||||
import { asJson, jsonArrayFilter } from "utils/proxy/api-helpers";
|
import { asJson, jsonArrayFilter } from "utils/proxy/api-helpers";
|
||||||
|
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||||
|
|
||||||
const widget = {
|
const widget = {
|
||||||
api: "{url}/api/v2.0/{endpoint}",
|
api: "{url}/api/v2.0/{endpoint}",
|
||||||
wsAPI: "{url}/api/current",
|
proxyHandler: credentialedProxyHandler,
|
||||||
proxyHandler: truenasProxyHandler,
|
|
||||||
|
|
||||||
mappings: {
|
mappings: {
|
||||||
alerts: {
|
alerts: {
|
||||||
endpoint: "alert/list",
|
endpoint: "alert/list",
|
||||||
wsMethod: "alert.list",
|
map: (data) => ({
|
||||||
map: (data) => {
|
pending: jsonArrayFilter(data, (item) => item?.dismissed === false).length,
|
||||||
if (Array.isArray(data)) {
|
}),
|
||||||
return { pending: data.filter((item) => item?.dismissed === false).length };
|
|
||||||
}
|
|
||||||
return { pending: jsonArrayFilter(data, (item) => item?.dismissed === false).length };
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
endpoint: "system/info",
|
endpoint: "system/info",
|
||||||
wsMethod: "system.info",
|
|
||||||
validate: ["loadavg", "uptime_seconds"],
|
validate: ["loadavg", "uptime_seconds"],
|
||||||
},
|
},
|
||||||
pools: {
|
pools: {
|
||||||
endpoint: "pool",
|
endpoint: "pool",
|
||||||
wsMethod: "pool.query",
|
map: (data) =>
|
||||||
map: (data) => {
|
asJson(data).map((entry) => ({
|
||||||
const list = Array.isArray(data) ? data : asJson(data);
|
|
||||||
return list.map((entry) => ({
|
|
||||||
id: entry.name,
|
id: entry.name,
|
||||||
name: entry.name,
|
name: entry.name,
|
||||||
healthy: entry.healthy,
|
healthy: entry.healthy,
|
||||||
}));
|
})),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
dataset: {
|
dataset: {
|
||||||
endpoint: "pool/dataset",
|
endpoint: "pool/dataset",
|
||||||
wsMethod: "pool.dataset.query",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,12 +49,10 @@ export default function Component({ service }) {
|
|||||||
|
|
||||||
// single monitor
|
// single monitor
|
||||||
const monitor = uptimerobotData.monitors[0];
|
const monitor = uptimerobotData.monitors[0];
|
||||||
const logs = Array.isArray(monitor.logs) ? monitor.logs : [];
|
|
||||||
const lastUpLog = logs.find((log) => log.type === 2);
|
|
||||||
const lastDownLog = logs.find((log) => log.type === 1);
|
|
||||||
|
|
||||||
let status;
|
let status;
|
||||||
let uptime = 0;
|
let uptime = 0;
|
||||||
|
let logIndex = 0;
|
||||||
|
const hasLogs = Array.isArray(monitor.logs) && monitor.logs.length > 0;
|
||||||
|
|
||||||
switch (monitor.status) {
|
switch (monitor.status) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -65,7 +63,8 @@ export default function Component({ service }) {
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
status = t("uptimerobot.up");
|
status = t("uptimerobot.up");
|
||||||
uptime = t("common.duration", { value: lastUpLog?.duration ?? 0 });
|
uptime = t("common.duration", { value: hasLogs ? monitor.logs[0].duration : 0 });
|
||||||
|
logIndex = 1;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
status = t("uptimerobot.seemsdown");
|
status = t("uptimerobot.seemsdown");
|
||||||
@@ -78,14 +77,14 @@ export default function Component({ service }) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastDown = lastDownLog ? new Date(lastDownLog.datetime * 1000).toLocaleString() : "";
|
const lastDown = hasLogs ? new Date(monitor.logs[logIndex].datetime * 1000).toLocaleString() : "";
|
||||||
const downDuration = t("common.duration", { value: lastDownLog?.duration ?? 0 });
|
const downDuration = t("common.duration", { value: hasLogs ? monitor.logs[logIndex].duration : 0 });
|
||||||
const hideDown = !lastDownLog;
|
const hideDown = !hasLogs || (logIndex === 1 && monitor.logs[logIndex].type !== 1);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container service={service}>
|
<Container service={service}>
|
||||||
<Block label="uptimerobot.status" value={status} />
|
<Block label="uptimerobot.status" value={status} />
|
||||||
<Block label="uptimerobot.uptime" value={uptime} />
|
{hasLogs && <Block label="uptimerobot.uptime" value={uptime} />}
|
||||||
{!hideDown && <Block label="uptimerobot.lastDown" value={lastDown} />}
|
{!hideDown && <Block label="uptimerobot.lastDown" value={lastDown} />}
|
||||||
{!hideDown && <Block label="uptimerobot.downDuration" value={downDuration} />}
|
{!hideDown && <Block label="uptimerobot.downDuration" value={downDuration} />}
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -6,17 +6,8 @@ import useWidgetAPI from "utils/proxy/use-widget-api";
|
|||||||
|
|
||||||
const MAX_ALLOWED_FIELDS = 4;
|
const MAX_ALLOWED_FIELDS = 4;
|
||||||
|
|
||||||
const todayDate = new Date();
|
|
||||||
function toApiMonthYear(offset = 0) {
|
|
||||||
// API expects 1-indexed months, wrap around if needed
|
|
||||||
const m = todayDate.getMonth() + 1 + offset;
|
|
||||||
return {
|
|
||||||
month: ((m + 11) % 12) + 1,
|
|
||||||
year: todayDate.getFullYear() + Math.floor((m - 1) / 12),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Component({ service }) {
|
export default function Component({ service }) {
|
||||||
|
const todayDate = new Date();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { widget } = service;
|
const { widget } = service;
|
||||||
|
|
||||||
@@ -38,19 +29,28 @@ export default function Component({ service }) {
|
|||||||
const { data: subscriptionsThisMonthlyCostData, error: subscriptionsThisMonthlyCostError } = useWidgetAPI(
|
const { data: subscriptionsThisMonthlyCostData, error: subscriptionsThisMonthlyCostError } = useWidgetAPI(
|
||||||
widget,
|
widget,
|
||||||
subscriptionsThisMonthlyEndpoint,
|
subscriptionsThisMonthlyEndpoint,
|
||||||
toApiMonthYear(), // this month
|
{
|
||||||
|
month: todayDate.getMonth(),
|
||||||
|
year: todayDate.getFullYear(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const subscriptionsNextMonthlyEndpoint = widget.fields.includes("nextMonthlyCost") ? "get_monthly_cost" : "";
|
const subscriptionsNextMonthlyEndpoint = widget.fields.includes("nextMonthlyCost") ? "get_monthly_cost" : "";
|
||||||
const { data: subscriptionsNextMonthlyCostData, error: subscriptionsNextMonthlyCostError } = useWidgetAPI(
|
const { data: subscriptionsNextMonthlyCostData, error: subscriptionsNextMonthlyCostError } = useWidgetAPI(
|
||||||
widget,
|
widget,
|
||||||
subscriptionsNextMonthlyEndpoint,
|
subscriptionsNextMonthlyEndpoint,
|
||||||
toApiMonthYear(1), // next month
|
{
|
||||||
|
month: todayDate.getMonth() + 1,
|
||||||
|
year: todayDate.getFullYear(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const subscriptionsPreviousMonthlyEndpoint = widget.fields.includes("previousMonthlyCost") ? "get_monthly_cost" : "";
|
const subscriptionsPreviousMonthlyEndpoint = widget.fields.includes("previousMonthlyCost") ? "get_monthly_cost" : "";
|
||||||
const { data: subscriptionsPreviousMonthlyCostData, error: subscriptionsPreviousMonthlyCostError } = useWidgetAPI(
|
const { data: subscriptionsPreviousMonthlyCostData, error: subscriptionsPreviousMonthlyCostError } = useWidgetAPI(
|
||||||
widget,
|
widget,
|
||||||
subscriptionsPreviousMonthlyEndpoint,
|
subscriptionsPreviousMonthlyEndpoint,
|
||||||
toApiMonthYear(-1), // previous month
|
{
|
||||||
|
month: todayDate.getMonth() - 1,
|
||||||
|
year: todayDate.getFullYear(),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ import homebridge from "./homebridge/widget";
|
|||||||
import immich from "./immich/widget";
|
import immich from "./immich/widget";
|
||||||
import jackett from "./jackett/widget";
|
import jackett from "./jackett/widget";
|
||||||
import jdownloader from "./jdownloader/widget";
|
import jdownloader from "./jdownloader/widget";
|
||||||
import jellyseerr from "./jellyseerr/widget";
|
|
||||||
import jellystat from "./jellystat/widget";
|
import jellystat from "./jellystat/widget";
|
||||||
import karakeep from "./karakeep/widget";
|
import karakeep from "./karakeep/widget";
|
||||||
import kavita from "./kavita/widget";
|
import kavita from "./kavita/widget";
|
||||||
@@ -111,6 +110,7 @@ import romm from "./romm/widget";
|
|||||||
import rutorrent from "./rutorrent/widget";
|
import rutorrent from "./rutorrent/widget";
|
||||||
import sabnzbd from "./sabnzbd/widget";
|
import sabnzbd from "./sabnzbd/widget";
|
||||||
import scrutiny from "./scrutiny/widget";
|
import scrutiny from "./scrutiny/widget";
|
||||||
|
import seerr from "./seerr/widget";
|
||||||
import slskd from "./slskd/widget";
|
import slskd from "./slskd/widget";
|
||||||
import sonarr from "./sonarr/widget";
|
import sonarr from "./sonarr/widget";
|
||||||
import speedtest from "./speedtest/widget";
|
import speedtest from "./speedtest/widget";
|
||||||
@@ -202,7 +202,7 @@ const widgets = {
|
|||||||
jackett,
|
jackett,
|
||||||
jdownloader,
|
jdownloader,
|
||||||
jellyfin: emby,
|
jellyfin: emby,
|
||||||
jellyseerr,
|
jellyseerr: seerr,
|
||||||
jellystat,
|
jellystat,
|
||||||
kavita,
|
kavita,
|
||||||
komga,
|
komga,
|
||||||
@@ -262,6 +262,7 @@ const widgets = {
|
|||||||
rutorrent,
|
rutorrent,
|
||||||
sabnzbd,
|
sabnzbd,
|
||||||
scrutiny,
|
scrutiny,
|
||||||
|
seerr,
|
||||||
slskd,
|
slskd,
|
||||||
sonarr,
|
sonarr,
|
||||||
speedtest,
|
speedtest,
|
||||||
|
|||||||
Reference in New Issue
Block a user