Compare commits

..

25 Commits

Author SHA1 Message Date
Crowdin Bot
7c0dd26ea8 New Crowdin translations by GitHub Action 2025-12-03 12:15:39 +00:00
dependabot[bot]
d13165699b Chore(deps-dev): Bump prettier from 3.6.2 to 3.7.3 (#6033)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 22:26:24 +00:00
dependabot[bot]
65ff248ee7 Chore(deps): Bump systeminformation from 5.27.7 to 5.27.11 (#6032)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 21:28:58 +00:00
dependabot[bot]
87e5643892 Chore(deps): Bump raw-body from 3.0.1 to 3.0.2 (#6034)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 12:21:53 -08:00
shamoon
5b50e8ff81 Enhancement: handle gluetun port forwarded API change (#6011)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-25 13:28:50 -08:00
Romloader
c36c6a9012 Enhancement: support authentication for Frigate widget (#6006)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2025-11-25 11:34:54 -08:00
shamoon
cf990063b9 Add AI tools disclosure to PR template
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-23 23:27:05 -08:00
dependabot[bot]
610f1bd974 Chore(deps): Bump actions/checkout from 5 to 6 (#5998)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-23 08:03:17 -08:00
shamoon
4031178831 Enhancement: treat 'error' as custom api field when mapped (#5999)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-21 10:36:31 -08:00
shamoon
b65c8399d8 Handle raw number errors, I guess 2025-11-21 10:05:01 -08:00
Darkangeel_hd
6b63cfd491 Chore: change MySpeed blocks layout order (#5984)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-17 06:57:47 -08:00
shamoon
196c51bf73 Enhancement: support limit crowdsec alerts to 24h (#5981)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Co-authored-by: MountainGod2 <admin@reid.ca>
2025-11-16 16:38:55 -08:00
qmph22
17c9b2631e Enhancement: add net worth field for ghostfolio (#5958)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2025-11-13 00:31:55 +00:00
Diego Barreiro Perez
1a21189643 Enhancement: Allow Disabling Indexing (#5954)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2025-11-13 00:13:16 +00:00
shamoon
b6b428363c 1.7.0
Some checks failed
Crowdin Action / Crowdin Sync (push) Has been cancelled
Docs / Linting Checks (push) Has been cancelled
Docker CI / Linting Checks (push) Has been cancelled
Docs / Test Build Docs (push) Has been cancelled
Docs / Build & Deploy Docs (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Repository Maintenance / Stale (push) Has been cancelled
Repository Maintenance / Lock Old Threads (push) Has been cancelled
Repository Maintenance / Close Answered Discussions (push) Has been cancelled
Repository Maintenance / Close Outdated Discussions (push) Has been cancelled
Repository Maintenance / Close Unsupported Feature Requests (push) Has been cancelled
2025-11-11 09:03:26 -08:00
shamoon
e707fa46cf Revert "Development: specify pnpm version (#5364)"
This reverts commit 0c6c40dae7.
2025-11-11 09:03:15 -08:00
shamoon
3d040362cb Merge branch 'dev' 2025-11-11 09:00:14 -08:00
github-actions[bot]
57b193b037 New Crowdin translations by GitHub Action (#5953) 2025-11-11 08:59:39 -08:00
shamoon
8a75c9b6e3 Fixhancement: improve UID support (#5963) 2025-11-11 08:56:44 -08:00
Alessandro Travi
0dafc792f7 Documentation: note support for omada controller version 6 (#5961)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-10 23:07:28 -08:00
shamoon
afc0fe29ee Fix: enforce max field blocks for esp home widget (#5951)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-08 12:32:41 -08:00
shamoon
817a9bbce5 Clarify showSummary precedence in Komodo widget docs
Some checks failed
Crowdin Action / Crowdin Sync (push) Has been cancelled
Docker CI / Linting Checks (push) Has been cancelled
Docs / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Docs / Test Build Docs (push) Has been cancelled
Docs / Build & Deploy Docs (push) Has been cancelled
Repository Maintenance / Stale (push) Has been cancelled
Repository Maintenance / Lock Old Threads (push) Has been cancelled
Repository Maintenance / Close Answered Discussions (push) Has been cancelled
Repository Maintenance / Close Outdated Discussions (push) Has been cancelled
Repository Maintenance / Close Unsupported Feature Requests (push) Has been cancelled
2025-11-05 08:44:31 -08:00
dependabot[bot]
3ef7031eb0 Chore(deps): Bump docker/setup-qemu-action from 3.6.0 to 3.7.0 (#5939)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-05 16:41:24 +00:00
shamoon
6faf32eae9 Chore: better guard against empty data in komodo widget 2025-11-05 08:32:33 -08:00
shamoon
455e86571a Chore: improve event hash generation in iCal integration (#5938)
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-11-05 08:11:24 -08:00
75 changed files with 378 additions and 177 deletions

View File

@@ -38,3 +38,4 @@ What type of change does your PR introduce to Homepage?
- [ ] If applicable, I have reviewed the [feature / enhancement](https://gethomepage.dev/more/development/#new-feature-guidelines) and / or [service widget guidelines](https://gethomepage.dev/more/development/#service-widget-guidelines). - [ ] If applicable, I have reviewed the [feature / enhancement](https://gethomepage.dev/more/development/#new-feature-guidelines) and / or [service widget guidelines](https://gethomepage.dev/more/development/#service-widget-guidelines).
- [ ] I have checked that all code style checks pass using [pre-commit hooks](https://gethomepage.dev/more/development/#code-formatting-with-pre-commit-hooks) and [linting checks](https://gethomepage.dev/more/development/#code-linting). - [ ] I have checked that all code style checks pass using [pre-commit hooks](https://gethomepage.dev/more/development/#code-formatting-with-pre-commit-hooks) and [linting checks](https://gethomepage.dev/more/development/#code-linting).
- [ ] If applicable, I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers. - [ ] If applicable, I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
- [ ] In the description above I have disclosed the use of AI tools in the coding of this PR.

View File

@@ -17,7 +17,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: crowdin action - name: crowdin action
uses: crowdin/github-action@v2 uses: crowdin/github-action@v2
with: with:

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Install python - name: Install python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@@ -35,6 +35,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
version: 10
run_install: false run_install: false
- name: Setup Node.js - name: Setup Node.js
@@ -61,7 +62,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Extract Docker metadata - name: Extract Docker metadata
id: meta id: meta
@@ -93,6 +94,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
version: 10
run_install: false run_install: false
- name: Setup Node.js - name: Setup Node.js
@@ -127,7 +129,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@v3.6.0 uses: docker/setup-qemu-action@v3.7.0
- name: Setup Docker buildx - name: Setup Docker buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Install python - name: Install python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
@@ -32,7 +32,7 @@ jobs:
needs: needs:
- pre-commit - pre-commit
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
@@ -54,7 +54,7 @@ jobs:
needs: needs:
- pre-commit - pre-commit
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Configure Git Credentials - name: Configure Git Credentials
run: | run: |
git config user.name github-actions[bot] git config user.name github-actions[bot]

View File

@@ -51,6 +51,8 @@ COPY --link --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static
RUN apk add --no-cache su-exec iputils-ping shadow RUN apk add --no-cache su-exec iputils-ping shadow
USER root
ENV NODE_ENV=production ENV NODE_ENV=production
ENV HOSTNAME=0.0.0.0 ENV HOSTNAME=0.0.0.0
ENV PORT=3000 ENV PORT=3000

View File

@@ -57,8 +57,8 @@ if [ -d /app/.next ]; then
fi fi
# Drop privileges (when asked to) if root, otherwise run as current user # Drop privileges (when asked to) if root, otherwise run as current user
if [ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ]; then if [ "$(id -u)" = "0" ] && [ "${PUID}" != "0" ]; then
su-exec ${PUID}:${PGID} "$@" exec su-exec ${PUID}:${PGID} "$@"
else else
exec "$@" exec "$@"
fi fi

View File

@@ -571,3 +571,18 @@ or per service widget (`services.yaml`) with:
``` ```
If either value is set to true, the error message will be hidden. If either value is set to true, the error message will be hidden.
## Disable Search Engine Indexing
You can request that search engines not to index your Homepage instance by enabling the `disableIndexing` setting.
```yaml
disableIndexing: true
```
When enabled, this will:
- Disallow all crawlers in `robots.txt`
- Add `<meta name="robots" content="noindex, nofollow">` tags to prevent indexing
By default this feature is disabled.

View File

@@ -8,6 +8,9 @@ Learn more about [Crowdsec](https://crowdsec.net).
See the [crowdsec docs](https://docs.crowdsec.net/docs/local_api/intro/#machines) for information about registering a machine, See the [crowdsec docs](https://docs.crowdsec.net/docs/local_api/intro/#machines) for information about registering a machine,
in most instances you can use the default credentials (`/etc/crowdsec/local_api_credentials.yaml`). in most instances you can use the default credentials (`/etc/crowdsec/local_api_credentials.yaml`).
!!! note
Without the `limit24h` option, the widget will fetch all alerts which is limited to 100 by the API to avoid performance issues.
Allowed fields: `["alerts", "bans"]`. Allowed fields: `["alerts", "bans"]`.
```yaml ```yaml
@@ -16,4 +19,5 @@ widget:
url: http://crowdsechostorip:port url: http://crowdsechostorip:port
username: localhost # machine_id in crowdsec username: localhost # machine_id in crowdsec
password: password password: password
limit24h: true # optional, limits alerts to last 24h. Default: false
``` ```

View File

@@ -14,4 +14,6 @@ widget:
type: frigate type: frigate
url: http://frigate.host.or.ip:port url: http://frigate.host.or.ip:port
enableRecentEvents: true # Optional, defaults to false enableRecentEvents: true # Optional, defaults to false
username: username # optional
password: password # optional
``` ```

View File

@@ -15,7 +15,7 @@ See the [official docs](https://github.com/ghostfolio/ghostfolio#authorization-b
_Note that the Bearer token is valid for 6 months, after which a new one must be generated._ _Note that the Bearer token is valid for 6 months, after which a new one must be generated._
Allowed fields: `["gross_percent_today", "gross_percent_1y", "gross_percent_max"]` Allowed fields: `["gross_percent_today", "gross_percent_1y", "gross_percent_max", "net_worth"]`
```yaml ```yaml
widget: widget:

View File

@@ -12,11 +12,17 @@ Learn more about [Gluetun](https://github.com/qdm12/gluetun).
Allowed fields: `["public_ip", "region", "country", "port_forwarded"]`. Allowed fields: `["public_ip", "region", "country", "port_forwarded"]`.
Default fields: `["public_ip", "region", "country"]`. Default fields: `["public_ip", "region", "country"]`.
To setup authentication, follow [the official Gluetun documentation](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication). Note that to use the api key method, you must add the route `GET /v1/publicip/ip` to the `routes` array in your Gluetun config.toml. Similarly, if you want to include the `port_forwarded` field, you must add the route `GET /v1/openvpn/portforwarded` to your Gluetun config.toml. To setup authentication, follow [the official Gluetun documentation](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication). Note that to use the api key method, you must add the route `GET /v1/publicip/ip` to the `routes` array in your Gluetun config.toml. Similarly, if you want to include the `port_forwarded` field, you must add the route `GET /v1/openvpn/portforwarded` (or `/v1/portforward`) to your Gluetun config.toml.
| Gluetun Version | Homepage Widget Version |
| --------------- | ----------------------- |
| < 3.40.1 | 1 (default) |
| >= 3.40.1 | 2 |
```yaml ```yaml
widget: widget:
type: gluetun type: gluetun
url: http://gluetun.host.or.ip:port url: http://gluetun.host.or.ip:port
key: gluetunkey # Not required if /v1/publicip/ip endpoint is configured with `auth = none` key: gluetunkey # Not required if /v1/publicip/ip endpoint is configured with `auth = none`
version: 2 # optional, default is 1
``` ```

View File

@@ -17,6 +17,6 @@ widget:
url: http://komodo.hostname.or.ip:port url: http://komodo.hostname.or.ip:port
key: K-xxxxxx... key: K-xxxxxx...
secret: S-xxxxxx... secret: S-xxxxxx...
showSummary: true # optional, default: false showSummary: true # optional, default: false. Takes precedence over showStacks
showStacks: true # optional, default: false showStacks: true # optional, default: false
``` ```

View File

@@ -3,7 +3,7 @@ title: Omada
description: Omada Widget Configuration description: Omada Widget Configuration
--- ---
The widget supports controller versions 3, 4 and 5. The widget supports controller versions 3, 4, 5 and 6.
Allowed fields: `["connectedAp", "activeUser", "alerts", "connectedGateways", "connectedSwitches"]`. Allowed fields: `["connectedAp", "activeUser", "alerts", "connectedGateways", "connectedSwitches"]`.

View File

@@ -1,6 +1,6 @@
{ {
"name": "homepage", "name": "homepage",
"version": "1.6.1", "version": "1.7.0",
"private": true, "private": true,
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
@@ -29,14 +29,14 @@
"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",
"raw-body": "^3.0.1", "raw-body": "^3.0.2",
"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.4.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.7", "systeminformation": "^5.27.11",
"tough-cookie": "^6.0.0", "tough-cookie": "^6.0.0",
"urbackup-server-api": "^0.8.9", "urbackup-server-api": "^0.8.9",
"winston": "^3.17.0", "winston": "^3.17.0",
@@ -54,7 +54,7 @@
"eslint-plugin-react": "^7.37.4", "eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.7.3",
"prettier-plugin-organize-imports": "^4.3.0", "prettier-plugin-organize-imports": "^4.3.0",
"tailwind-scrollbar": "^4.0.2", "tailwind-scrollbar": "^4.0.2",
"tailwindcss": "^4.0.9", "tailwindcss": "^4.0.9",
@@ -63,13 +63,6 @@
"optionalDependencies": { "optionalDependencies": {
"osx-temperature-sensor": "^1.0.8" "osx-temperature-sensor": "^1.0.8"
}, },
"packageManager": "pnpm@10.8.1",
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": "10.8.1"
}
},
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"osx-temperature-sensor", "osx-temperature-sensor",

58
pnpm-lock.yaml generated
View File

@@ -63,8 +63,8 @@ importers:
specifier: ^7.1.0 specifier: ^7.1.0
version: 7.1.0 version: 7.1.0
raw-body: raw-body:
specifier: ^3.0.1 specifier: ^3.0.2
version: 3.0.1 version: 3.0.2
react: react:
specifier: ^18.3.1 specifier: ^18.3.1
version: 18.3.1 version: 18.3.1
@@ -84,8 +84,8 @@ importers:
specifier: ^2.3.3 specifier: ^2.3.3
version: 2.3.3(react@18.3.1) version: 2.3.3(react@18.3.1)
systeminformation: systeminformation:
specifier: ^5.27.7 specifier: ^5.27.11
version: 5.27.7 version: 5.27.11
tough-cookie: tough-cookie:
specifier: ^6.0.0 specifier: ^6.0.0
version: 6.0.0 version: 6.0.0
@@ -122,7 +122,7 @@ importers:
version: 6.10.2(eslint@9.25.1(jiti@2.6.1)) version: 6.10.2(eslint@9.25.1(jiti@2.6.1))
eslint-plugin-prettier: eslint-plugin-prettier:
specifier: ^5.5.4 specifier: ^5.5.4
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.25.1(jiti@2.6.1)))(eslint@9.25.1(jiti@2.6.1))(prettier@3.6.2) version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.25.1(jiti@2.6.1)))(eslint@9.25.1(jiti@2.6.1))(prettier@3.7.3)
eslint-plugin-react: eslint-plugin-react:
specifier: ^7.37.4 specifier: ^7.37.4
version: 7.37.4(eslint@9.25.1(jiti@2.6.1)) version: 7.37.4(eslint@9.25.1(jiti@2.6.1))
@@ -133,11 +133,11 @@ importers:
specifier: ^8.5.6 specifier: ^8.5.6
version: 8.5.6 version: 8.5.6
prettier: prettier:
specifier: ^3.6.2 specifier: ^3.7.3
version: 3.6.2 version: 3.7.3
prettier-plugin-organize-imports: prettier-plugin-organize-imports:
specifier: ^4.3.0 specifier: ^4.3.0
version: 4.3.0(prettier@3.6.2)(typescript@5.7.3) version: 4.3.0(prettier@3.7.3)(typescript@5.7.3)
tailwind-scrollbar: tailwind-scrollbar:
specifier: ^4.0.2 specifier: ^4.0.2
version: 4.0.2(react@18.3.1)(tailwindcss@4.0.9) version: 4.0.2(react@18.3.1)(tailwindcss@4.0.9)
@@ -1749,8 +1749,8 @@ packages:
http-cache-semantics@4.2.0: http-cache-semantics@4.2.0:
resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==}
http-errors@2.0.0: http-errors@2.0.1:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
http2-wrapper@2.2.1: http2-wrapper@2.2.1:
@@ -2371,8 +2371,8 @@ packages:
vue-tsc: vue-tsc:
optional: true optional: true
prettier@3.6.2: prettier@3.7.3:
resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} resolution: {integrity: sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@@ -2409,8 +2409,8 @@ packages:
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
engines: {node: '>=10'} engines: {node: '>=10'}
raw-body@3.0.1: raw-body@3.0.2:
resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
react-dom@18.3.1: react-dom@18.3.1:
@@ -2669,8 +2669,8 @@ packages:
stack-trace@0.0.10: stack-trace@0.0.10:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
statuses@2.0.1: statuses@2.0.2:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
stop-iteration-iterator@1.1.0: stop-iteration-iterator@1.1.0:
@@ -2776,8 +2776,8 @@ packages:
resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
systeminformation@5.27.7: systeminformation@5.27.11:
resolution: {integrity: sha512-saaqOoVEEFaux4v0K8Q7caiauRwjXC4XbD2eH60dxHXbpKxQ8kH9Rf7Jh+nryKpOUSEFxtCdBlSUx0/lO6rwRg==} resolution: {integrity: sha512-K3Lto/2m3K2twmKHdgx5B+0in9qhXK4YnoT9rIlgwN/4v7OV5c8IjbeAUkuky/6VzCQC7iKCAqi8rZathCdjHg==}
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
hasBin: true hasBin: true
@@ -4506,10 +4506,10 @@ snapshots:
safe-regex-test: 1.1.0 safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1 string.prototype.includes: 2.0.1
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.25.1(jiti@2.6.1)))(eslint@9.25.1(jiti@2.6.1))(prettier@3.6.2): eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.25.1(jiti@2.6.1)))(eslint@9.25.1(jiti@2.6.1))(prettier@3.7.3):
dependencies: dependencies:
eslint: 9.25.1(jiti@2.6.1) eslint: 9.25.1(jiti@2.6.1)
prettier: 3.6.2 prettier: 3.7.3
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
synckit: 0.11.11 synckit: 0.11.11
optionalDependencies: optionalDependencies:
@@ -4850,12 +4850,12 @@ snapshots:
http-cache-semantics@4.2.0: {} http-cache-semantics@4.2.0: {}
http-errors@2.0.0: http-errors@2.0.1:
dependencies: dependencies:
depd: 2.0.0 depd: 2.0.0
inherits: 2.0.4 inherits: 2.0.4
setprototypeof: 1.2.0 setprototypeof: 1.2.0
statuses: 2.0.1 statuses: 2.0.2
toidentifier: 1.0.1 toidentifier: 1.0.1
http2-wrapper@2.2.1: http2-wrapper@2.2.1:
@@ -5416,12 +5416,12 @@ snapshots:
dependencies: dependencies:
fast-diff: 1.3.0 fast-diff: 1.3.0
prettier-plugin-organize-imports@4.3.0(prettier@3.6.2)(typescript@5.7.3): prettier-plugin-organize-imports@4.3.0(prettier@3.7.3)(typescript@5.7.3):
dependencies: dependencies:
prettier: 3.6.2 prettier: 3.7.3
typescript: 5.7.3 typescript: 5.7.3
prettier@3.6.2: {} prettier@3.7.3: {}
pretty-bytes@7.1.0: {} pretty-bytes@7.1.0: {}
@@ -5465,10 +5465,10 @@ snapshots:
quick-lru@5.1.1: {} quick-lru@5.1.1: {}
raw-body@3.0.1: raw-body@3.0.2:
dependencies: dependencies:
bytes: 3.1.2 bytes: 3.1.2
http-errors: 2.0.0 http-errors: 2.0.1
iconv-lite: 0.7.0 iconv-lite: 0.7.0
unpipe: 1.0.0 unpipe: 1.0.0
@@ -5780,7 +5780,7 @@ snapshots:
stack-trace@0.0.10: {} stack-trace@0.0.10: {}
statuses@2.0.1: {} statuses@2.0.2: {}
stop-iteration-iterator@1.1.0: stop-iteration-iterator@1.1.0:
dependencies: dependencies:
@@ -5905,7 +5905,7 @@ snapshots:
dependencies: dependencies:
'@pkgr/core': 0.2.9 '@pkgr/core': 0.2.9
systeminformation@5.27.7: {} systeminformation@5.27.11: {}
tabbable@6.3.0: {} tabbable@6.3.0: {}

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Vandag", "gross_percent_today": "Vandag",
"gross_percent_1y": "Een jaar", "gross_percent_1y": "Een jaar",
"gross_percent_max": "Alle tyd" "gross_percent_max": "Alle tyd",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podsendinge", "podcasts": "Podsendinge",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "سنة", "gross_percent_1y": "سنة",
"gross_percent_max": "كل الوقت" "gross_percent_max": "كل الوقت",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "بودكاست", "podcasts": "بودكاست",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Една година", "gross_percent_1y": "Една година",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Подкасти", "podcasts": "Подкасти",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Un any", "gross_percent_1y": "Un any",
"gross_percent_max": "Sempre" "gross_percent_max": "Sempre",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Pòdcasts", "podcasts": "Pòdcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Jeden rok", "gross_percent_1y": "Jeden rok",
"gross_percent_max": "Za celou dobu" "gross_percent_max": "Za celou dobu",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasty", "podcasts": "Podcasty",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Et År", "gross_percent_1y": "Et År",
"gross_percent_max": "Altid" "gross_percent_max": "Altid",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Heute", "gross_percent_today": "Heute",
"gross_percent_1y": "Ein Jahr", "gross_percent_1y": "Ein Jahr",
"gross_percent_max": "Gesamt" "gross_percent_max": "Gesamt",
"net_worth": ""
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Ένας χρόνος", "gross_percent_1y": "Ένας χρόνος",
"gross_percent_max": "Διαχρονικά" "gross_percent_max": "Διαχρονικά",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Hoy", "gross_percent_today": "Hoy",
"gross_percent_1y": "Un año", "gross_percent_1y": "Un año",
"gross_percent_max": "Todo el tiempo" "gross_percent_max": "Todo el tiempo",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -142,8 +142,8 @@
"connectionStatusDisconnected": "Déconnecté", "connectionStatusDisconnected": "Déconnecté",
"connectionStatusConnected": "Connecté", "connectionStatusConnected": "Connecté",
"uptime": "Démarré depuis", "uptime": "Démarré depuis",
"maxDown": "Réception max.", "maxDown": "Réception max",
"maxUp": "Envoi max.", "maxUp": "Envoi max",
"down": "Réception", "down": "Réception",
"up": "Envoi", "up": "Envoi",
"received": "Reçu", "received": "Reçu",
@@ -229,7 +229,7 @@
"seed": "En partage" "seed": "En partage"
}, },
"develancacheui": { "develancacheui": {
"cachehitbytes": "Cache Hit (B)", "cachehitbytes": "Octets acquis du cache",
"cachemissbytes": "Cache Miss (B)" "cachemissbytes": "Cache Miss (B)"
}, },
"downloadstation": { "downloadstation": {
@@ -294,7 +294,7 @@
"queries": "Requêtes", "queries": "Requêtes",
"blocked": "Bloqué", "blocked": "Bloqué",
"blocked_percent": "% bloqué", "blocked_percent": "% bloqué",
"gravity": "Listes dom. bloqués" "gravity": "Listes dom. Bloqués"
}, },
"adguard": { "adguard": {
"queries": "Requêtes", "queries": "Requêtes",
@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Aujourd'hui", "gross_percent_today": "Aujourd'hui",
"gross_percent_1y": "Un an", "gross_percent_1y": "Un an",
"gross_percent_max": "Depuis le début" "gross_percent_max": "Depuis le début",
"net_worth": "Patrimoine net"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",
@@ -1091,7 +1092,7 @@
"NEW_ARRAY": "Nouveau tableau", "NEW_ARRAY": "Nouveau tableau",
"RECON_DISK": "Reconstruction du disque", "RECON_DISK": "Reconstruction du disque",
"DISABLE_DISK": "Disque désactivé", "DISABLE_DISK": "Disque désactivé",
"SWAP_DSBL": "Swap Disable", "SWAP_DSBL": "Désactiver le swap",
"INVALID_EXPANSION": "Extension invalide", "INVALID_EXPANSION": "Extension invalide",
"PARITY_NOT_BIGGEST": "La parité n'est pas la plus grande", "PARITY_NOT_BIGGEST": "La parité n'est pas la plus grande",
"TOO_MANY_MISSING_DISKS": "Trop de disques manquants", "TOO_MANY_MISSING_DISKS": "Trop de disques manquants",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "היום", "gross_percent_today": "היום",
"gross_percent_1y": "שנה", "gross_percent_1y": "שנה",
"gross_percent_max": "כל הזמן" "gross_percent_max": "כל הזמן",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "פודקאסטים", "podcasts": "פודקאסטים",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Danas", "gross_percent_today": "Danas",
"gross_percent_1y": "Jedna godina", "gross_percent_1y": "Jedna godina",
"gross_percent_max": "Svo vrijeme" "gross_percent_max": "Svo vrijeme",
"net_worth": "Neto vrijednost"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasti", "podcasts": "Podcasti",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Egy év", "gross_percent_1y": "Egy év",
"gross_percent_max": "Mindig" "gross_percent_max": "Mindig",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcast", "podcasts": "Podcast",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Satu Tahun", "gross_percent_1y": "Satu Tahun",
"gross_percent_max": "Sepanjang Masa" "gross_percent_max": "Sepanjang Masa",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcast", "podcasts": "Podcast",

View File

@@ -168,7 +168,7 @@
"passes": "Tessere" "passes": "Tessere"
}, },
"tautulli": { "tautulli": {
"playing": "Playing", "playing": "In riproduzione",
"transcoding": "Transcoding", "transcoding": "Transcoding",
"bitrate": "Bitrate", "bitrate": "Bitrate",
"no_active": "No Active Streams", "no_active": "No Active Streams",
@@ -313,7 +313,7 @@
"total": "Total" "total": "Total"
}, },
"suwayomi": { "suwayomi": {
"download": "Downloaded", "download": "Scaricati",
"nondownload": "Non Scaricato", "nondownload": "Non Scaricato",
"read": "Read", "read": "Read",
"unread": "Unread", "unread": "Unread",
@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Un anno", "gross_percent_1y": "Un anno",
"gross_percent_max": "Sempre" "gross_percent_max": "Sempre",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcast", "podcasts": "Podcast",

View File

@@ -69,7 +69,7 @@
"docker": { "docker": {
"rx": "受信済み", "rx": "受信済み",
"tx": "送信済み", "tx": "送信済み",
"mem": "MEM", "mem": "メモリ",
"cpu": "CPU", "cpu": "CPU",
"running": "起動中", "running": "起動中",
"offline": "オフライン", "offline": "オフライン",
@@ -83,7 +83,7 @@
"partial": "部分的" "partial": "部分的"
}, },
"ping": { "ping": {
"error": "Error", "error": "エラー",
"ping": "Ping", "ping": "Ping",
"down": "下へ", "down": "下へ",
"up": "稼働", "up": "稼働",
@@ -112,7 +112,7 @@
"offline_alt": "オフライン", "offline_alt": "オフライン",
"online": "オンライン", "online": "オンライン",
"total": "Total", "total": "Total",
"unknown": "Unknown" "unknown": "不明"
}, },
"evcc": { "evcc": {
"pv_power": "発電量", "pv_power": "発電量",
@@ -223,8 +223,8 @@
"invalid": "無効" "invalid": "無効"
}, },
"deluge": { "deluge": {
"download": "Download", "download": "ダウンロード",
"upload": "Upload", "upload": "アップロード",
"leech": "Leech", "leech": "Leech",
"seed": "Seed" "seed": "Seed"
}, },
@@ -233,8 +233,8 @@
"cachemissbytes": "キャッシュミスバイト" "cachemissbytes": "キャッシュミスバイト"
}, },
"downloadstation": { "downloadstation": {
"download": "Download", "download": "ダウンロード",
"upload": "Upload", "upload": "アップロード",
"leech": "Leech", "leech": "Leech",
"seed": "Seed" "seed": "Seed"
}, },
@@ -251,7 +251,7 @@
"queued": "Queued", "queued": "Queued",
"movies": "Movies", "movies": "Movies",
"queue": "Queue", "queue": "Queue",
"unknown": "Unknown" "unknown": "不明"
}, },
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
@@ -692,8 +692,8 @@
}, },
"diskstation": { "diskstation": {
"days": "Days", "days": "Days",
"uptime": "Uptime", "uptime": "稼働時間",
"volumeAvailable": "Available" "volumeAvailable": "利用可能"
}, },
"mylar": { "mylar": {
"series": "Series", "series": "Series",
@@ -754,12 +754,13 @@
"gatus": { "gatus": {
"up": "Sites Up", "up": "Sites Up",
"down": "Sites Down", "down": "Sites Down",
"uptime": "Uptime" "uptime": "稼働時間"
}, },
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "1年", "gross_percent_1y": "1年",
"gross_percent_max": "全期間" "gross_percent_max": "全期間",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "ポッドキャスト", "podcasts": "ポッドキャスト",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "오늘", "gross_percent_today": "오늘",
"gross_percent_1y": "1년", "gross_percent_1y": "1년",
"gross_percent_max": "전체 기간" "gross_percent_max": "전체 기간",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "팟캐스트", "podcasts": "팟캐스트",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Satu tahun", "gross_percent_1y": "Satu tahun",
"gross_percent_max": "Sepanjang masa" "gross_percent_max": "Sepanjang masa",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podkas", "podcasts": "Podkas",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Vandaag", "gross_percent_today": "Vandaag",
"gross_percent_1y": "Een jaar", "gross_percent_1y": "Een jaar",
"gross_percent_max": "Altijd" "gross_percent_max": "Altijd",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Ett år", "gross_percent_1y": "Ett år",
"gross_percent_max": "Gjennom tidene" "gross_percent_max": "Gjennom tidene",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podkaster", "podcasts": "Podkaster",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Dzisiaj", "gross_percent_today": "Dzisiaj",
"gross_percent_1y": "Rok", "gross_percent_1y": "Rok",
"gross_percent_max": "Od początku" "gross_percent_max": "Od początku",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasty", "podcasts": "Podcasty",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Um ano", "gross_percent_1y": "Um ano",
"gross_percent_max": "Desde Sempre" "gross_percent_max": "Desde Sempre",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Hoje", "gross_percent_today": "Hoje",
"gross_percent_1y": "Um ano", "gross_percent_1y": "Um ano",
"gross_percent_max": "Todo o tempo" "gross_percent_max": "Todo o tempo",
"net_worth": "Patrimônio Líquido"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Un an", "gross_percent_1y": "Un an",
"gross_percent_max": "Tot timpul" "gross_percent_max": "Tot timpul",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasturi", "podcasts": "Podcasturi",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Сегодня", "gross_percent_today": "Сегодня",
"gross_percent_1y": "Один год", "gross_percent_1y": "Один год",
"gross_percent_max": "Все время" "gross_percent_max": "Все время",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Подкасты", "podcasts": "Подкасты",

View File

@@ -362,8 +362,8 @@
}, },
"trilium": { "trilium": {
"version": "Verzia", "version": "Verzia",
"notesCount": "Notes", "notesCount": "Poznámky",
"dbSize": "Database Size", "dbSize": "Veľkosť databázy",
"unknown": "Neznáme" "unknown": "Neznáme"
}, },
"navidrome": { "navidrome": {
@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Dnes", "gross_percent_today": "Dnes",
"gross_percent_1y": "Jeden rok", "gross_percent_1y": "Jeden rok",
"gross_percent_max": "Za celý čas" "gross_percent_max": "Za celý čas",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasty", "podcasts": "Podcasty",
@@ -786,7 +787,7 @@
"downloadCount": "Poradie", "downloadCount": "Poradie",
"downloadBytesRemaining": "Zostávajúce", "downloadBytesRemaining": "Zostávajúce",
"downloadTotalBytes": "Veľkosť", "downloadTotalBytes": "Veľkosť",
"downloadSpeed": "Speed" "downloadSpeed": "Rýchlosť"
}, },
"kavita": { "kavita": {
"seriesCount": "Series", "seriesCount": "Series",
@@ -952,7 +953,7 @@
"loading": "Načítava sa", "loading": "Načítava sa",
"open": "Open - US Market", "open": "Open - US Market",
"closed": "Closed - US Market", "closed": "Closed - US Market",
"invalidConfiguration": "Invalid Configuration" "invalidConfiguration": "Neplatná konfigurácia"
}, },
"frigate": { "frigate": {
"cameras": "Kamery", "cameras": "Kamery",
@@ -1022,10 +1023,10 @@
"loading": "Načítava sa" "loading": "Načítava sa"
}, },
"gitlab": { "gitlab": {
"groups": "Groups", "groups": "Skupiny",
"issues": "Issues", "issues": "Problémy",
"merges": "Merge Requests", "merges": "Merge Requests",
"projects": "Projects" "projects": "Projekty"
}, },
"apcups": { "apcups": {
"status": "Stav", "status": "Stav",
@@ -1035,7 +1036,7 @@
}, },
"karakeep": { "karakeep": {
"bookmarks": "Bookmarks", "bookmarks": "Bookmarks",
"favorites": "Favorites", "favorites": "Obľúbené",
"archived": "Archived", "archived": "Archived",
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Zoznamy", "lists": "Zoznamy",
@@ -1065,13 +1066,13 @@
"komodo": { "komodo": {
"total": "Celkom", "total": "Celkom",
"running": "Beží", "running": "Beží",
"stopped": "Stopped", "stopped": "Zastavené",
"down": "Down", "down": "Down",
"unhealthy": "Nezdravý", "unhealthy": "Nezdravý",
"unknown": "Neznáme", "unknown": "Neznáme",
"servers": "Servers", "servers": "Servery",
"stacks": "Stacks", "stacks": "Stacks",
"containers": "Containers" "containers": "Kontajnery"
}, },
"filebrowser": { "filebrowser": {
"available": "Dostupné", "available": "Dostupné",
@@ -1080,8 +1081,8 @@
}, },
"wallos": { "wallos": {
"activeSubscriptions": "Subscriptions", "activeSubscriptions": "Subscriptions",
"thisMonthlyCost": "This Month", "thisMonthlyCost": "Tento mesiac",
"nextMonthlyCost": "Next Month", "nextMonthlyCost": "Ďalší mesiac",
"previousMonthlyCost": "Prev. Month", "previousMonthlyCost": "Prev. Month",
"nextRenewingSubscription": "Next Payment" "nextRenewingSubscription": "Next Payment"
}, },

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Eno leto", "gross_percent_1y": "Eno leto",
"gross_percent_max": "Celoten čas" "gross_percent_max": "Celoten čas",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasti", "podcasts": "Podcasti",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Данас", "gross_percent_today": "Данас",
"gross_percent_1y": "Једна година", "gross_percent_1y": "Једна година",
"gross_percent_max": "Све време" "gross_percent_max": "Све време",
"net_worth": "Нето вредност"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Подкасти", "podcasts": "Подкасти",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Bugün", "gross_percent_today": "Bugün",
"gross_percent_1y": "Bir yıl", "gross_percent_1y": "Bir yıl",
"gross_percent_max": "Tüm zaman" "gross_percent_max": "Tüm zaman",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcast", "podcasts": "Podcast",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "Один рік", "gross_percent_1y": "Один рік",
"gross_percent_max": "Весь час" "gross_percent_max": "Весь час",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Подкасти", "podcasts": "Подкасти",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "One year", "gross_percent_1y": "One year",
"gross_percent_max": "All time" "gross_percent_max": "All time",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "一年", "gross_percent_1y": "一年",
"gross_percent_max": "所有時間" "gross_percent_max": "所有時間",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "播客", "podcasts": "播客",

View File

@@ -200,10 +200,10 @@
"rutorrent": { "rutorrent": {
"active": "活动中", "active": "活动中",
"upload": "Upload", "upload": "Upload",
"download": "Download" "download": "下载"
}, },
"transmission": { "transmission": {
"download": "Download", "download": "下载",
"upload": "", "upload": "",
"leech": "Leech", "leech": "Leech",
"seed": "Seed" "seed": "Seed"
@@ -223,8 +223,8 @@
"invalid": "Invalid" "invalid": "Invalid"
}, },
"deluge": { "deluge": {
"download": "Download", "download": "下载",
"upload": "Upload", "upload": "上传",
"leech": "Leech", "leech": "Leech",
"seed": "Seed" "seed": "Seed"
}, },
@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "一年", "gross_percent_1y": "一年",
"gross_percent_max": "所有时间" "gross_percent_max": "所有时间",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "播客", "podcasts": "播客",

View File

@@ -759,7 +759,8 @@
"ghostfolio": { "ghostfolio": {
"gross_percent_today": "Today", "gross_percent_today": "Today",
"gross_percent_1y": "一年", "gross_percent_1y": "一年",
"gross_percent_max": "所有時間" "gross_percent_max": "所有時間",
"net_worth": "Net Worth"
}, },
"audiobookshelf": { "audiobookshelf": {
"podcasts": "Podcasts", "podcasts": "Podcasts",

View File

@@ -14,6 +14,8 @@ export default function Error({ error }) {
if (typeof error === "string") { if (typeof error === "string") {
error = { message: error }; // eslint-disable-line no-param-reassign error = { message: error }; // eslint-disable-line no-param-reassign
} else if (typeof error === "number") {
error = { message: `Error ${error}` }; // eslint-disable-line no-param-reassign
} }
if (error?.data?.error) { if (error?.data?.error) {

View File

@@ -400,6 +400,7 @@ function Home({ initialSettings }) {
"A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations." "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations."
} }
/> />
{settings.disableIndexing && <meta name="robots" content="noindex, nofollow" />}
{settings.base && <base href={settings.base} />} {settings.base && <base href={settings.base} />}
{settings.favicon ? ( {settings.favicon ? (
<> <>

19
src/pages/robots.txt.js Normal file
View File

@@ -0,0 +1,19 @@
import { getSettings } from "utils/config/config";
export async function getServerSideProps({ res }) {
const settings = getSettings();
const content = ["User-agent: *", !!settings.disableIndexing ? "Disallow: /" : "Allow: /"].join("\n");
res.setHeader("Content-Type", "text/plain");
res.write(content);
res.end();
return {
props: {},
};
}
export default function RobotsTxt() {
// placeholder component
return null;
}

View File

@@ -279,6 +279,9 @@ export function cleanServiceGroups(groups) {
slugs, slugs,
symbols, symbols,
// crowdsec
limit24h,
// customapi // customapi
mappings, mappings,
display, display,
@@ -473,6 +476,10 @@ export function cleanServiceGroups(groups) {
if (defaultinterval) widget.defaultinterval = defaultinterval; if (defaultinterval) widget.defaultinterval = defaultinterval;
} }
if (limit24h !== undefined) {
widget.limit24h = !!limit24h;
}
if (type === "docker") { if (type === "docker") {
if (server) widget.server = server; if (server) widget.server = server;
if (container) widget.container = container; if (container) widget.container = container;
@@ -556,6 +563,7 @@ export function cleanServiceGroups(groups) {
"speedtest", "speedtest",
"wgeasy", "wgeasy",
"grafana", "grafana",
"gluetun",
].includes(type) ].includes(type)
) { ) {
if (version) widget.version = parseInt(version, 10); if (version) widget.version = parseInt(version, 10);

View File

@@ -106,13 +106,19 @@ export default function Integration({ config, params, setEvents, hideErrors, tim
}; };
const eventsToAdd = []; const eventsToAdd = [];
events.forEach((event, index) => { events.forEach((event) => {
const occurrences = getOcurrencesFromRange(event); const occurrences = getOcurrencesFromRange(event);
occurrences.forEach((icalDate) => { occurrences.forEach((icalDate) => {
const date = icalDate.toJSDate(); const date = icalDate.toJSDate();
const hash = simpleHash(`${event.id}-${event.title}-${index}-${date.toString()}`); const occurrenceTimestamp = date.getTime();
const eventIdentifier =
event.id ??
simpleHash(
`${event.title ?? ""}-${event.type ?? ""}-${event.status ?? ""}-${event.url ?? ""}-${event.location ?? ""}`,
);
const hash = simpleHash(`${eventIdentifier}-${occurrenceTimestamp}`);
let title = event.title; let title = event.title;
if (showName) { if (showName) {

View File

@@ -9,7 +9,7 @@ export default function Component({ service }) {
const { widget } = service; const { widget } = service;
const { data: alerts, error: alertsError } = useWidgetAPI(widget, "alerts"); const { data: alerts, error: alertsError } = useWidgetAPI(widget, !!widget.limit24h ? "alerts24h" : "alerts");
const { data: bans, error: bansError } = useWidgetAPI(widget, "bans"); const { data: bans, error: bansError } = useWidgetAPI(widget, "bans");
if (alertsError || bansError) { if (alertsError || bansError) {

View File

@@ -9,6 +9,9 @@ const widget = {
alerts: { alerts: {
endpoint: "alerts", endpoint: "alerts",
}, },
alerts24h: {
endpoint: "alerts?limit=0&since=24h",
},
bans: { bans: {
endpoint: "alerts?decision_type=ban&origin=crowdsec&has_active_decision=1", endpoint: "alerts?decision_type=ban&origin=crowdsec&has_active_decision=1",
}, },

View File

@@ -166,7 +166,11 @@ export default function Component({ service }) {
refreshInterval: Math.max(1000, refreshInterval), refreshInterval: Math.max(1000, refreshInterval),
}); });
if (customError) { // if mappings includes an error field and the data contains an error field then show data even if there is an error
const mappingsIncludesError = Array.isArray(mappings) && mappings.find((mapping) => mapping.field === "error");
const errorIsData = customData && typeof customData === "object" && "error" in customData;
if (customError && !(mappingsIncludesError && errorIsData)) {
return <Container service={service} error={customError} />; return <Container service={service} error={customError} />;
} }

View File

@@ -14,6 +14,12 @@ export default function Component({ service }) {
return <Container service={service} error={resultError} />; return <Container service={service} error={resultError} />;
} }
if (!widget.fields || widget.fields.length === 0) {
widget.fields = ["online", "offline", "offline_alt", "total"];
} else if (widget.fields.length > 4) {
widget.fields = widget.fields.slice(0, 4);
}
if (!resultData) { if (!resultData) {
return ( return (
<Container service={service}> <Container service={service}>

View File

@@ -0,0 +1,95 @@
import getServiceWidget from "utils/config/service-helpers";
import createLogger from "utils/logger";
import { asJson, formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers";
import { addCookieToJar } from "utils/proxy/cookie-jar";
import { httpProxy } from "utils/proxy/http";
import widgets from "widgets/widgets";
const proxyName = "frigateProxyHandler";
const logger = createLogger(proxyName);
export default async function frigateProxyHandler(req, res, map) {
const { group, service, endpoint, index } = req.query;
if (group && service) {
const widget = await getServiceWidget(group, service, index);
if (!widgets?.[widget.type]?.api) {
return res.status(403).json({ error: "Service does not support API calls" });
}
if (widget) {
const url = formatApiCall(widgets[widget.type].api, { endpoint, ...widget });
const params = {
method: "GET",
headers: {
"Content-Type": "application/json",
},
};
let [status, , data] = await httpProxy(url, params);
if (status === 401 && widget.username && widget.password) {
const loginUrl = `${widget.url}/api/login`;
logger.debug("Attempting login to Frigate at %s", loginUrl);
const [loginStatus, , , loginResponseHeaders] = await httpProxy(loginUrl, {
method: "POST",
body: JSON.stringify({ user: widget.username, password: widget.password }),
headers: {
"Content-Type": "application/json",
},
});
if (loginStatus !== 200) {
logger.error("HTTP Error %d calling %s", loginStatus, sanitizeErrorURL(loginUrl));
return res.status(status).json({
error: {
message: `HTTP Error ${status} while trying to login to Frigate`,
url: sanitizeErrorURL(url),
},
});
}
addCookieToJar(url, loginResponseHeaders);
// Retry original request with cookie set
[status, , data] = await httpProxy(url, params);
}
if (status >= 400) {
logger.error("HTTP Error %d calling %s", status, sanitizeErrorURL(url));
return res.status(status).json({
error: {
message: `HTTP Error ${status} from Frigate`,
url: sanitizeErrorURL(url),
},
});
}
data = asJson(data);
if (endpoint == "stats") {
res.status(status).send({
num_cameras: data?.cameras !== undefined ? Object.keys(data?.cameras).length : 0,
uptime: data?.service?.uptime,
version: data?.service.version,
});
} else if (endpoint == "events") {
return res.status(status).send(
data.slice(0, 5).map((event) => ({
id: event.id,
camera: event.camera,
label: event.label,
start_time: new Date(event.start_time * 1000),
thumbnail: event.thumbnail,
score: event.data.score,
type: event.data.type,
})),
);
}
}
}
logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group);
return res.status(400).json({ error: "Invalid proxy service type" });
}

View File

@@ -1,37 +1,12 @@
import { asJson } from "utils/proxy/api-helpers"; import frigateProxyHandler from "./proxy";
import genericProxyHandler from "utils/proxy/handlers/generic";
const widget = { const widget = {
api: "{url}/api/{endpoint}", api: "{url}/api/{endpoint}",
proxyHandler: genericProxyHandler, proxyHandler: frigateProxyHandler,
mappings: { mappings: {
stats: { stats: { endpoint: "stats" },
endpoint: "stats", events: { endpoint: "events" },
map: (data) => {
const jsonData = asJson(data);
return {
num_cameras: jsonData?.cameras !== undefined ? Object.keys(jsonData?.cameras).length : 0,
uptime: jsonData?.service?.uptime,
version: jsonData?.service.version,
};
},
},
events: {
endpoint: "events",
map: (data) =>
asJson(data)
.slice(0, 5)
.map((event) => ({
id: event.id,
camera: event.camera,
label: event.label,
start_time: new Date(event.start_time * 1000),
thumbnail: event.thumbnail,
score: event.data.score,
type: event.data.type,
})),
},
}, },
}; };

View File

@@ -20,13 +20,15 @@ function getPerformancePercent(t, performanceRange) {
export default function Component({ service }) { export default function Component({ service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const includeNetWorth = widget.fields?.includes("net_worth");
const { data: performanceToday, error: ghostfolioErrorToday } = useWidgetAPI(widget, "today"); const { data: performanceToday, error: ghostfolioErrorToday } = useWidgetAPI(widget, "today");
const { data: performanceYear, error: ghostfolioErrorYear } = useWidgetAPI(widget, "year"); const { data: performanceYear, error: ghostfolioErrorYear } = useWidgetAPI(widget, "year");
const { data: performanceMax, error: ghostfolioErrorMax } = useWidgetAPI(widget, "max"); const { data: performanceMax, error: ghostfolioErrorMax } = useWidgetAPI(widget, "max");
const { data: userInfo, error: ghostfolioErrorUserInfo } = useWidgetAPI(widget, includeNetWorth ? "userInfo" : "");
if (ghostfolioErrorToday || ghostfolioErrorYear || ghostfolioErrorMax) { if (ghostfolioErrorToday || ghostfolioErrorYear || ghostfolioErrorMax || ghostfolioErrorUserInfo) {
const finalError = ghostfolioErrorToday ?? ghostfolioErrorYear ?? ghostfolioErrorMax; const finalError = ghostfolioErrorToday ?? ghostfolioErrorYear ?? ghostfolioErrorMax ?? ghostfolioErrorUserInfo;
return <Container service={service} error={finalError} />; return <Container service={service} error={finalError} />;
} }
@@ -34,12 +36,13 @@ export default function Component({ service }) {
return <Container service={service} error={performanceToday} />; return <Container service={service} error={performanceToday} />;
} }
if (!performanceToday || !performanceYear || !performanceMax) { if (!performanceToday || !performanceYear || !performanceMax || (includeNetWorth && !userInfo)) {
return ( return (
<Container service={service}> <Container service={service}>
<Block label="ghostfolio.gross_percent_today" /> <Block label="ghostfolio.gross_percent_today" />
<Block label="ghostfolio.gross_percent_1y" /> <Block label="ghostfolio.gross_percent_1y" />
<Block label="ghostfolio.gross_percent_max" /> <Block label="ghostfolio.gross_percent_max" />
{includeNetWorth && <Block label="ghostfolio.net_worth" />}
</Container> </Container>
); );
} }
@@ -49,6 +52,12 @@ export default function Component({ service }) {
<Block label="ghostfolio.gross_percent_today" value={getPerformancePercent(t, performanceToday)} /> <Block label="ghostfolio.gross_percent_today" value={getPerformancePercent(t, performanceToday)} />
<Block label="ghostfolio.gross_percent_1y" value={getPerformancePercent(t, performanceYear)} /> <Block label="ghostfolio.gross_percent_1y" value={getPerformancePercent(t, performanceYear)} />
<Block label="ghostfolio.gross_percent_max" value={getPerformancePercent(t, performanceMax)} /> <Block label="ghostfolio.gross_percent_max" value={getPerformancePercent(t, performanceMax)} />
{includeNetWorth && (
<Block
label="ghostfolio.net_worth"
value={`${performanceToday.performance.currentNetWorth.toFixed(2)} ${userInfo?.settings?.currency ?? ""}`}
/>
)}
</Container> </Container>
); );
} }

View File

@@ -1,18 +1,21 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = { const widget = {
api: "{url}/api/v2/portfolio/performance?range={endpoint}", api: "{url}/api/{endpoint}",
proxyHandler: credentialedProxyHandler, proxyHandler: credentialedProxyHandler,
mappings: { mappings: {
today: { today: {
endpoint: "1d", endpoint: "v2/portfolio/performance?range=1d",
}, },
year: { year: {
endpoint: "1y", endpoint: "v2/portfolio/performance?range=1y",
}, },
max: { max: {
endpoint: "max", endpoint: "v2/portfolio/performance?range=max",
},
userInfo: {
endpoint: "v1/user",
}, },
}, },
}; };

View File

@@ -12,10 +12,8 @@ export default function Component({ service }) {
const { data: gluetunData, error: gluetunError } = useWidgetAPI(widget, "ip"); const { data: gluetunData, error: gluetunError } = useWidgetAPI(widget, "ip");
const includePF = widget.fields.includes("port_forwarded"); const includePF = widget.fields.includes("port_forwarded");
const { data: portForwardedData, error: portForwardedError } = useWidgetAPI( const pfEndpoint = widget.version > 1 ? "port_forwarded_v2" : "port_forwarded";
widget, const { data: portForwardedData, error: portForwardedError } = useWidgetAPI(widget, includePF ? pfEndpoint : "");
includePF ? "port_forwarded" : "",
);
if (gluetunError || (includePF && portForwardedError)) { if (gluetunError || (includePF && portForwardedError)) {
return <Container service={service} error={gluetunError || portForwardedError} />; return <Container service={service} error={gluetunError || portForwardedError} />;

View File

@@ -13,6 +13,10 @@ const widget = {
endpoint: "openvpn/portforwarded", endpoint: "openvpn/portforwarded",
validate: ["port"], validate: ["port"],
}, },
port_forwarded_v2: {
endpoint: "portforward",
validate: ["port"],
},
}, },
}; };

View File

@@ -32,7 +32,7 @@ export default function Component({ service }) {
if ( if (
(!widget.showStacks && !containersData) || (!widget.showStacks && !containersData) ||
(widget.showSummary && (!stacksData || !serversData)) || (widget.showSummary && (!containersData || !stacksData || !serversData)) ||
(widget.showStacks && !stacksData) (widget.showStacks && !stacksData)
) { ) {
return widget.showSummary ? ( return widget.showSummary ? (

View File

@@ -24,9 +24,9 @@ export default function Component({ service }) {
if (!data || (data && data.length === 0)) { if (!data || (data && data.length === 0)) {
return ( return (
<Container service={service}> <Container service={service}>
<Block label="myspeed.ping" />
<Block label="myspeed.download" /> <Block label="myspeed.download" />
<Block label="myspeed.upload" /> <Block label="myspeed.upload" />
<Block label="myspeed.ping" />
</Container> </Container>
); );
} }