Compare commits
62
Commits
No files matched your search
@@ -33,6 +33,8 @@ body:
|
|||||||
options:
|
options:
|
||||||
# The below comment is used to insert a new version with on-release.yml
|
# The below comment is used to insert a new version with on-release.yml
|
||||||
#NEXT_VERSION#
|
#NEXT_VERSION#
|
||||||
|
- 1.57.1
|
||||||
|
- 1.57.0
|
||||||
- 1.56.1
|
- 1.56.1
|
||||||
- 1.56.0
|
- 1.56.0
|
||||||
- 1.55.0
|
- 1.55.0
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ runs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build docker image for ${{ inputs.architecture }}
|
- name: Build docker image for ${{ inputs.architecture }}
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
push: false
|
push: false
|
||||||
load: true
|
load: true
|
||||||
|
|||||||
@@ -31,6 +31,12 @@
|
|||||||
automerge: false,
|
automerge: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
// Updates lockfile for prebuilt-debian after if package.json is updated, otherwise only root lockfile is updated
|
||||||
|
postUpgradeTasks: {
|
||||||
|
commands: ["cd deployments/prebuilt-debian && pnpm install --ignore-workspace --lockfile-only"],
|
||||||
|
fileFilters: ["deployments/prebuilt-debian/pnpm-lock.yaml"],
|
||||||
|
executionMode: "update",
|
||||||
|
},
|
||||||
updateInternalDeps: true,
|
updateInternalDeps: true,
|
||||||
rangeStrategy: "bump",
|
rangeStrategy: "bump",
|
||||||
automerge: false,
|
automerge: false,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||||
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
uses: ./tooling/github/setup
|
uses: ./tooling/github/setup
|
||||||
- name: Build docker image
|
- name: Build docker image
|
||||||
id: build-docker-image
|
id: build-docker-image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.CROWDIN_APP_PRIVATE_KEY }}
|
private-key: ${{ secrets.CROWDIN_APP_PRIVATE_KEY }}
|
||||||
app-id: ${{ secrets.CROWDIN_APP_ID }}
|
app-id: ${{ secrets.CROWDIN_APP_ID }}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||||
@@ -60,12 +60,12 @@ jobs:
|
|||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
- uses: actions/setup-node@v6
|
- uses: actions/setup-node@v6
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
with:
|
with:
|
||||||
node-version: 24.14.0
|
node-version: 24.14.1
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- run: npm i -g pnpm
|
- run: npm i -g pnpm
|
||||||
if: env.SKIP_RELEASE == 'false'
|
if: env.SKIP_RELEASE == 'false'
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: "${{ env.GHCR_REPO }}"
|
images: "${{ env.GHCR_REPO }}"
|
||||||
|
|
||||||
@@ -135,11 +135,11 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push by digest
|
- name: Build and push by digest
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
network: host
|
network: host
|
||||||
@@ -162,7 +162,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
images: "${{ env.GHCR_REPO }}"
|
images: "${{ env.GHCR_REPO }}"
|
||||||
|
|
||||||
@@ -174,11 +174,11 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Build and push by digest
|
- name: Build and push by digest
|
||||||
id: build
|
id: build
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
network: host
|
network: host
|
||||||
@@ -231,7 +231,7 @@ jobs:
|
|||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
if: needs.release.outputs.skipped == 'false'
|
if: needs.release.outputs.skipped == 'false'
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
|
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainApprovalToken
|
id: obtainApprovalToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
|
||||||
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
gh pr review --approve --body "Automatically approved by GitHub Action"
|
gh pr review --approve --body "Automatically approved by GitHub Action"
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainMergeToken
|
id: obtainMergeToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
|
private-key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
|
||||||
app-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
|
app-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
|
||||||
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
||||||
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v5
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Obtain token
|
- name: Obtain token
|
||||||
id: obtainToken
|
id: obtainToken
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@v3
|
||||||
with:
|
with:
|
||||||
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
|
||||||
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
|
||||||
|
|||||||
@@ -1,3 +1,50 @@
|
|||||||
|
## [1.58.0](https://github.com/homarr-labs/homarr/compare/v1.57.1...v1.58.0) (2026-04-03)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **integrations:** add search.ch integration with timetable widget ([#5180](https://github.com/homarr-labs/homarr/issues/5180)) ([abb2dfc](https://github.com/homarr-labs/homarr/commit/abb2dfc92ab51b16e7172374cfee8b9c0c752454))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **boards:** unable to click on links at settings page ([#5230](https://github.com/homarr-labs/homarr/issues/5230)) ([6a200c3](https://github.com/homarr-labs/homarr/commit/6a200c39afaa1a9a1057643612065a40ad1cdd82))
|
||||||
|
* **deps:** update dependency @ctrl/deluge to ^7.5.3 ([#5365](https://github.com/homarr-labs/homarr/issues/5365)) ([9c134c5](https://github.com/homarr-labs/homarr/commit/9c134c5ceb742b21b021ef3b49de8cc0d526d460))
|
||||||
|
* **deps:** update dependency @ctrl/deluge to v8 ([#5367](https://github.com/homarr-labs/homarr/issues/5367)) ([e88e3d1](https://github.com/homarr-labs/homarr/commit/e88e3d17c01dab8836bec11d2ac01195f06cdc2e))
|
||||||
|
* **deps:** update dependency @immich/sdk to ^2.6.2 ([#5370](https://github.com/homarr-labs/homarr/issues/5370)) ([4a5bafa](https://github.com/homarr-labs/homarr/commit/4a5bafa79092b209e94a56f72362011f416bf115))
|
||||||
|
* **deps:** update dependency @immich/sdk to ^2.6.3 ([#5380](https://github.com/homarr-labs/homarr/issues/5380)) ([2dd3c90](https://github.com/homarr-labs/homarr/commit/2dd3c906c4215b55e23f4b15ba4b423e23be8376))
|
||||||
|
* **deps:** update dependency @tabler/icons-react to ^3.41.0 ([#5384](https://github.com/homarr-labs/homarr/issues/5384)) ([a9bcc9d](https://github.com/homarr-labs/homarr/commit/a9bcc9d04bdfcf28ea561cd590ca2a4bd775593c))
|
||||||
|
* **deps:** update dependency better-sqlite3 to ^12.8.0 ([#5266](https://github.com/homarr-labs/homarr/issues/5266)) ([46db8b6](https://github.com/homarr-labs/homarr/commit/46db8b6ab32406fb4ff7a7a9938692954778a070))
|
||||||
|
* **deps:** update dependency drizzle-orm to ^0.45.2 ([#5388](https://github.com/homarr-labs/homarr/issues/5388)) ([0a78e3f](https://github.com/homarr-labs/homarr/commit/0a78e3f12aa99509f42d7af647a478c5abaf5c96))
|
||||||
|
* **deps:** update dependency fast-xml-parser to ^5.5.9 ([#5359](https://github.com/homarr-labs/homarr/issues/5359)) ([189806b](https://github.com/homarr-labs/homarr/commit/189806b5a2b8e67f925cc8def6bc8ddefdc5bfc4))
|
||||||
|
* **deps:** update dependency fastify to ^5.8.4 ([#5351](https://github.com/homarr-labs/homarr/issues/5351)) ([728f7e4](https://github.com/homarr-labs/homarr/commit/728f7e489aa499bcd7e97c7ec8571e29e7e2cab3))
|
||||||
|
* **deps:** update dependency isomorphic-dompurify to ^3.7.0 ([#5366](https://github.com/homarr-labs/homarr/issues/5366)) ([c2b54dd](https://github.com/homarr-labs/homarr/commit/c2b54ddfaab93daa4e65c0c38bf5c27e4402a492))
|
||||||
|
* **deps:** update dependency isomorphic-dompurify to ^3.7.1 ([#5369](https://github.com/homarr-labs/homarr/issues/5369)) ([8d1ff40](https://github.com/homarr-labs/homarr/commit/8d1ff4065d669268f0e6a03f2b68c1ba728a756a))
|
||||||
|
* **deps:** update dependency jotai to ^2.19.0 ([#5368](https://github.com/homarr-labs/homarr/issues/5368)) ([14d8717](https://github.com/homarr-labs/homarr/commit/14d87178b5a93bdc6fb74793feed95d014814d0b))
|
||||||
|
* **deps:** update dependency node-ical to ^0.23.1 - abandoned ([#4856](https://github.com/homarr-labs/homarr/issues/4856)) ([b07c545](https://github.com/homarr-labs/homarr/commit/b07c545f17f4dafa62167047baa9eed4b7ee4395))
|
||||||
|
* **deps:** update dependency typescript-eslint to ^8.57.2 ([#5360](https://github.com/homarr-labs/homarr/issues/5360)) ([cefbf0e](https://github.com/homarr-labs/homarr/commit/cefbf0e2715e9adbc72cdb0b83d84c309e3257d6))
|
||||||
|
* **deps:** update dependency undici to v7.24.6 ([#5374](https://github.com/homarr-labs/homarr/issues/5374)) ([4f2cc9a](https://github.com/homarr-labs/homarr/commit/4f2cc9af2a42e19624def3bdea60fcb00334c483))
|
||||||
|
* **deps:** update dependency zod to ^4.3.5 - abandoned ([#4803](https://github.com/homarr-labs/homarr/issues/4803)) ([2b4c83d](https://github.com/homarr-labs/homarr/commit/2b4c83dccac168ffa6f9b6e184ed004659373cf7))
|
||||||
|
* **deps:** update dependency zod to ^4.3.6 ([#5376](https://github.com/homarr-labs/homarr/issues/5376)) ([a9582f0](https://github.com/homarr-labs/homarr/commit/a9582f04edb0e161d1fab6e64b13d0ce463ef341))
|
||||||
|
* **deps:** update tanstack-query monorepo to ^5.95.0 ([#5325](https://github.com/homarr-labs/homarr/issues/5325)) ([b11bf36](https://github.com/homarr-labs/homarr/commit/b11bf367d0567e4c8b9068804442ebd200dab01c))
|
||||||
|
* **deps:** update tanstack-query monorepo to ^5.95.1 ([#5355](https://github.com/homarr-labs/homarr/issues/5355)) ([332b6b4](https://github.com/homarr-labs/homarr/commit/332b6b4f42e7c90570cd4c25a6e6a331a76c660f))
|
||||||
|
* **deps:** update tanstack-query monorepo to ^5.95.2 ([#5357](https://github.com/homarr-labs/homarr/issues/5357)) ([6820991](https://github.com/homarr-labs/homarr/commit/682099189b689ea17e16af41fd280a72ed68016f))
|
||||||
|
* **deps:** update tiptap monorepo to v3.20.5 ([#5371](https://github.com/homarr-labs/homarr/issues/5371)) ([6820f8a](https://github.com/homarr-labs/homarr/commit/6820f8afea622587c1ee18b13d13a041ca0572ee))
|
||||||
|
* **deps:** update tiptap monorepo to v3.21.0 ([#5386](https://github.com/homarr-labs/homarr/issues/5386)) ([b30dbaf](https://github.com/homarr-labs/homarr/commit/b30dbafbbbad468ff37ff11237a64ec29df62345))
|
||||||
|
* **deps:** update trpc monorepo to ^11.15.0 ([#5354](https://github.com/homarr-labs/homarr/issues/5354)) ([2fb2981](https://github.com/homarr-labs/homarr/commit/2fb2981d2fb79ca2ca6a9bc5c08d91c8fa5710e7))
|
||||||
|
* **deps:** update trpc monorepo to ^11.15.1 ([#5381](https://github.com/homarr-labs/homarr/issues/5381)) ([451e68a](https://github.com/homarr-labs/homarr/commit/451e68ab57551e23dd7ae772b7483cc56d57ebbb))
|
||||||
|
* **deps:** update trpc monorepo to ^11.16.0 ([#5392](https://github.com/homarr-labs/homarr/issues/5392)) ([72a93e6](https://github.com/homarr-labs/homarr/commit/72a93e681f3701459176692857220730c279f80b))
|
||||||
|
* **deps:** update turbo monorepo to ^2.8.21 ([#5391](https://github.com/homarr-labs/homarr/issues/5391)) ([fa40b6b](https://github.com/homarr-labs/homarr/commit/fa40b6b647b884a7f10c268a22330e0e68b50d4d))
|
||||||
|
* honor trusted certificates in Immich integration requests ([#5275](https://github.com/homarr-labs/homarr/issues/5275)) ([7679553](https://github.com/homarr-labs/homarr/commit/7679553f16ff2945a3eb6cb668ca3a701d13d5ce)), closes [#5242](https://github.com/homarr-labs/homarr/issues/5242)
|
||||||
|
* node-ical prevents many trpc queries from loading ([#5395](https://github.com/homarr-labs/homarr/issues/5395)) ([c977b84](https://github.com/homarr-labs/homarr/commit/c977b84e2d84d5724219f5ff5f99bc550438e168))
|
||||||
|
|
||||||
|
## [1.57.1](https://github.com/homarr-labs/homarr/compare/v1.57.0...v1.57.1) (2026-03-27)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* dashdot gpu ([#5347](https://github.com/homarr-labs/homarr/issues/5347)) ([192a43a](https://github.com/homarr-labs/homarr/commit/192a43a031eb9431da2c11395eda69a6c7236b9b))
|
||||||
|
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.11 ([#5345](https://github.com/homarr-labs/homarr/issues/5345)) ([49d9394](https://github.com/homarr-labs/homarr/commit/49d9394cd78b2a1146c8dc00dd982215a2f9a580))
|
||||||
|
* **deps:** update dependency dockerode to ^4.0.10 ([#5327](https://github.com/homarr-labs/homarr/issues/5327)) ([42a3791](https://github.com/homarr-labs/homarr/commit/42a3791e6b5664a0f28d3ee4d29d5257ef3765f6))
|
||||||
|
* **deps:** update dependency fast-xml-parser to ^5.5.8 ([#5324](https://github.com/homarr-labs/homarr/issues/5324)) ([7982938](https://github.com/homarr-labs/homarr/commit/798293813cf82e38d7059069823f927b7cdfb3b8))
|
||||||
|
|
||||||
## [1.57.0](https://github.com/homarr-labs/homarr/compare/v1.56.1...v1.57.0) (2026-03-27)
|
## [1.57.0](https://github.com/homarr-labs/homarr/compare/v1.56.1...v1.57.0) (2026-03-27)
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM node:24.14.0-alpine AS base
|
FROM node:24.14.1-alpine AS base
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
RUN apk add --no-cache libc6-compat
|
RUN apk add --no-cache libc6-compat
|
||||||
|
|||||||
+11
-11
@@ -55,14 +55,14 @@
|
|||||||
"@mantine/modals": "^8.3.18",
|
"@mantine/modals": "^8.3.18",
|
||||||
"@mantine/tiptap": "^8.3.18",
|
"@mantine/tiptap": "^8.3.18",
|
||||||
"@million/lint": "1.0.14",
|
"@million/lint": "1.0.14",
|
||||||
"@tabler/icons-react": "^3.40.0",
|
"@tabler/icons-react": "^3.41.0",
|
||||||
"@tanstack/react-query": "^5.91.2",
|
"@tanstack/react-query": "^5.95.2",
|
||||||
"@tanstack/react-query-devtools": "^5.91.3",
|
"@tanstack/react-query-devtools": "^5.95.2",
|
||||||
"@tanstack/react-query-next-experimental": "^5.91.0",
|
"@tanstack/react-query-next-experimental": "^5.95.2",
|
||||||
"@trpc/client": "^11.14.1",
|
"@trpc/client": "^11.16.0",
|
||||||
"@trpc/next": "^11.14.1",
|
"@trpc/next": "^11.16.0",
|
||||||
"@trpc/react-query": "^11.14.1",
|
"@trpc/react-query": "^11.16.0",
|
||||||
"@trpc/server": "^11.14.1",
|
"@trpc/server": "^11.16.0",
|
||||||
"@xterm/addon-canvas": "^0.7.0",
|
"@xterm/addon-canvas": "^0.7.0",
|
||||||
"@xterm/addon-fit": "0.11.0",
|
"@xterm/addon-fit": "0.11.0",
|
||||||
"@xterm/xterm": "^6.0.0",
|
"@xterm/xterm": "^6.0.0",
|
||||||
@@ -73,8 +73,8 @@
|
|||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"flag-icons": "^7.5.0",
|
"flag-icons": "^7.5.0",
|
||||||
"glob": "^13.0.6",
|
"glob": "^13.0.6",
|
||||||
"isomorphic-dompurify": "^3.6.0",
|
"isomorphic-dompurify": "^3.7.1",
|
||||||
"jotai": "^2.18.1",
|
"jotai": "^2.19.0",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "16.2.1",
|
"next": "16.2.1",
|
||||||
"postcss-preset-mantine": "^1.18.0",
|
"postcss-preset-mantine": "^1.18.0",
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"swagger-ui-react": "^5.32.1",
|
"swagger-ui-react": "^5.32.1",
|
||||||
"use-deep-compare-effect": "^1.8.1",
|
"use-deep-compare-effect": "^1.8.1",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Button, Grid, Group, Loader, Stack, TextInput } from "@mantine/core";
|
import { Button, Grid, Group, Loader, Stack, TextInput } from "@mantine/core";
|
||||||
import { useDebouncedValue, useDocumentTitle, useFavicon } from "@mantine/hooks";
|
import { useDebouncedValue, useDocumentTitle } from "@mantine/hooks";
|
||||||
|
|
||||||
import { useUpdateBoard } from "@homarr/boards/updater";
|
import { useUpdateBoard } from "@homarr/boards/updater";
|
||||||
import { useZodForm } from "@homarr/form";
|
import { useZodForm } from "@homarr/form";
|
||||||
@@ -10,6 +10,7 @@ import { IconPicker } from "@homarr/forms-collection";
|
|||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
import { boardSavePartialSettingsSchema } from "@homarr/validation/board";
|
import { boardSavePartialSettingsSchema } from "@homarr/validation/board";
|
||||||
|
|
||||||
|
import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
|
||||||
import { createMetaTitle } from "~/metadata";
|
import { createMetaTitle } from "~/metadata";
|
||||||
import type { Board } from "../../_types";
|
import type { Board } from "../../_types";
|
||||||
import { useSavePartialSettingsMutation } from "./_shared";
|
import { useSavePartialSettingsMutation } from "./_shared";
|
||||||
@@ -53,7 +54,6 @@ export const GeneralSettingsContent = ({ board }: Props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useLogoPreview(form.values.logoImageUrl);
|
useLogoPreview(form.values.logoImageUrl);
|
||||||
useFaviconPreview(form.values.faviconImageUrl);
|
|
||||||
const metaTitleStatus = useMetaTitlePreview(form.values.metaTitle);
|
const metaTitleStatus = useMetaTitlePreview(form.values.metaTitle);
|
||||||
|
|
||||||
// Cleanup for not applied changes of the page title and logo image URL
|
// Cleanup for not applied changes of the page title and logo image URL
|
||||||
@@ -75,6 +75,7 @@ export const GeneralSettingsContent = ({ board }: Props) => {
|
|||||||
pageTitle: values.pageTitle,
|
pageTitle: values.pageTitle,
|
||||||
logoImageUrl: values.logoImageUrl,
|
logoImageUrl: values.logoImageUrl,
|
||||||
};
|
};
|
||||||
|
updateFavicon(values.faviconImageUrl ?? homarrLogoPath);
|
||||||
savePartialSettings({
|
savePartialSettings({
|
||||||
id: board.id,
|
id: board.id,
|
||||||
...values,
|
...values,
|
||||||
@@ -146,7 +147,13 @@ const useMetaTitlePreview = (title: string | null) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const useFaviconPreview = (url: string | null) => {
|
// Previously we used the useFavicon hook from Mantine
|
||||||
const [faviconDebounced] = useDebouncedValue(url ?? "", 500);
|
// However if the user tried to click on a link to a different page
|
||||||
useFavicon(faviconDebounced);
|
// it caused an error "Cannot read properties of null (reading 'removeChild')"
|
||||||
|
// Probably because the head element was null (https://github.com/mantinedev/mantine/blob/b90d9b81031f45f5d15e75f25138ed6477f65bce/packages/%40mantine/hooks/src/use-favicon/use-favicon.ts#L21)
|
||||||
|
// See https://github.com/homarr-labs/homarr/issues/4905
|
||||||
|
const updateFavicon = (url: string) => {
|
||||||
|
const link: HTMLLinkElement | null = document.querySelector('link[rel="icon"]');
|
||||||
|
if (!link) return;
|
||||||
|
link.href = url;
|
||||||
};
|
};
|
||||||
@@ -39,9 +39,9 @@
|
|||||||
"@homarr/widgets": "workspace:^0.1.0",
|
"@homarr/widgets": "workspace:^0.1.0",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"fastify": "^5.8.2",
|
"fastify": "^5.8.4",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"undici": "7.24.5"
|
"undici": "7.24.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:24.14.0-trixie AS base
|
FROM node:24.14.1-trixie AS base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
COPY pnpm-lock.yaml .
|
COPY pnpm-lock.yaml .
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
"name": "homarr-prebuilt-debian",
|
"name": "homarr-prebuilt-debian",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "^12.6.2"
|
"better-sqlite3": "^12.8.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.32.1",
|
"packageManager": "pnpm@10.33.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24.14.0",
|
"node": ">=24.14.1",
|
||||||
"pnpm": ">=10.32.1"
|
"pnpm": ">=10.33.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
|||||||
+6
-5
@@ -9,16 +9,16 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
better-sqlite3:
|
better-sqlite3:
|
||||||
specifier: ^12.6.2
|
specifier: ^12.8.0
|
||||||
version: 12.6.2
|
version: 12.8.0
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
base64-js@1.5.1:
|
base64-js@1.5.1:
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
|
||||||
better-sqlite3@12.6.2:
|
better-sqlite3@12.8.0:
|
||||||
resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==}
|
resolution: {integrity: sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==}
|
||||||
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
|
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
@@ -93,6 +93,7 @@ packages:
|
|||||||
prebuild-install@7.1.3:
|
prebuild-install@7.1.3:
|
||||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
pump@3.0.3:
|
pump@3.0.3:
|
||||||
@@ -147,7 +148,7 @@ snapshots:
|
|||||||
|
|
||||||
base64-js@1.5.1: {}
|
base64-js@1.5.1: {}
|
||||||
|
|
||||||
better-sqlite3@12.6.2:
|
better-sqlite3@12.8.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
bindings: 1.5.0
|
bindings: 1.5.0
|
||||||
prebuild-install: 7.1.3
|
prebuild-install: 7.1.3
|
||||||
|
|||||||
+11
-4
@@ -302,6 +302,13 @@
|
|||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td align="center">
|
<tr><td align="center">
|
||||||
|
<a href="https://homarr.dev/docs/integrations/search-ch" target="_blank" rel="noreferrer noopener">
|
||||||
|
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/search-ch.png" alt="Search.ch" width="90" height="90" />
|
||||||
|
<br/>
|
||||||
|
<p align="center">Search.ch</p>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
<a href="https://homarr.dev/docs/integrations/seerr" target="_blank" rel="noreferrer noopener">
|
<a href="https://homarr.dev/docs/integrations/seerr" target="_blank" rel="noreferrer noopener">
|
||||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/seerr.svg" alt="Seerr" width="90" height="90" />
|
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/seerr.svg" alt="Seerr" width="90" height="90" />
|
||||||
<br/>
|
<br/>
|
||||||
@@ -342,15 +349,15 @@
|
|||||||
<br/>
|
<br/>
|
||||||
<p align="center">Transmission</p>
|
<p align="center">Transmission</p>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td></tr>
|
||||||
<td align="center">
|
<tr><td align="center">
|
||||||
<a href="https://homarr.dev/docs/integrations/truenas" target="_blank" rel="noreferrer noopener">
|
<a href="https://homarr.dev/docs/integrations/truenas" target="_blank" rel="noreferrer noopener">
|
||||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/truenas.svg" alt="TrueNAS" width="90" height="90" />
|
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/truenas.svg" alt="TrueNAS" width="90" height="90" />
|
||||||
<br/>
|
<br/>
|
||||||
<p align="center">TrueNAS</p>
|
<p align="center">TrueNAS</p>
|
||||||
</a>
|
</a>
|
||||||
</td></tr>
|
</td>
|
||||||
<tr><td align="center">
|
<td align="center">
|
||||||
<a href="https://homarr.dev/docs/integrations/unifi-controller" target="_blank" rel="noreferrer noopener">
|
<a href="https://homarr.dev/docs/integrations/unifi-controller" target="_blank" rel="noreferrer noopener">
|
||||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unifi.png" alt="Unifi Controller" width="90" height="90" />
|
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unifi.png" alt="Unifi Controller" width="90" height="90" />
|
||||||
<br/>
|
<br/>
|
||||||
|
|||||||
+17
-17
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homarr",
|
"name": "homarr",
|
||||||
"version": "1.57.0",
|
"version": "1.58.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env CI=true turbo build",
|
"build": "cross-env CI=true turbo build",
|
||||||
@@ -43,26 +43,26 @@
|
|||||||
"@semantic-release/npm": "^13.1.5",
|
"@semantic-release/npm": "^13.1.5",
|
||||||
"@semantic-release/release-notes-generator": "^14.1.0",
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
||||||
"@testcontainers/redis": "^11.13.0",
|
"@testcontainers/redis": "^11.13.0",
|
||||||
"@turbo/gen": "^2.8.20",
|
"@turbo/gen": "^2.8.21",
|
||||||
"@vitejs/plugin-react": "^5.2.0",
|
"@vitejs/plugin-react": "^5.2.0",
|
||||||
"@vitest/coverage-v8": "^4.1.0",
|
"@vitest/coverage-v8": "^4.1.2",
|
||||||
"@vitest/ui": "^4.1.0",
|
"@vitest/ui": "^4.1.2",
|
||||||
"conventional-changelog-conventionalcommits": "^9.3.0",
|
"conventional-changelog-conventionalcommits": "^9.3.1",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"jsdom": "^28.1.0",
|
"jsdom": "^29.0.1",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
"semantic-release": "^25.0.3",
|
"semantic-release": "^25.0.3",
|
||||||
"testcontainers": "^11.13.0",
|
"testcontainers": "^11.13.0",
|
||||||
"turbo": "^2.8.20",
|
"turbo": "^2.8.21",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite-tsconfig-paths": "^6.1.1",
|
"vite-tsconfig-paths": "^6.1.1",
|
||||||
"vitest": "^4.1.0"
|
"vitest": "^4.1.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.32.1",
|
"packageManager": "pnpm@10.33.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=24.14.0",
|
"node": ">=24.14.1",
|
||||||
"pnpm": ">=10.32.1"
|
"pnpm": ">=10.33.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
@@ -79,23 +79,23 @@
|
|||||||
"overrides": {
|
"overrides": {
|
||||||
"@babel/helpers@<7.26.10": ">=7.29.2",
|
"@babel/helpers@<7.26.10": ">=7.29.2",
|
||||||
"@babel/runtime@<7.26.10": ">=7.29.2",
|
"@babel/runtime@<7.26.10": ">=7.29.2",
|
||||||
"axios@>=1.0.0 <1.8.2": ">=1.13.6",
|
"axios@>=1.0.0 <1.8.2": ">=1.14.0",
|
||||||
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.4",
|
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.5",
|
||||||
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.4",
|
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.5",
|
||||||
"esbuild@<=0.24.2": ">=0.27.4",
|
"esbuild@<=0.24.2": ">=0.27.4",
|
||||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
||||||
"hono@<4.6.5": ">=4.12.8",
|
"hono@<4.6.5": ">=4.12.9",
|
||||||
"linkifyjs@<4.3.2": ">=4.3.2",
|
"linkifyjs@<4.3.2": ">=4.3.2",
|
||||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.7",
|
"nanoid@>=4.0.0 <5.0.9": ">=5.1.7",
|
||||||
"prismjs@<1.30.0": ">=1.30.0",
|
"prismjs@<1.30.0": ">=1.30.0",
|
||||||
"proxmox-api>undici": "7.24.5",
|
"proxmox-api>undici": "7.24.6",
|
||||||
"react-is": "^19.2.4",
|
"react-is": "^19.2.4",
|
||||||
"rollup@>=4.0.0 <4.22.4": ">=4.60.0",
|
"rollup@>=4.0.0 <4.22.4": ">=4.60.0",
|
||||||
"sha.js@<=2.4.11": ">=2.4.12",
|
"sha.js@<=2.4.11": ">=2.4.12",
|
||||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
|
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
|
||||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
|
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
|
||||||
"tmp@<=0.2.3": ">=0.2.5",
|
"tmp@<=0.2.3": ">=0.2.5",
|
||||||
"vite@>=5.0.0 <=5.4.18": ">=8.0.1"
|
"vite@>=5.0.0 <=5.4.18": ">=8.0.3"
|
||||||
},
|
},
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
||||||
|
|||||||
@@ -41,18 +41,18 @@
|
|||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@kubernetes/client-node": "^1.4.0",
|
"@kubernetes/client-node": "^1.4.0",
|
||||||
"@tanstack/react-query": "^5.91.2",
|
"@tanstack/react-query": "^5.95.2",
|
||||||
"@trpc/client": "^11.14.1",
|
"@trpc/client": "^11.16.0",
|
||||||
"@trpc/react-query": "^11.14.1",
|
"@trpc/react-query": "^11.16.0",
|
||||||
"@trpc/server": "^11.14.1",
|
"@trpc/server": "^11.16.0",
|
||||||
"@trpc/tanstack-react-query": "^11.14.1",
|
"@trpc/tanstack-react-query": "^11.16.0",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"next": "16.2.1",
|
"next": "16.2.1",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"trpc-to-openapi": "^3.2.0",
|
"trpc-to-openapi": "^3.2.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export const userRouter = createTRPCRouter({
|
|||||||
create: permissionRequiredProcedure
|
create: permissionRequiredProcedure
|
||||||
.requiresPermission("admin")
|
.requiresPermission("admin")
|
||||||
.meta({ openapi: { method: "POST", path: "/api/users", tags: ["users"], protect: true } })
|
.meta({ openapi: { method: "POST", path: "/api/users", tags: ["users"], protect: true } })
|
||||||
.input(userCreateSchema)
|
.input(convertIntersectionToZodObject(userCreateSchema))
|
||||||
.output(z.void())
|
.output(z.void())
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
throwIfCredentialsDisabled();
|
throwIfCredentialsDisabled();
|
||||||
@@ -376,7 +376,7 @@ export const userRouter = createTRPCRouter({
|
|||||||
await ctx.db.delete(users).where(eq(users.id, input.userId));
|
await ctx.db.delete(users).where(eq(users.id, input.userId));
|
||||||
}),
|
}),
|
||||||
changePassword: protectedProcedure
|
changePassword: protectedProcedure
|
||||||
.input(userChangePasswordApiSchema)
|
.input(convertIntersectionToZodObject(userChangePasswordApiSchema))
|
||||||
.output(z.void())
|
.output(z.void())
|
||||||
.meta({ openapi: { method: "PATCH", path: "/api/users/{userId}/changePassword", tags: ["users"], protect: true } })
|
.meta({ openapi: { method: "PATCH", path: "/api/users/{userId}/changePassword", tags: ["users"], protect: true } })
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
|||||||
@@ -26,5 +26,6 @@ export const widgetRouter = createTRPCRouter({
|
|||||||
networkController: lazy(() => import("./network-controller").then((mod) => mod.networkControllerRouter)),
|
networkController: lazy(() => import("./network-controller").then((mod) => mod.networkControllerRouter)),
|
||||||
firewall: lazy(() => import("./firewall").then((mod) => mod.firewallRouter)),
|
firewall: lazy(() => import("./firewall").then((mod) => mod.firewallRouter)),
|
||||||
notifications: lazy(() => import("./notifications").then((mod) => mod.notificationsRouter)),
|
notifications: lazy(() => import("./notifications").then((mod) => mod.notificationsRouter)),
|
||||||
|
timetable: lazy(() => import("./timetable").then((mod) => mod.timetableRouter)),
|
||||||
tracearr: lazy(() => import("./tracearr").then((mod) => mod.tracearrRouter)),
|
tracearr: lazy(() => import("./tracearr").then((mod) => mod.tracearrRouter)),
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||||
|
import { timetableOptionsSchema } from "@homarr/integrations/types";
|
||||||
|
import {
|
||||||
|
timetableGetTimetableRequestHandler,
|
||||||
|
timetableSearchStationsRequestHandler,
|
||||||
|
} from "@homarr/request-handler/timetable";
|
||||||
|
|
||||||
|
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
|
||||||
|
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||||
|
|
||||||
|
export const timetableRouter = createTRPCRouter({
|
||||||
|
getTimetable: publicProcedure
|
||||||
|
.concat(createOneIntegrationMiddleware("query", ...getIntegrationKindsByCategory("timetable")))
|
||||||
|
.input(timetableOptionsSchema)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const innerHandler = timetableGetTimetableRequestHandler.handler(ctx.integration, {
|
||||||
|
stationId: input.stationId,
|
||||||
|
limit: input.limit,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}),
|
||||||
|
searchStations: publicProcedure
|
||||||
|
.concat(createOneIntegrationMiddleware("query", ...getIntegrationKindsByCategory("timetable")))
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
query: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const innerHandler = timetableSearchStationsRequestHandler.handler(ctx.integration, {
|
||||||
|
query: input.query,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"next-auth": "5.0.0-beta.30",
|
"next-auth": "5.0.0-beta.30",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
"octokit": "^5.0.5",
|
"octokit": "^5.0.5",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
"undici": "7.24.5",
|
"undici": "7.24.6",
|
||||||
"zod": "^4.2.1",
|
"zod": "^4.3.6",
|
||||||
"zod-validation-error": "^5.0.0"
|
"zod-validation-error": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -38,15 +38,15 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@t3-oss/env-nextjs": "^0.13.11",
|
"@t3-oss/env-nextjs": "^0.13.11",
|
||||||
"better-sqlite3": "^12.6.2",
|
"better-sqlite3": "^12.8.0",
|
||||||
"dns-caching": "^0.2.9",
|
"dns-caching": "^0.2.9",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.2",
|
||||||
"ioredis": "5.10.1",
|
"ioredis": "5.10.1",
|
||||||
"mysql2": "3.20.0",
|
"mysql2": "3.20.0",
|
||||||
"pg": "^8.20.0",
|
"pg": "^8.20.0",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"winston": "3.19.0",
|
"winston": "3.19.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -28,13 +28,13 @@
|
|||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"@homarr/core": "workspace:^0.1.0",
|
"@homarr/core": "workspace:^0.1.0",
|
||||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||||
"@tanstack/react-query": "^5.91.2",
|
"@tanstack/react-query": "^5.95.2",
|
||||||
"@trpc/client": "^11.14.1",
|
"@trpc/client": "^11.16.0",
|
||||||
"@trpc/server": "^11.14.1",
|
"@trpc/server": "^11.16.0",
|
||||||
"@trpc/tanstack-react-query": "^11.14.1",
|
"@trpc/tanstack-react-query": "^11.16.0",
|
||||||
"node-cron": "^4.2.1",
|
"node-cron": "^4.2.1",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { mediaServerJob } from "./jobs/integrations/media-server";
|
|||||||
import { mediaTranscodingJob } from "./jobs/integrations/media-transcoding";
|
import { mediaTranscodingJob } from "./jobs/integrations/media-transcoding";
|
||||||
import { networkControllerJob } from "./jobs/integrations/network-controller";
|
import { networkControllerJob } from "./jobs/integrations/network-controller";
|
||||||
import { refreshNotificationsJob } from "./jobs/integrations/notifications";
|
import { refreshNotificationsJob } from "./jobs/integrations/notifications";
|
||||||
|
import { timetableJob } from "./jobs/integrations/timetable";
|
||||||
import { tracearrJob } from "./jobs/integrations/tracearr";
|
import { tracearrJob } from "./jobs/integrations/tracearr";
|
||||||
import { minecraftServerStatusJob } from "./jobs/minecraft-server-status";
|
import { minecraftServerStatusJob } from "./jobs/minecraft-server-status";
|
||||||
import { pingJob } from "./jobs/ping";
|
import { pingJob } from "./jobs/ping";
|
||||||
@@ -51,6 +52,7 @@ export const jobGroup = createCronJobGroup({
|
|||||||
firewallInterfaces: firewallInterfacesJob,
|
firewallInterfaces: firewallInterfacesJob,
|
||||||
refreshNotifications: refreshNotificationsJob,
|
refreshNotifications: refreshNotificationsJob,
|
||||||
weather: weatherJob,
|
weather: weatherJob,
|
||||||
|
timetable: timetableJob,
|
||||||
tracearr: tracearrJob,
|
tracearr: tracearrJob,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { EVERY_5_MINUTES } from "@homarr/cron-jobs-core/expressions";
|
||||||
|
import { createRequestIntegrationJobHandler } from "@homarr/request-handler/lib/cached-request-integration-job-handler";
|
||||||
|
import { timetableGetTimetableRequestHandler } from "@homarr/request-handler/timetable";
|
||||||
|
|
||||||
|
import { createCronJob } from "../../lib";
|
||||||
|
|
||||||
|
export const timetableJob = createCronJob("timetable", EVERY_5_MINUTES).withCallback(
|
||||||
|
createRequestIntegrationJobHandler(timetableGetTimetableRequestHandler.handler, {
|
||||||
|
widgetKinds: ["timetable"],
|
||||||
|
getInput: {
|
||||||
|
timetable: (options) =>
|
||||||
|
options.station
|
||||||
|
? {
|
||||||
|
stationId: options.station.value,
|
||||||
|
limit: 10,
|
||||||
|
}
|
||||||
|
: [],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
@@ -51,10 +51,10 @@
|
|||||||
"@paralleldrive/cuid2": "^3.3.0",
|
"@paralleldrive/cuid2": "^3.3.0",
|
||||||
"@testcontainers/mysql": "^11.13.0",
|
"@testcontainers/mysql": "^11.13.0",
|
||||||
"@testcontainers/postgresql": "^11.13.0",
|
"@testcontainers/postgresql": "^11.13.0",
|
||||||
"better-sqlite3": "^12.6.2",
|
"better-sqlite3": "^12.8.0",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"drizzle-kit": "^0.31.10",
|
"drizzle-kit": "^0.31.10",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.2",
|
||||||
"drizzle-zod": "^0.8.3",
|
"drizzle-zod": "^0.8.3",
|
||||||
"mysql2": "3.20.0",
|
"mysql2": "3.20.0",
|
||||||
"pg": "^8.20.0",
|
"pg": "^8.20.0",
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"fast-xml-parser": "^5.5.8",
|
"fast-xml-parser": "^5.5.9",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -334,6 +334,15 @@ export const integrationDefs = {
|
|||||||
// @ts-expect-error - docs page will be created when integration is merged
|
// @ts-expect-error - docs page will be created when integration is merged
|
||||||
documentationUrl: createDocumentationLink("/docs/integrations/coolify"),
|
documentationUrl: createDocumentationLink("/docs/integrations/coolify"),
|
||||||
},
|
},
|
||||||
|
searchCh: {
|
||||||
|
name: "Search.ch",
|
||||||
|
secretKinds: [[]],
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/search-ch.png",
|
||||||
|
defaultUrl: "https://search.ch",
|
||||||
|
category: ["timetable"],
|
||||||
|
// @ts-expect-error - docs page will be created when integration is merged
|
||||||
|
documentationUrl: createDocumentationLink("/docs/integrations/search-ch"),
|
||||||
|
},
|
||||||
immich: {
|
immich: {
|
||||||
name: "Immich",
|
name: "Immich",
|
||||||
secretKinds: [["apiKey"]],
|
secretKinds: [["apiKey"]],
|
||||||
@@ -434,6 +443,7 @@ export const integrationCategories = [
|
|||||||
"releasesProvider",
|
"releasesProvider",
|
||||||
"notifications",
|
"notifications",
|
||||||
"firewall",
|
"firewall",
|
||||||
|
"timetable",
|
||||||
"photoService",
|
"photoService",
|
||||||
"mediaMonitoring",
|
"mediaMonitoring",
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const widgetKinds = [
|
|||||||
"systemResources",
|
"systemResources",
|
||||||
"coolify",
|
"coolify",
|
||||||
"systemDisks",
|
"systemDisks",
|
||||||
|
"timetable",
|
||||||
"immich-serverStats",
|
"immich-serverStats",
|
||||||
"immich-albumCarousel",
|
"immich-albumCarousel",
|
||||||
"tracearr",
|
"tracearr",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/form": "^8.3.18",
|
"@mantine/form": "^8.3.18",
|
||||||
"mantine-form-zod-resolver": "^1.3.0",
|
"mantine-form-zod-resolver": "^1.3.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/core": "^8.3.18",
|
"@mantine/core": "^8.3.18",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ctrl/deluge": "^7.5.2",
|
"@ctrl/deluge": "^8.0.0",
|
||||||
"@ctrl/qbittorrent": "^9.11.2",
|
"@ctrl/qbittorrent": "^9.11.2",
|
||||||
"@ctrl/transmission": "^7.4.0",
|
"@ctrl/transmission": "^7.4.0",
|
||||||
"@gitbeaker/rest": "^43.8.0",
|
"@gitbeaker/rest": "^43.8.0",
|
||||||
@@ -38,18 +38,18 @@
|
|||||||
"@homarr/redis": "workspace:^0.1.0",
|
"@homarr/redis": "workspace:^0.1.0",
|
||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@immich/sdk": "^2.6.1",
|
"@immich/sdk": "^2.6.3",
|
||||||
"@jellyfin/sdk": "^0.13.0",
|
"@jellyfin/sdk": "^0.13.0",
|
||||||
"@octokit/auth-app": "^8.2.0",
|
"@octokit/auth-app": "^8.2.0",
|
||||||
"ical.js": "^2.2.1",
|
"ical.js": "^2.2.1",
|
||||||
"maria2": "^0.4.1",
|
"maria2": "^0.4.1",
|
||||||
"node-ical": "^0.22.1",
|
|
||||||
"octokit": "^5.0.5",
|
"octokit": "^5.0.5",
|
||||||
"proxmox-api": "1.1.1",
|
"proxmox-api": "1.1.1",
|
||||||
|
"timezones-ical-library": "^2.1.3",
|
||||||
"tsdav": "^2.1.8",
|
"tsdav": "^2.1.8",
|
||||||
"undici": "7.24.5",
|
"undici": "7.24.6",
|
||||||
"xml2js": "^0.6.2",
|
"xml2js": "^0.6.2",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import { PlexIntegration } from "../plex/plex-integration";
|
|||||||
import { ProwlarrIntegration } from "../prowlarr/prowlarr-integration";
|
import { ProwlarrIntegration } from "../prowlarr/prowlarr-integration";
|
||||||
import { ProxmoxIntegration } from "../proxmox/proxmox-integration";
|
import { ProxmoxIntegration } from "../proxmox/proxmox-integration";
|
||||||
import { QuayIntegration } from "../quay/quay-integration";
|
import { QuayIntegration } from "../quay/quay-integration";
|
||||||
|
import { SearchChIntegration } from "../search-ch/search-ch-integration";
|
||||||
import { SeerrIntegration } from "../seerr/seerr-integration";
|
import { SeerrIntegration } from "../seerr/seerr-integration";
|
||||||
import { TracearrIntegration } from "../tracearr/tracearr-integration";
|
import { TracearrIntegration } from "../tracearr/tracearr-integration";
|
||||||
import { TrueNasIntegration } from "../truenas/truenas-integration";
|
import { TrueNasIntegration } from "../truenas/truenas-integration";
|
||||||
@@ -114,6 +115,7 @@ export const integrationCreators = {
|
|||||||
coolify: CoolifyIntegration,
|
coolify: CoolifyIntegration,
|
||||||
tracearr: TracearrIntegration,
|
tracearr: TracearrIntegration,
|
||||||
glances: GlancesIntegration,
|
glances: GlancesIntegration,
|
||||||
|
searchCh: SearchChIntegration,
|
||||||
immich: ImmichIntegration,
|
immich: ImmichIntegration,
|
||||||
} satisfies Record<IntegrationKind, IntegrationInstance | [(input: IntegrationInput) => Promise<Integration>]>;
|
} satisfies Record<IntegrationKind, IntegrationInstance | [(input: IntegrationInput) => Promise<Integration>]>;
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
|
|||||||
cpuModel: serverInfo.cpu.model,
|
cpuModel: serverInfo.cpu.model,
|
||||||
operatingSystemVersion: `${serverInfo.os.distro} ${serverInfo.os.release} (${serverInfo.os.kernel})`,
|
operatingSystemVersion: `${serverInfo.os.distro} ${serverInfo.os.release} (${serverInfo.os.kernel})`,
|
||||||
uptime: serverInfo.os.uptime,
|
uptime: serverInfo.os.uptime,
|
||||||
gpuNames: serverInfo.gpu.map((gpu) => gpu.brand),
|
gpuNames: serverInfo.gpu.layout.map((gpu) => gpu.brand),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,14 +161,14 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getCurrentGpuLoadAsync() {
|
private async getCurrentGpuLoadAsync() {
|
||||||
const response = await fetchWithTrustedCertificatesAsync(this.url("/load/gpu"));
|
try {
|
||||||
const result = await response.text();
|
const response = await fetchWithTrustedCertificatesAsync(this.url("/load/gpu"));
|
||||||
|
const result = await response.json();
|
||||||
// response is either valid json or empty if gpu widget is disabled.
|
const data = await gpuLoadApi.parseAsync(result);
|
||||||
if (result.length === 0) return [];
|
return data.layout;
|
||||||
|
} catch {
|
||||||
const data = await gpuLoadApi.parseAsync(JSON.parse(result));
|
return [];
|
||||||
return data.layout;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getChannel() {
|
private getChannel() {
|
||||||
@@ -219,14 +219,16 @@ const internalServerInfoApi = z.object({
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
gpu: z
|
gpu: z.object({
|
||||||
.array(
|
layout: z
|
||||||
z.object({
|
.array(
|
||||||
brand: z.string(),
|
z.object({
|
||||||
model: z.string().optional(),
|
brand: z.string(),
|
||||||
}),
|
model: z.string().optional(),
|
||||||
)
|
}),
|
||||||
.default([]),
|
)
|
||||||
|
.default([]),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const cpuLoadPerCoreApiList = z.array(cpuLoadPerCoreApi);
|
const cpuLoadPerCoreApiList = z.array(cpuLoadPerCoreApi);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { getAlbumInfo, getAllAlbums, getMyUser, getServerStatistics, init, searchUsers } from "@immich/sdk";
|
import { getAlbumInfo, getAllAlbums, getMyUser, getServerStatistics, init, searchUsers } from "@immich/sdk";
|
||||||
|
|
||||||
|
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
||||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||||
import { ImageProxy } from "@homarr/image-proxy";
|
import { ImageProxy } from "@homarr/image-proxy";
|
||||||
|
|
||||||
@@ -41,7 +42,10 @@ export class ImmichIntegration extends Integration {
|
|||||||
|
|
||||||
public async getServerStatsAsync(): Promise<ImmichServerStats> {
|
public async getServerStatsAsync(): Promise<ImmichServerStats> {
|
||||||
this.initClient();
|
this.initClient();
|
||||||
const [statistics, users] = await Promise.all([getServerStatistics(), searchUsers()]);
|
const [statistics, users] = await Promise.all([
|
||||||
|
getServerStatistics(this.getRequestOptions()),
|
||||||
|
searchUsers(this.getRequestOptions()),
|
||||||
|
]);
|
||||||
return {
|
return {
|
||||||
photoCount: statistics.photos,
|
photoCount: statistics.photos,
|
||||||
userCount: users.length,
|
userCount: users.length,
|
||||||
@@ -52,7 +56,7 @@ export class ImmichIntegration extends Integration {
|
|||||||
|
|
||||||
public async getAlbumAsync(albumId: string): Promise<ImmichAlbum> {
|
public async getAlbumAsync(albumId: string): Promise<ImmichAlbum> {
|
||||||
this.initClient();
|
this.initClient();
|
||||||
const album = await getAlbumInfo({ id: albumId });
|
const album = await getAlbumInfo({ id: albumId }, this.getRequestOptions());
|
||||||
const imageProxy = new ImageProxy();
|
const imageProxy = new ImageProxy();
|
||||||
return {
|
return {
|
||||||
albumName: album.albumName,
|
albumName: album.albumName,
|
||||||
@@ -89,17 +93,24 @@ export class ImmichIntegration extends Integration {
|
|||||||
}[]
|
}[]
|
||||||
> {
|
> {
|
||||||
this.initClient();
|
this.initClient();
|
||||||
const albums = await getAllAlbums({});
|
const albums = await getAllAlbums({}, this.getRequestOptions());
|
||||||
return albums.map((album) => ({ id: album.id, albumName: album.albumName, assetCount: album.assetCount }));
|
return albums.map((album) => ({ id: album.id, albumName: album.albumName, assetCount: album.assetCount }));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async testingAsync(_: IntegrationTestingInput): Promise<TestingResult> {
|
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
||||||
this.initClient();
|
this.initClient();
|
||||||
const user = await getMyUser();
|
const user = await getMyUser(this.getRequestOptions(input.fetchAsync));
|
||||||
logger.debug(`Logged in as ${user.name} (${user.id})`);
|
logger.debug(`Logged in as ${user.name} (${user.id})`);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getRequestOptions(fetchAsync = fetchWithTrustedCertificatesAsync) {
|
||||||
|
return {
|
||||||
|
// Undici and node types are not the same
|
||||||
|
fetch: fetchAsync as unknown as typeof fetch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the credentials and prepares the client for calls.
|
* Sets the credentials and prepares the client for calls.
|
||||||
* Must be called before any other functions.
|
* Must be called before any other functions.
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import type { Station, Timetable, TimetableOptions } from "./timetable-types";
|
||||||
|
|
||||||
|
export interface TimetableIntegration {
|
||||||
|
searchStationsAsync(query: string): Promise<Station[]>;
|
||||||
|
getTimetableAsync(options: TimetableOptions): Promise<Timetable>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
export interface Station {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const timetableOptionsSchema = z.object({
|
||||||
|
stationId: z.string(),
|
||||||
|
limit: z.number().default(10),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TimetableOptions = z.infer<typeof timetableOptionsSchema>;
|
||||||
|
|
||||||
|
export interface TimetableEntry {
|
||||||
|
timestamp: Date;
|
||||||
|
delay: number;
|
||||||
|
line: {
|
||||||
|
name: string;
|
||||||
|
color: string | null;
|
||||||
|
} | null;
|
||||||
|
location: string;
|
||||||
|
platform: {
|
||||||
|
name: string;
|
||||||
|
hasChanged: boolean;
|
||||||
|
} | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Timetable {
|
||||||
|
stationId: string;
|
||||||
|
timestamp: Date;
|
||||||
|
entries: TimetableEntry[];
|
||||||
|
}
|
||||||
@@ -2,8 +2,9 @@ import type { Agent } from "https";
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import timezone from "dayjs/plugin/timezone";
|
import timezone from "dayjs/plugin/timezone";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
|
import ICAL from "ical.js";
|
||||||
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
|
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
|
||||||
import * as ical from "node-ical";
|
import { tzlib_get_ical_block, tzlib_get_timezones } from "timezones-ical-library";
|
||||||
import { DAVClient } from "tsdav";
|
import { DAVClient } from "tsdav";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -21,6 +22,20 @@ import type { CalendarEvent } from "../interfaces/calendar/calendar-types";
|
|||||||
import type { Notification } from "../interfaces/notifications/notification-types";
|
import type { Notification } from "../interfaces/notifications/notification-types";
|
||||||
import type { INotificationsIntegration } from "../interfaces/notifications/notifications-integration";
|
import type { INotificationsIntegration } from "../interfaces/notifications/notifications-integration";
|
||||||
|
|
||||||
|
// Register all existing timezones
|
||||||
|
if (ICAL.TimezoneService.count === 0) {
|
||||||
|
const timezones = tzlib_get_timezones() as string[];
|
||||||
|
for (const tzid of timezones) {
|
||||||
|
ICAL.TimezoneService.register(
|
||||||
|
new ICAL.Timezone({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-non-null-assertion
|
||||||
|
component: new ICAL.Component(ICAL.parse((tzlib_get_ical_block(tzid) as string[])[0]!)),
|
||||||
|
tzid,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const notificationSchema = z.object({
|
const notificationSchema = z.object({
|
||||||
ocs: z.object({
|
ocs: z.object({
|
||||||
data: z.array(
|
data: z.array(
|
||||||
@@ -68,42 +83,64 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr
|
|||||||
|
|
||||||
return calendarEvents
|
return calendarEvents
|
||||||
.map((event) => {
|
.map((event) => {
|
||||||
// @ts-expect-error the typescript definitions for this package are wrong
|
if (typeof event.data !== "string") {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
return [];
|
||||||
const icalData = ical.default.parseICS(event.data) as ical.CalendarResponse;
|
|
||||||
const veventObject = Object.values(icalData).find((data) => data.type === "VEVENT");
|
|
||||||
|
|
||||||
if (!veventObject) {
|
|
||||||
throw new Error(`Invalid event data object: ${JSON.stringify(event.data)}. Unable to process the calendar.`);
|
|
||||||
}
|
}
|
||||||
|
const comp = ICAL.Component.fromString(event.data);
|
||||||
|
const veventComponent = comp.getFirstSubcomponent("vevent");
|
||||||
|
|
||||||
|
if (!veventComponent) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const veventObject = new ICAL.Event(veventComponent);
|
||||||
|
|
||||||
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
|
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
|
||||||
|
|
||||||
const eventUrlWithoutHost = new URL(event.url).pathname;
|
const eventUrlWithoutHost = new URL(event.url).pathname;
|
||||||
const eventSlug = Buffer.from(eventUrlWithoutHost).toString("base64url");
|
const eventSlug = Buffer.from(eventUrlWithoutHost).toString("base64url");
|
||||||
|
|
||||||
const startDates = veventObject.rrule ? veventObject.rrule.between(start, end) : [veventObject.start];
|
const durationMs = veventObject.duration.toSeconds() * 1000;
|
||||||
|
|
||||||
const durationMs = veventObject.end.getTime() - veventObject.start.getTime();
|
// Handle recurring events
|
||||||
|
let startDates: Date[];
|
||||||
|
if (veventObject.isRecurring()) {
|
||||||
|
const iterator = veventObject.iterator();
|
||||||
|
startDates = [];
|
||||||
|
|
||||||
|
// Iterate through occurrences within the time range
|
||||||
|
let next: ICAL.Time | undefined;
|
||||||
|
// next actually returns undefined when there are no more occurrences
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
while ((next = iterator.next())) {
|
||||||
|
const nextStartDate = next.convertToZone(ICAL.Timezone.utcTimezone).toJSDate();
|
||||||
|
if (nextStartDate > end) break;
|
||||||
|
const nextEndDate = new Date(nextStartDate.getTime() + durationMs);
|
||||||
|
if (nextEndDate < start) continue;
|
||||||
|
|
||||||
|
startDates.push(nextStartDate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startDates = [veventObject.startDate.convertToZone(ICAL.Timezone.utcTimezone).toJSDate()];
|
||||||
|
}
|
||||||
|
|
||||||
return startDates.map((startDate) => {
|
return startDates.map((startDate) => {
|
||||||
const timezoneOffsetMinutes = veventObject.rrule?.origOptions.tzid
|
const endDate = new Date(startDate.getTime() + durationMs);
|
||||||
? dayjs(startDate).tz(veventObject.rrule.origOptions.tzid).utcOffset()
|
const dateInMillis = startDate.valueOf();
|
||||||
: 0;
|
|
||||||
const utcStartDate = new Date(startDate.getTime() - timezoneOffsetMinutes * 60 * 1000);
|
// Get color property from the component
|
||||||
const endDate = new Date(utcStartDate.getTime() + durationMs);
|
const colorProperty = veventComponent.getFirstProperty("color");
|
||||||
const dateInMillis = utcStartDate.valueOf();
|
const color = colorProperty?.getFirstValue() as string | undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: veventObject.summary,
|
title: veventObject.summary,
|
||||||
subTitle: null,
|
subTitle: null,
|
||||||
description: veventObject.description,
|
description: veventObject.description || null,
|
||||||
startDate: utcStartDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
image: null,
|
image: null,
|
||||||
location: veventObject.location || null,
|
location: veventObject.location || null,
|
||||||
indicatorColor:
|
indicatorColor: color ?? "#ff8600",
|
||||||
"color" in veventObject && typeof veventObject.color === "string" ? veventObject.color : "#ff8600",
|
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
href: this.externalUrl(
|
href: this.externalUrl(
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { ResponseError } from "@homarr/common/server";
|
||||||
|
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
||||||
|
|
||||||
|
import type { IntegrationTestingInput } from "../base/integration";
|
||||||
|
import { Integration } from "../base/integration";
|
||||||
|
import { TestConnectionError } from "../base/test-connection/test-connection-error";
|
||||||
|
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||||
|
import type { TimetableIntegration } from "../interfaces/timetable/timetable-integration";
|
||||||
|
import type { Station, Timetable, TimetableOptions } from "../interfaces/timetable/timetable-types";
|
||||||
|
|
||||||
|
const supportedStationTypes = ["bus", "tram", "train", "ship", "cablecar", "funicular", "chairlift"];
|
||||||
|
|
||||||
|
export class SearchChIntegration extends Integration implements TimetableIntegration {
|
||||||
|
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
||||||
|
const response = await input.fetchAsync(this.url("/timetable/api/completion.json", { term: "Zürich" }));
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return TestConnectionError.StatusResult(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for stations matching the given query.
|
||||||
|
* @param query The search query for station names.
|
||||||
|
* @returns A list of stations matching the search query.
|
||||||
|
* @docs https://search.ch/fahrplan/api/help
|
||||||
|
*/
|
||||||
|
public async searchStationsAsync(query: string): Promise<Station[]> {
|
||||||
|
const response = await fetchWithTrustedCertificatesAsync(
|
||||||
|
this.url("/timetable/api/completion.json", {
|
||||||
|
term: query,
|
||||||
|
show_ids: 1,
|
||||||
|
nofavorites: 1,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new ResponseError(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await searchSchema.parseAsync(await response.json());
|
||||||
|
|
||||||
|
return (
|
||||||
|
data
|
||||||
|
// Remove completions like addresses, we only want stations.
|
||||||
|
.filter((item) => supportedStationTypes.some((type) => item.iconclass.endsWith(type)))
|
||||||
|
.map((item) => (item.id !== undefined ? { id: item.id, name: item.label } : null))
|
||||||
|
.filter((item) => item !== null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the departure timetable for a given station.
|
||||||
|
* @param options Timetable options including station ID and limit.
|
||||||
|
* @returns timetable for the given station.
|
||||||
|
* @docs https://search.ch/fahrplan/api/help
|
||||||
|
*/
|
||||||
|
public async getTimetableAsync(options: TimetableOptions): Promise<Timetable> {
|
||||||
|
const now = new Date();
|
||||||
|
// The following format is used [01.01.2000, 23:59:00]. The API allows usage of DD.MM.YYYY format and HH:mm format
|
||||||
|
const [date, time] = now.toLocaleString("en-CH", { timeZone: "Europe/Zurich" }).split(", ") as [string, string];
|
||||||
|
|
||||||
|
const response = await fetchWithTrustedCertificatesAsync(
|
||||||
|
this.url("/timetable/api/stationboard.json", {
|
||||||
|
stop: options.stationId,
|
||||||
|
limit: options.limit,
|
||||||
|
show_delays: 1,
|
||||||
|
show_tracks: 1,
|
||||||
|
date,
|
||||||
|
time: time.substring(0, 5), // Only keep the HH:mm part of the time, as that's what the API expects
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new ResponseError(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await timetableSchema.parseAsync(await response.json());
|
||||||
|
|
||||||
|
return {
|
||||||
|
stationId: options.stationId,
|
||||||
|
timestamp: now,
|
||||||
|
entries: data.connections.map((connection) => {
|
||||||
|
// Color is in format A35~F10~ for foreground and background color. Some only have ~~ and therefore no color
|
||||||
|
const color = connection.color.split("~")[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
timestamp: connection.time,
|
||||||
|
line: connection.line
|
||||||
|
? {
|
||||||
|
name: connection.line,
|
||||||
|
color: color && color.length >= 1 ? `#${color}` : null,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
location: connection.terminal.name,
|
||||||
|
delay: connection.dep_delay,
|
||||||
|
platform: connection.track
|
||||||
|
? {
|
||||||
|
// If the track name contains "!", it indicates a change in the track
|
||||||
|
// so we remove the "!" from the name and set hasChanged to true
|
||||||
|
name: connection.track.replace("!", ""),
|
||||||
|
hasChanged: connection.track.includes("!"),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timetableSchema = z.object({
|
||||||
|
connections: z.array(
|
||||||
|
z.object({
|
||||||
|
time: z.string().transform((str) => new Date(str)),
|
||||||
|
line: z.string().optional(),
|
||||||
|
color: z.string(),
|
||||||
|
terminal: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
track: z.string().optional(),
|
||||||
|
dep_delay: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((str) => (str ? parseInt(str) : 0)),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchSchema = z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string().optional(),
|
||||||
|
label: z.string(),
|
||||||
|
iconclass: z.string(), // This is in format sl-icon-type-{type}
|
||||||
|
}),
|
||||||
|
);
|
||||||
@@ -12,4 +12,5 @@ export * from "./unifi-controller/unifi-controller-types";
|
|||||||
export * from "./opnsense/opnsense-types";
|
export * from "./opnsense/opnsense-types";
|
||||||
export * from "./interfaces/media-releases";
|
export * from "./interfaces/media-releases";
|
||||||
export * from "./coolify/coolify-types";
|
export * from "./coolify/coolify-types";
|
||||||
|
export * from "./interfaces/timetable/timetable-types";
|
||||||
export * from "./tracearr/tracearr-types";
|
export * from "./tracearr/tracearr-types";
|
||||||
@@ -0,0 +1,454 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
|
import type { StartedTestContainer } from "testcontainers";
|
||||||
|
import { GenericContainer, getContainerRuntimeClient, ImageName, Wait } from "testcontainers";
|
||||||
|
import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
|
import { createId } from "@homarr/common";
|
||||||
|
import { createDb } from "@homarr/db/test";
|
||||||
|
|
||||||
|
import { NextcloudIntegration } from "../src";
|
||||||
|
|
||||||
|
// Mock the database
|
||||||
|
vi.mock("@homarr/db", async (importActual) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||||
|
const actual = await importActual<typeof import("@homarr/db")>();
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
db: createDb(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock certificate verification
|
||||||
|
vi.mock("@homarr/core/infrastructure/certificates", async (importActual) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||||
|
const actual = await importActual<typeof import("@homarr/core/infrastructure/certificates")>();
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
getTrustedCertificateHostnamesAsync: vi.fn().mockImplementation(() => {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const IMAGE_NAME = "nextcloud:latest";
|
||||||
|
const DEFAULT_USERNAME = "admin";
|
||||||
|
const DEFAULT_PASSWORD = "admin123";
|
||||||
|
|
||||||
|
interface CalendarTestEvent {
|
||||||
|
summary: string;
|
||||||
|
description?: string;
|
||||||
|
location?: string;
|
||||||
|
color?: string;
|
||||||
|
dtstart: string;
|
||||||
|
dtend: string;
|
||||||
|
rrule?: string;
|
||||||
|
timezone?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Nextcloud integration", () => {
|
||||||
|
let startedContainer: StartedTestContainer | null = null;
|
||||||
|
let nextcloudIntegration: NextcloudIntegration | null = null;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const containerRuntimeClient = await getContainerRuntimeClient();
|
||||||
|
await containerRuntimeClient.image.pull(ImageName.fromString(IMAGE_NAME));
|
||||||
|
|
||||||
|
startedContainer = await prepareNextcloudContainerAsync();
|
||||||
|
nextcloudIntegration = createNextcloudIntegration(startedContainer);
|
||||||
|
}, 100_000);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
if (startedContainer) {
|
||||||
|
await startedContainer.stop();
|
||||||
|
startedContainer = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test connection", () => {
|
||||||
|
test("Test connection > Should work with valid credentials", async () => {
|
||||||
|
// Act
|
||||||
|
const result = await nextcloudIntegration!.testConnectionAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
}, 20_000);
|
||||||
|
|
||||||
|
test("Test connection > Should fail with invalid credentials", async () => {
|
||||||
|
// Arrange
|
||||||
|
const invalidIntegration = createNextcloudIntegration(startedContainer!, "wrong-user", "wrong-pass");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await invalidIntegration.testConnectionAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
}, 20_000);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Retrieve calendar events", () => {
|
||||||
|
test("Retrieve calendar events > Should retrieve all fields correctly", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Test Meeting",
|
||||||
|
description: "This is a test meeting",
|
||||||
|
location: "Conference Room A",
|
||||||
|
color: "#FF5733",
|
||||||
|
dtstart: "20260415T140000Z",
|
||||||
|
dtend: "20260415T150000Z",
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(1);
|
||||||
|
const retrievedEvent = relevantEvents[0];
|
||||||
|
expect(retrievedEvent).toMatchObject({
|
||||||
|
title: testEvent.summary,
|
||||||
|
description: testEvent.description,
|
||||||
|
location: testEvent.location,
|
||||||
|
indicatorColor: testEvent.color,
|
||||||
|
});
|
||||||
|
expect(retrievedEvent!.startDate.toISOString()).toBe("2026-04-15T14:00:00.000Z");
|
||||||
|
expect(retrievedEvent!.endDate?.toISOString()).toBe("2026-04-15T15:00:00.000Z");
|
||||||
|
}, 20_000);
|
||||||
|
test("Retrieve calendar events > Should work with only required fields", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Simple Event",
|
||||||
|
dtstart: "20260415T140000Z",
|
||||||
|
dtend: "20260415T150000Z",
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(1);
|
||||||
|
const retrievedEvent = relevantEvents[0];
|
||||||
|
expect(retrievedEvent?.location).toBeNull();
|
||||||
|
expect(retrievedEvent?.description).toBeNull();
|
||||||
|
expect(retrievedEvent?.indicatorColor).toBe("#ff8600"); // Default color
|
||||||
|
}, 20_000);
|
||||||
|
test("Retrieve calendar events > Should work with timezone specified", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Timezone Event",
|
||||||
|
dtstart: "20260415T140000",
|
||||||
|
dtend: "20260415T150000",
|
||||||
|
timezone: "Asia/Tokyo",
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(1);
|
||||||
|
const retrievedEvent = relevantEvents[0];
|
||||||
|
|
||||||
|
expect(retrievedEvent!.startDate.toISOString()).toBe("2026-04-15T05:00:00.000Z"); // 14:00 in Tokyo is 05:00 UTC
|
||||||
|
expect(retrievedEvent!.endDate?.toISOString()).toBe("2026-04-15T06:00:00.000Z"); // 15:00 in Tokyo is 06:00 UTC
|
||||||
|
}, 120_000);
|
||||||
|
test("Retrieve calendar events > Should work with event that starts before the range but ends within the range", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Early Meeting",
|
||||||
|
dtstart: "20260401T230000Z", // Starts before the range
|
||||||
|
dtend: "20260402T010000Z", // Ends within the range
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-02T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(1);
|
||||||
|
}, 20_000);
|
||||||
|
test("Retrieve calendar events > Should work with event that starts within the range but ends after the range", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Late Meeting",
|
||||||
|
dtstart: "20260430T230000Z", // Starts within the range
|
||||||
|
dtend: "20260501T010000Z", // Ends after the range
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(1);
|
||||||
|
}, 20_000);
|
||||||
|
test("Retrieve calendar events > Should not retrieve events outside the range", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Out of Range Meeting",
|
||||||
|
dtstart: "20260301T140000Z", // Starts before the range
|
||||||
|
dtend: "20260301T150000Z", // Ends before the range
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(0);
|
||||||
|
}, 20_000);
|
||||||
|
test("Retrieve calendar events > Should work with events that begin before the range and end after the range", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Spanning Meeting",
|
||||||
|
dtstart: "20260330T230000Z", // Starts before the range
|
||||||
|
dtend: "20260501T010000Z", // Ends after the range
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(1);
|
||||||
|
}, 20_000);
|
||||||
|
test("Retrieve calendar events > Should work with recurring events first occurrence before the range", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Recurring Meeting",
|
||||||
|
dtstart: "20260330T140000Z", // First occurrence starts before the range
|
||||||
|
dtend: "20260330T150000Z",
|
||||||
|
rrule: "FREQ=DAILY;COUNT=5", // Daily for 5 occurrences
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(3);
|
||||||
|
}, 20_000);
|
||||||
|
test("Retrieve calendar events > Should work with recurring events that go beyond the range", async () => {
|
||||||
|
// Arrange
|
||||||
|
const calendarName = createId();
|
||||||
|
const testEvent: CalendarTestEvent = {
|
||||||
|
summary: "Working Week",
|
||||||
|
dtstart: "20260302T090000Z",
|
||||||
|
dtend: "20260306T170000Z",
|
||||||
|
rrule: "FREQ=WEEKLY;COUNT=10",
|
||||||
|
};
|
||||||
|
const start = new Date("2026-04-01T00:00:00Z");
|
||||||
|
const end = new Date("2026-04-30T23:59:59Z");
|
||||||
|
|
||||||
|
await createCalendarAsync(startedContainer!, calendarName);
|
||||||
|
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
|
||||||
|
expect(relevantEvents).toHaveLength(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares a Nextcloud container with proper configuration
|
||||||
|
*/
|
||||||
|
async function prepareNextcloudContainerAsync(): Promise<StartedTestContainer> {
|
||||||
|
const container = createNextcloudContainer();
|
||||||
|
const startedContainer = await container.start();
|
||||||
|
|
||||||
|
return startedContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Nextcloud container with proper configuration
|
||||||
|
*/
|
||||||
|
function createNextcloudContainer() {
|
||||||
|
return new GenericContainer(IMAGE_NAME)
|
||||||
|
.withEnvironment({
|
||||||
|
NEXTCLOUD_ADMIN_USER: DEFAULT_USERNAME,
|
||||||
|
NEXTCLOUD_ADMIN_PASSWORD: DEFAULT_PASSWORD,
|
||||||
|
NEXTCLOUD_TRUSTED_DOMAINS: "*",
|
||||||
|
SQLITE_DATABASE: "nextcloud", // Use SQLite for faster startup
|
||||||
|
})
|
||||||
|
.withExposedPorts(80)
|
||||||
|
.withWaitStrategy(Wait.forHttp("/status.php", 80).forStatusCode(200))
|
||||||
|
.withStartupTimeout(120_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Nextcloud integration instance
|
||||||
|
*/
|
||||||
|
function createNextcloudIntegration(
|
||||||
|
container: StartedTestContainer,
|
||||||
|
username?: string,
|
||||||
|
password?: string,
|
||||||
|
): NextcloudIntegration {
|
||||||
|
return new NextcloudIntegration({
|
||||||
|
id: "1",
|
||||||
|
decryptedSecrets: [
|
||||||
|
{
|
||||||
|
kind: "username",
|
||||||
|
value: username ?? DEFAULT_USERNAME,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "password",
|
||||||
|
value: password ?? DEFAULT_PASSWORD,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: "Nextcloud",
|
||||||
|
url: `http://${container.getHost()}:${container.getMappedPort(80)}`,
|
||||||
|
externalUrl: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a calendar in Nextcloud via OCS API
|
||||||
|
*/
|
||||||
|
async function createCalendarAsync(container: StartedTestContainer, calendarName: string): Promise<void> {
|
||||||
|
const host = container.getHost();
|
||||||
|
const port = container.getMappedPort(80);
|
||||||
|
const url = `http://${host}:${port}/remote.php/dav/calendars/${DEFAULT_USERNAME}/${calendarName}`;
|
||||||
|
|
||||||
|
const auth = Buffer.from(`${DEFAULT_USERNAME}:${DEFAULT_PASSWORD}`).toString("base64");
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "MKCALENDAR",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${auth}`,
|
||||||
|
"Content-Type": "application/xml",
|
||||||
|
},
|
||||||
|
body: `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<c:mkcalendar xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
|
||||||
|
<d:set>
|
||||||
|
<d:prop>
|
||||||
|
<d:displayname>${calendarName}</d:displayname>
|
||||||
|
</d:prop>
|
||||||
|
</d:set>
|
||||||
|
</c:mkcalendar>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok && response.status !== 201) {
|
||||||
|
throw new Error(`Failed to create calendar: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a calendar event in Nextcloud via CalDAV
|
||||||
|
*/
|
||||||
|
async function createCalendarEventAsync(
|
||||||
|
container: StartedTestContainer,
|
||||||
|
calendarName: string,
|
||||||
|
event: CalendarTestEvent,
|
||||||
|
): Promise<void> {
|
||||||
|
const host = container.getHost();
|
||||||
|
const port = container.getMappedPort(80);
|
||||||
|
const id = createId();
|
||||||
|
const url = `http://${host}:${port}/remote.php/dav/calendars/${DEFAULT_USERNAME}/${calendarName}/${id}.ics`;
|
||||||
|
|
||||||
|
const auth = Buffer.from(`${DEFAULT_USERNAME}:${DEFAULT_PASSWORD}`).toString("base64");
|
||||||
|
|
||||||
|
// Build iCalendar format
|
||||||
|
const icalData = buildICalEvent(id, event);
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${auth}`,
|
||||||
|
"Content-Type": "text/calendar",
|
||||||
|
},
|
||||||
|
body: icalData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok && response.status !== 201) {
|
||||||
|
throw new Error(`Failed to create event: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an iCalendar format string from event data
|
||||||
|
*/
|
||||||
|
function buildICalEvent(uid: string, event: CalendarTestEvent): string {
|
||||||
|
const lines = [
|
||||||
|
"BEGIN:VCALENDAR",
|
||||||
|
"VERSION:2.0",
|
||||||
|
"PRODID:-//Homarr Tests//EN",
|
||||||
|
"BEGIN:VEVENT",
|
||||||
|
`UID:${uid}`,
|
||||||
|
`DTSTAMP:${new Date().toISOString().replace(/[-:]/g, "").split(".")[0]}Z`,
|
||||||
|
`DTSTART${event.timezone ? `;TZID=${event.timezone}` : ""}:${event.dtstart}`,
|
||||||
|
`DTEND${event.timezone ? `;TZID=${event.timezone}` : ""}:${event.dtend}`,
|
||||||
|
`SUMMARY:${event.summary}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (event.description) {
|
||||||
|
lines.push(`DESCRIPTION:${event.description}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.location) {
|
||||||
|
lines.push(`LOCATION:${event.location}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.color) {
|
||||||
|
lines.push(`COLOR:${event.color}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.rrule) {
|
||||||
|
lines.push(`RRULE:${event.rrule}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push("END:VEVENT", "END:VCALENDAR");
|
||||||
|
|
||||||
|
return lines.join("\r\n");
|
||||||
|
}
|
||||||
@@ -34,12 +34,12 @@
|
|||||||
"@homarr/ui": "workspace:^0.1.0",
|
"@homarr/ui": "workspace:^0.1.0",
|
||||||
"@homarr/validation": "workspace:^0.1.0",
|
"@homarr/validation": "workspace:^0.1.0",
|
||||||
"@mantine/core": "^8.3.18",
|
"@mantine/core": "^8.3.18",
|
||||||
"@tabler/icons-react": "^3.40.0",
|
"@tabler/icons-react": "^3.41.0",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
"next": "16.2.1",
|
"next": "16.2.1",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/ui": "workspace:^0.1.0",
|
"@homarr/ui": "workspace:^0.1.0",
|
||||||
"@mantine/notifications": "^8.3.18",
|
"@mantine/notifications": "^8.3.18",
|
||||||
"@tabler/icons-react": "^3.40.0"
|
"@tabler/icons-react": "^3.41.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"zod": "^4.2.1",
|
"zod": "^4.3.6",
|
||||||
"zod-form-data": "^3.0.1"
|
"zod-form-data": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"prettier": "@homarr/prettier-config",
|
"prettier": "@homarr/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/common": "workspace:^0.1.0",
|
"@homarr/common": "workspace:^0.1.0",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -33,8 +33,8 @@
|
|||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
"octokit": "^5.0.5",
|
"octokit": "^5.0.5",
|
||||||
"superjson": "2.2.6",
|
"superjson": "2.2.6",
|
||||||
"undici": "7.24.5",
|
"undici": "7.24.6",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||||
|
import { createIntegrationAsync } from "@homarr/integrations";
|
||||||
|
import type { Station, Timetable } from "@homarr/integrations/types";
|
||||||
|
|
||||||
|
import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-request-handler";
|
||||||
|
|
||||||
|
export const timetableSearchStationsRequestHandler = createCachedIntegrationRequestHandler<
|
||||||
|
Station[],
|
||||||
|
IntegrationKindByCategory<"timetable">,
|
||||||
|
{ query: string }
|
||||||
|
>({
|
||||||
|
async requestAsync(integration, input) {
|
||||||
|
const integrationInstance = await createIntegrationAsync(integration);
|
||||||
|
return await integrationInstance.searchStationsAsync(input.query);
|
||||||
|
},
|
||||||
|
queryKey: "timetableSearchStations",
|
||||||
|
cacheDuration: dayjs.duration(1, "day"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const timetableGetTimetableRequestHandler = createCachedIntegrationRequestHandler<
|
||||||
|
Timetable,
|
||||||
|
IntegrationKindByCategory<"timetable">,
|
||||||
|
{ stationId: string; limit: number }
|
||||||
|
>({
|
||||||
|
async requestAsync(integration, input) {
|
||||||
|
const integrationInstance = await createIntegrationAsync(integration);
|
||||||
|
return await integrationInstance.getTimetableAsync({
|
||||||
|
stationId: input.stationId,
|
||||||
|
limit: input.limit,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
queryKey: "timetableGetTimetable",
|
||||||
|
cacheDuration: dayjs.duration(1, "minute"),
|
||||||
|
});
|
||||||
@@ -36,8 +36,8 @@
|
|||||||
"@mantine/core": "^8.3.18",
|
"@mantine/core": "^8.3.18",
|
||||||
"@mantine/hooks": "^8.3.18",
|
"@mantine/hooks": "^8.3.18",
|
||||||
"@mantine/spotlight": "^8.3.18",
|
"@mantine/spotlight": "^8.3.18",
|
||||||
"@tabler/icons-react": "^3.40.0",
|
"@tabler/icons-react": "^3.41.0",
|
||||||
"jotai": "^2.18.1",
|
"jotai": "^2.19.0",
|
||||||
"next": "16.2.1",
|
"next": "16.2.1",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": ""
|
"label": ""
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "允许地理位置"
|
"label": "允许地理位置"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "显示内存信息"
|
"label": "显示内存信息"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "显示运行时间"
|
"label": "显示运行时间"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "内存",
|
"memory": "内存",
|
||||||
|
"gpu": "",
|
||||||
"network": "网络"
|
"network": "网络"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "内存",
|
"memory": "内存",
|
||||||
|
"gpu": "",
|
||||||
"network": "网络",
|
"network": "网络",
|
||||||
"up": "上传",
|
"up": "上传",
|
||||||
"down": "下载"
|
"down": "下载"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "crwdns970:0crwdne970:0"
|
"label": "crwdns970:0crwdne970:0"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": "crwdns3992:0crwdne3992:0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "crwdns1080:0crwdne1080:0"
|
"label": "crwdns1080:0crwdne1080:0"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": "crwdns3994:0crwdne3994:0"
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "crwdns3446:0crwdne3446:0"
|
"label": "crwdns3446:0crwdne3446:0"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "crwdns3700:0crwdne3700:0",
|
"cpu": "crwdns3700:0crwdne3700:0",
|
||||||
"memory": "crwdns3702:0crwdne3702:0",
|
"memory": "crwdns3702:0crwdne3702:0",
|
||||||
|
"gpu": "crwdns3996:0crwdne3996:0",
|
||||||
"network": "crwdns3704:0crwdne3704:0"
|
"network": "crwdns3704:0crwdne3704:0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "crwdns3604:0crwdne3604:0",
|
"cpu": "crwdns3604:0crwdne3604:0",
|
||||||
"memory": "crwdns3606:0crwdne3606:0",
|
"memory": "crwdns3606:0crwdne3606:0",
|
||||||
|
"gpu": "crwdns3998:0crwdne3998:0",
|
||||||
"network": "crwdns3608:0crwdne3608:0",
|
"network": "crwdns3608:0crwdne3608:0",
|
||||||
"up": "crwdns3610:0crwdne3610:0",
|
"up": "crwdns3610:0crwdne3610:0",
|
||||||
"down": "crwdns3612:0crwdne3612:0"
|
"down": "crwdns3612:0crwdne3612:0"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Povolit polohu"
|
"label": "Povolit polohu"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Zobrazit informace o paměti"
|
"label": "Zobrazit informace o paměti"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Tillad geolokalisering"
|
"label": "Tillad geolokalisering"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Vis hukommelsesoplysninger"
|
"label": "Vis hukommelsesoplysninger"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Vis Oppetid"
|
"label": "Vis Oppetid"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Hukommelse",
|
"memory": "Hukommelse",
|
||||||
|
"gpu": "",
|
||||||
"network": "Netværk"
|
"network": "Netværk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "MEM",
|
"memory": "MEM",
|
||||||
|
"gpu": "",
|
||||||
"network": "NET",
|
"network": "NET",
|
||||||
"up": "OP",
|
"up": "OP",
|
||||||
"down": "NED"
|
"down": "NED"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Geolokalisierung zulassen"
|
"label": "Geolokalisierung zulassen"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Speicher-Info anzeigen"
|
"label": "Speicher-Info anzeigen"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Geolokalisierung zulassen"
|
"label": "Geolokalisierung zulassen"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Speicher-Info anzeigen"
|
"label": "Speicher-Info anzeigen"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Laufzeit anzeigen"
|
"label": "Laufzeit anzeigen"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Speicher",
|
"memory": "Speicher",
|
||||||
|
"gpu": "",
|
||||||
"network": "Netzwerk"
|
"network": "Netzwerk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Arbeitsspeicher",
|
"memory": "Arbeitsspeicher",
|
||||||
|
"gpu": "",
|
||||||
"network": "Netzwerk",
|
"network": "Netzwerk",
|
||||||
"up": "OBEN",
|
"up": "OBEN",
|
||||||
"down": "UNTEN"
|
"down": "UNTEN"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Επιτρέπεται ο γεωεντοπισμός"
|
"label": "Επιτρέπεται ο γεωεντοπισμός"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Εμφάνιση Πληροφοριών Μνήμης"
|
"label": "Εμφάνιση Πληροφοριών Μνήμης"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1125,17 +1125,17 @@
|
|||||||
"userAvatar": {
|
"userAvatar": {
|
||||||
"menu": {
|
"menu": {
|
||||||
"switchToDarkMode": "Switch to dark mode",
|
"switchToDarkMode": "Switch to dark mode",
|
||||||
"switchToLightMode": "Switch to light mode",
|
"switchToLightMode": "التبديل إلى الوضع الفاتح",
|
||||||
"management": "Management",
|
"management": "الإدارة",
|
||||||
"preferences": "Your preferences",
|
"preferences": "تفضيلاتك",
|
||||||
"logout": "Log-out",
|
"logout": "تسجيل الخروج",
|
||||||
"login": "Log-in",
|
"login": "Log-in",
|
||||||
"homeBoard": "Your home board",
|
"homeBoard": "لوحة منزلك",
|
||||||
"loggedOut": "Logged out",
|
"loggedOut": "Logged out",
|
||||||
"updateAvailable": "New update available: {tag}"
|
"updateAvailable": "{countUpdates} يتوفر تحديث جديد: {tag}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dangerZone": "Danger zone",
|
"dangerZone": "المنطقة الخطرة",
|
||||||
"noResults": "No results have been found",
|
"noResults": "No results have been found",
|
||||||
"unsavedChanges": "You have unsaved changes!",
|
"unsavedChanges": "You have unsaved changes!",
|
||||||
"servers": "Servers",
|
"servers": "Servers",
|
||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Allow geolocation"
|
"label": "Allow geolocation"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": "Allow modals"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1856,107 +1859,110 @@
|
|||||||
"label": "Show CPU Info"
|
"label": "Show CPU Info"
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": "Show Memory Info"
|
||||||
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": "Show GPU Info"
|
||||||
},
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": "Show Uptime"
|
||||||
},
|
},
|
||||||
"fileSystem": {
|
"fileSystem": {
|
||||||
"label": ""
|
"label": "Show Filesystem Info"
|
||||||
},
|
},
|
||||||
"defaultTab": {
|
"defaultTab": {
|
||||||
"label": ""
|
"label": "Default tab"
|
||||||
},
|
},
|
||||||
"visibleClusterSections": {
|
"visibleClusterSections": {
|
||||||
"label": ""
|
"label": "Visible cluster sections"
|
||||||
},
|
},
|
||||||
"sectionIndicatorRequirement": {
|
"sectionIndicatorRequirement": {
|
||||||
"label": ""
|
"label": "Section indicator requirement"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"popover": {
|
"popover": {
|
||||||
"information": "",
|
"information": "Information",
|
||||||
"processor": "",
|
"processor": "Processor: {cpuModelName}",
|
||||||
"memory": "",
|
"memory": "Memory: {memory}GiB",
|
||||||
"memoryAvailable": "",
|
"memoryAvailable": "Available: {memoryAvailable}GiB ({percent}%)",
|
||||||
"version": "",
|
"version": "Version: {version}",
|
||||||
"uptime": "",
|
"uptime": "Uptime: {months} Months, {days} Days, {hours} Hours, {minutes} Minutes",
|
||||||
"loadAverage": "",
|
"loadAverage": "Load average:",
|
||||||
"minute": "",
|
"minute": "1 minute",
|
||||||
"minutes": "",
|
"minutes": "{count} minutes",
|
||||||
"used": "",
|
"used": "Used",
|
||||||
"available": "",
|
"available": "Available",
|
||||||
"lastSeen": ""
|
"lastSeen": "Last status update: {lastSeen}"
|
||||||
},
|
},
|
||||||
"memory": {},
|
"memory": {},
|
||||||
"error": {
|
"error": {
|
||||||
"internalServerError": ""
|
"internalServerError": "Failed to fetch health status"
|
||||||
},
|
},
|
||||||
"cluster": {
|
"cluster": {
|
||||||
"summary": {
|
"summary": {
|
||||||
"cpu": "",
|
"cpu": "CPU",
|
||||||
"memory": ""
|
"memory": "RAM"
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
"node": {
|
"node": {
|
||||||
"name": ""
|
"name": "Nodes"
|
||||||
},
|
},
|
||||||
"qemu": {
|
"qemu": {
|
||||||
"name": ""
|
"name": "VMs"
|
||||||
},
|
},
|
||||||
"lxc": {
|
"lxc": {
|
||||||
"name": ""
|
"name": "LXCs"
|
||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"name": ""
|
"name": "Storage"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"popover": {
|
"popover": {
|
||||||
"rightSection": {
|
"rightSection": {
|
||||||
"node": "",
|
"node": "Node",
|
||||||
"vmId": "",
|
"vmId": "VM ID",
|
||||||
"plugin": ""
|
"plugin": "Plugin"
|
||||||
},
|
},
|
||||||
"detail": {
|
"detail": {
|
||||||
"cpu": "",
|
"cpu": "Cores",
|
||||||
"memory": "",
|
"memory": "Memory",
|
||||||
"storage": "",
|
"storage": "Storage",
|
||||||
"uptime": "",
|
"uptime": "Uptime",
|
||||||
"haState": "",
|
"haState": "HA State",
|
||||||
"storageType": {
|
"storageType": {
|
||||||
"local": "",
|
"local": "Local storage",
|
||||||
"shared": ""
|
"shared": "Shared storage"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"header": {
|
"header": {
|
||||||
"name": "",
|
"name": "Name",
|
||||||
"cpu": "",
|
"cpu": "CPU",
|
||||||
"memory": "",
|
"memory": "RAM",
|
||||||
"node": ""
|
"node": "Node"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dockerContainers": {
|
"dockerContainers": {
|
||||||
"name": "",
|
"name": "Docker stats",
|
||||||
"description": "",
|
"description": "Stats of your containers (This widget can only be added with administrator privileges)",
|
||||||
"option": {
|
"option": {
|
||||||
"enableRowSorting": {
|
"enableRowSorting": {
|
||||||
"label": ""
|
"label": "Enable items sorting"
|
||||||
},
|
},
|
||||||
"defaultSort": {
|
"defaultSort": {
|
||||||
"label": "",
|
"label": "Column used for sorting by default",
|
||||||
"option": {
|
"option": {
|
||||||
"name": "",
|
"name": "Name",
|
||||||
"state": "",
|
"state": "State",
|
||||||
"cpuUsage": "",
|
"cpuUsage": "CPU usage",
|
||||||
"memoryUsage": ""
|
"memoryUsage": "Memory usage"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"descendingDefaultSort": {
|
"descendingDefaultSort": {
|
||||||
"label": ""
|
"label": "Invert sorting"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
@@ -3149,98 +3157,98 @@
|
|||||||
},
|
},
|
||||||
"navbar": {
|
"navbar": {
|
||||||
"items": {
|
"items": {
|
||||||
"home": "",
|
"home": "الصفحة الرئسية",
|
||||||
"boards": "",
|
"boards": "الألواح",
|
||||||
"apps": "",
|
"apps": "التطبيقات",
|
||||||
"integrations": "",
|
"integrations": "التكاملات",
|
||||||
"searchEngies": "",
|
"searchEngies": "محركات البحث",
|
||||||
"medias": "",
|
"medias": "الوسائط",
|
||||||
"users": {
|
"users": {
|
||||||
"label": "",
|
"label": "المستخدمين",
|
||||||
"items": {
|
"items": {
|
||||||
"manage": "",
|
"manage": "إدارة المستخدمين",
|
||||||
"invites": "",
|
"invites": "الدعوات",
|
||||||
"groups": ""
|
"groups": "المجموعات"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
"label": "",
|
"label": "الأدوات",
|
||||||
"items": {
|
"items": {
|
||||||
"docker": "",
|
"docker": "الحاويات",
|
||||||
"kubernetes": "",
|
"kubernetes": "",
|
||||||
"logs": "",
|
"logs": "السجلات",
|
||||||
"api": "",
|
"api": "API",
|
||||||
"certificates": "",
|
"certificates": "الشهادات",
|
||||||
"tasks": ""
|
"tasks": "المهام"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings": "",
|
"settings": "الإعدادات",
|
||||||
"help": {
|
"help": {
|
||||||
"label": "",
|
"label": "المساعدة",
|
||||||
"items": {
|
"items": {
|
||||||
"documentation": "",
|
"documentation": "المستندات",
|
||||||
"submitIssue": "",
|
"submitIssue": "إبلاغ عن مشكلة",
|
||||||
"discord": "",
|
"discord": "مجتمع ديسكورد",
|
||||||
"sourceCode": ""
|
"sourceCode": "كود المصدر"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"about": ""
|
"about": "حول"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
"home": {
|
"home": {
|
||||||
"statistic": {
|
"statistic": {
|
||||||
"board": "",
|
"board": "الألواح",
|
||||||
"user": "",
|
"user": "مستخدمين",
|
||||||
"invite": "",
|
"invite": "دعوات",
|
||||||
"integration": "",
|
"integration": "التكاملات",
|
||||||
"app": "",
|
"app": "البرامج",
|
||||||
"group": "",
|
"group": "المجموعات",
|
||||||
"searchEngine": "",
|
"searchEngine": "محركات البحث",
|
||||||
"media": ""
|
"media": "الوسائط"
|
||||||
},
|
},
|
||||||
"statisticLabel": {
|
"statisticLabel": {
|
||||||
"boards": "",
|
"boards": "اللوحات",
|
||||||
"resources": "",
|
"resources": "الموارد",
|
||||||
"authentication": "",
|
"authentication": "المصادقة",
|
||||||
"authorization": ""
|
"authorization": "الأذونات"
|
||||||
},
|
},
|
||||||
"heroBanner": {
|
"heroBanner": {
|
||||||
"title": "",
|
"title": "مرحبًا بعودتك إلى",
|
||||||
"subtitle": ""
|
"subtitle": "{app} اللوحات"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"board": {
|
"board": {
|
||||||
"title": "",
|
"title": "لوحاتك",
|
||||||
"action": {
|
"action": {
|
||||||
"new": {
|
"new": {
|
||||||
"label": ""
|
"label": "لوحة جديدة"
|
||||||
},
|
},
|
||||||
"open": {
|
"open": {
|
||||||
"label": ""
|
"label": "فتح اللوحة"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"label": ""
|
"label": "الإعدادات"
|
||||||
},
|
},
|
||||||
"setHomeBoard": {
|
"setHomeBoard": {
|
||||||
"label": "",
|
"label": "تعيينه كلوحة رئيسية",
|
||||||
"badge": {
|
"badge": {
|
||||||
"label": "",
|
"label": "الرئيسية",
|
||||||
"tooltip": ""
|
"tooltip": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setMobileHomeBoard": {
|
"setMobileHomeBoard": {
|
||||||
"label": "",
|
"label": "تعيينه كصفحتك الرئيسية للجوال",
|
||||||
"badge": {
|
"badge": {
|
||||||
"label": "",
|
"label": "الجوال",
|
||||||
"tooltip": ""
|
"tooltip": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"duplicate": {
|
"duplicate": {
|
||||||
"label": ""
|
"label": "تكرار اللوحة"
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"label": "",
|
"label": "حذف نهائيًا",
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "",
|
"title": "",
|
||||||
"description": ""
|
"description": ""
|
||||||
@@ -3329,10 +3337,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invite": {
|
"invite": {
|
||||||
"title": "",
|
"title": "إدارة دعوات المستخدمين",
|
||||||
"action": {
|
"action": {
|
||||||
"new": {
|
"new": {
|
||||||
"title": "",
|
"title": "دعوة جديدة",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
"copy": {
|
"copy": {
|
||||||
@@ -3348,13 +3356,13 @@
|
|||||||
},
|
},
|
||||||
"field": {
|
"field": {
|
||||||
"id": {
|
"id": {
|
||||||
"label": ""
|
"label": "المعرّف"
|
||||||
},
|
},
|
||||||
"creator": {
|
"creator": {
|
||||||
"label": ""
|
"label": "المنشئ"
|
||||||
},
|
},
|
||||||
"expirationDate": {
|
"expirationDate": {
|
||||||
"label": ""
|
"label": "انتهاء الصلاحية"
|
||||||
},
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"label": ""
|
"label": ""
|
||||||
@@ -4121,7 +4129,7 @@
|
|||||||
"manage": {
|
"manage": {
|
||||||
"label": "",
|
"label": "",
|
||||||
"boards": {
|
"boards": {
|
||||||
"label": ""
|
"label": "الألواح"
|
||||||
},
|
},
|
||||||
"integrations": {
|
"integrations": {
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -4154,7 +4162,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"label": "",
|
"label": "المستخدمين",
|
||||||
"create": {
|
"create": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -4165,7 +4173,7 @@
|
|||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"invites": {
|
"invites": {
|
||||||
"label": ""
|
"label": "الدعوات"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
@@ -4222,7 +4230,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"placeholder": "",
|
"placeholder": "البحث عن",
|
||||||
"nothingFound": "",
|
"nothingFound": "",
|
||||||
"error": {
|
"error": {
|
||||||
"fetch": ""
|
"fetch": ""
|
||||||
@@ -4382,13 +4390,13 @@
|
|||||||
"title": "",
|
"title": "",
|
||||||
"option": {
|
"option": {
|
||||||
"documentation": {
|
"documentation": {
|
||||||
"label": ""
|
"label": "Documentation"
|
||||||
},
|
},
|
||||||
"submitIssue": {
|
"submitIssue": {
|
||||||
"label": ""
|
"label": "Submit an issue"
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"label": ""
|
"label": "Community Discord"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4397,20 +4405,20 @@
|
|||||||
"home": {
|
"home": {
|
||||||
"group": {
|
"group": {
|
||||||
"search": {
|
"search": {
|
||||||
"title": "",
|
"title": "Search",
|
||||||
"option": {
|
"option": {
|
||||||
"other": {
|
"other": {
|
||||||
"label": ""
|
"label": "Search with another search engine"
|
||||||
},
|
},
|
||||||
"no-default": {
|
"no-default": {
|
||||||
"label": "",
|
"label": "No default search engine",
|
||||||
"description": ""
|
"description": "Set a default search engine in preferences"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"label": ""
|
"label": "Search for \"{query}\" with {name}"
|
||||||
},
|
},
|
||||||
"from-integration": {
|
"from-integration": {
|
||||||
"description": ""
|
"description": "Start typing to search"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1525,6 +1525,17 @@
|
|||||||
"offline": "Offline"
|
"offline": "Offline"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"timetable": {
|
||||||
|
"name": "Timetable",
|
||||||
|
"description": "Displays departure times for a station",
|
||||||
|
"title": "Departures {station}",
|
||||||
|
"noStation": "No station selected",
|
||||||
|
"option": {
|
||||||
|
"station": {
|
||||||
|
"label": "Station"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"notebook": {
|
"notebook": {
|
||||||
"name": "Notebook",
|
"name": "Notebook",
|
||||||
"description": "A simple notebook widget that supports markdown",
|
"description": "A simple notebook widget that supports markdown",
|
||||||
@@ -3513,6 +3524,9 @@
|
|||||||
"minecraftServerStatus": {
|
"minecraftServerStatus": {
|
||||||
"label": "Minecraft server status"
|
"label": "Minecraft server status"
|
||||||
},
|
},
|
||||||
|
"timetable": {
|
||||||
|
"label": "Timetable"
|
||||||
|
},
|
||||||
"iconsUpdater": {
|
"iconsUpdater": {
|
||||||
"label": "Icons Updater"
|
"label": "Icons Updater"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Permitir geolocalización"
|
"label": "Permitir geolocalización"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Mostrar información de la memoria"
|
"label": "Mostrar información de la memoria"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Mostrar tiempo de actividad"
|
"label": "Mostrar tiempo de actividad"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Memoria",
|
"memory": "Memoria",
|
||||||
|
"gpu": "",
|
||||||
"network": "Red"
|
"network": "Red"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "MEM",
|
"memory": "MEM",
|
||||||
|
"gpu": "",
|
||||||
"network": "RED",
|
"network": "RED",
|
||||||
"up": "ENCENDIDO",
|
"up": "ENCENDIDO",
|
||||||
"down": "APAGADO"
|
"down": "APAGADO"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": ""
|
"label": ""
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
},
|
},
|
||||||
"invite": {
|
"invite": {
|
||||||
"title": "Rejoignez Homarr",
|
"title": "Rejoignez Homarr",
|
||||||
"subtitle": "Bienvenue sur Homarr! Veuillez créer votre compte",
|
"subtitle": "Bienvenue sur Homarr ! Veuillez créer votre compte",
|
||||||
"description": "Vous avez été invité par {username}"
|
"description": "Vous avez été invité par {username}"
|
||||||
},
|
},
|
||||||
"init": {
|
"init": {
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forgotPassword": {
|
"forgotPassword": {
|
||||||
"label": "Mot de passe oublié?",
|
"label": "Mot de passe oublié ?",
|
||||||
"description": "Un administrateur peut utiliser la commande suivante pour réinitialiser votre mot de passe :"
|
"description": "Un administrateur peut utiliser la commande suivante pour réinitialiser votre mot de passe :"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -578,7 +578,7 @@
|
|||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"title": "Supprimer l'app",
|
"title": "Supprimer l'app",
|
||||||
"message": "Êtes-vous sûr de vouloir supprimer l'app {name}?",
|
"message": "Êtes-vous sûr de vouloir supprimer l'app {name} ?",
|
||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
"title": "Suppression effectuée avec succès",
|
"title": "Suppression effectuée avec succès",
|
||||||
@@ -684,7 +684,7 @@
|
|||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"title": "Supprimer l'intégration",
|
"title": "Supprimer l'intégration",
|
||||||
"message": "Êtes-vous sûr(e) de vouloir supprimer l'intégration {name}?",
|
"message": "Êtes-vous sûr(e) de vouloir supprimer l'intégration {name} ?",
|
||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
"title": "Suppression réussie",
|
"title": "Suppression réussie",
|
||||||
@@ -742,7 +742,7 @@
|
|||||||
"description": "La réponse n'a pas pu être analysée. Veuillez vérifier que l'URL pointe vers l'URL de base du service."
|
"description": "La réponse n'a pas pu être analysée. Veuillez vérifier que l'URL pointe vers l'URL de base du service."
|
||||||
},
|
},
|
||||||
"authorization": {
|
"authorization": {
|
||||||
"title": "",
|
"title": "Erreur d'autorisation",
|
||||||
"description": "La requête n'a pas été autorisée. Veuillez vérifier que les informations d'identification sont correctes et que vous les avez configurées avec suffisamment d'autorisations."
|
"description": "La requête n'a pas été autorisée. Veuillez vérifier que les informations d'identification sont correctes et que vous les avez configurées avec suffisamment d'autorisations."
|
||||||
},
|
},
|
||||||
"statusCode": {
|
"statusCode": {
|
||||||
@@ -794,7 +794,7 @@
|
|||||||
"hostnameMismatch": {
|
"hostnameMismatch": {
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "Faire confiant à l'incohérence du nom d'hôte",
|
"title": "Faire confiant à l'incohérence du nom d'hôte",
|
||||||
"message": ""
|
"message": "Êtes-vous sûr de vouloir faire confiance ce certificat avec un nom d'hôte incohérent ?"
|
||||||
},
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
@@ -1164,8 +1164,8 @@
|
|||||||
"number": "Ce champ doit être inférieur ou égal à {maximum}"
|
"number": "Ce champ doit être inférieur ou égal à {maximum}"
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"passwordsDoNotMatch": "Les mots de passe ne correspondent pas.",
|
"passwordsDoNotMatch": "Les mots de passe ne correspondent pas",
|
||||||
"passwordRequirements": "Le mot de passe ne respecte pas les exigences requises.",
|
"passwordRequirements": "Le mot de passe ne respecte pas les exigences requises",
|
||||||
"boardAlreadyExists": "Un tableau avec ce nom existe déjà",
|
"boardAlreadyExists": "Un tableau avec ce nom existe déjà",
|
||||||
"invalidFileType": "Type de fichier invalide, {expected} attendu",
|
"invalidFileType": "Type de fichier invalide, {expected} attendu",
|
||||||
"invalidFileName": "Nom de fichier invalide",
|
"invalidFileName": "Nom de fichier invalide",
|
||||||
@@ -1248,7 +1248,7 @@
|
|||||||
"action": {
|
"action": {
|
||||||
"create": "Nouvel élément",
|
"create": "Nouvel élément",
|
||||||
"import": "Importer un élément",
|
"import": "Importer un élément",
|
||||||
"edit": "Editer l'élément",
|
"edit": "Éditer l'élément",
|
||||||
"moveResize": "Déplacer / redimensionner l'élément",
|
"moveResize": "Déplacer / redimensionner l'élément",
|
||||||
"duplicate": "Dupliquer l'élément",
|
"duplicate": "Dupliquer l'élément",
|
||||||
"remove": "Supprimer l'élément"
|
"remove": "Supprimer l'élément"
|
||||||
@@ -1281,7 +1281,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
"title": "Editer l'élément",
|
"title": "Éditer l'élément",
|
||||||
"advancedOptions": {
|
"advancedOptions": {
|
||||||
"label": "Options avancées",
|
"label": "Options avancées",
|
||||||
"title": "Options avancées de l'élément"
|
"title": "Options avancées de l'élément"
|
||||||
@@ -1309,7 +1309,7 @@
|
|||||||
"widget": {
|
"widget": {
|
||||||
"app": {
|
"app": {
|
||||||
"name": "App",
|
"name": "App",
|
||||||
"description": "Intégre une application dans le tableau.",
|
"description": "Intègre une application dans le tableau.",
|
||||||
"option": {
|
"option": {
|
||||||
"appId": {
|
"appId": {
|
||||||
"label": "Choisir une application"
|
"label": "Choisir une application"
|
||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Autoriser la géolocalisation"
|
"label": "Autoriser la géolocalisation"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1634,7 +1637,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"smartHome-entityState": {
|
"smartHome-entityState": {
|
||||||
"name": "Etat de l'entité",
|
"name": "État de l'entité",
|
||||||
"description": "Affichez l'état d'une entité et optionnellement activez la",
|
"description": "Affichez l'état d'une entité et optionnellement activez la",
|
||||||
"option": {
|
"option": {
|
||||||
"entityId": {
|
"entityId": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Afficher les infos de la mémoire"
|
"label": "Afficher les infos de la mémoire"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Afficher le temps de disponibilité"
|
"label": "Afficher le temps de disponibilité"
|
||||||
},
|
},
|
||||||
@@ -1877,16 +1883,16 @@
|
|||||||
"popover": {
|
"popover": {
|
||||||
"information": "Information",
|
"information": "Information",
|
||||||
"processor": "Processeur : {cpuModelName}",
|
"processor": "Processeur : {cpuModelName}",
|
||||||
"memory": "Mémoire: {memory}GiB",
|
"memory": "Mémoire : {memory}GiB",
|
||||||
"memoryAvailable": "Disponible: {memoryAvailable}GiB ({percent}%)",
|
"memoryAvailable": "Disponible : {memoryAvailable}GiB ({percent}%)",
|
||||||
"version": "Version : {version}",
|
"version": "Version : {version}",
|
||||||
"uptime": "Disponibilité: {months} Mois, {days} Jours, {hours} Heures, {minutes} Minutes",
|
"uptime": "Disponibilité : {months} Mois, {days} Jours, {hours} Heures, {minutes} Minutes",
|
||||||
"loadAverage": "Charge moyenne:",
|
"loadAverage": "Charge moyenne :",
|
||||||
"minute": "1 minute",
|
"minute": "1 minute",
|
||||||
"minutes": "{count} minutes",
|
"minutes": "{count} minutes",
|
||||||
"used": "Utilisé ",
|
"used": "Utilisé ",
|
||||||
"available": "Disponible",
|
"available": "Disponible",
|
||||||
"lastSeen": "Dernière mise à jour de statut: {lastSeen}"
|
"lastSeen": "Dernière mise à jour de statut : {lastSeen}"
|
||||||
},
|
},
|
||||||
"memory": {},
|
"memory": {},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1922,7 +1928,7 @@
|
|||||||
"memory": "Mémoire",
|
"memory": "Mémoire",
|
||||||
"storage": "Stockage",
|
"storage": "Stockage",
|
||||||
"uptime": "Disponibilité",
|
"uptime": "Disponibilité",
|
||||||
"haState": "Etat HA",
|
"haState": "État HA",
|
||||||
"storageType": {
|
"storageType": {
|
||||||
"local": "Stockage local",
|
"local": "Stockage local",
|
||||||
"shared": "Stockage partagé"
|
"shared": "Stockage partagé"
|
||||||
@@ -2056,7 +2062,7 @@
|
|||||||
"query": "Ville / Code Postal",
|
"query": "Ville / Code Postal",
|
||||||
"latitude": "Latitude",
|
"latitude": "Latitude",
|
||||||
"longitude": "Longitude",
|
"longitude": "Longitude",
|
||||||
"disabledTooltip": "Veuillez saisir une ville ou un code postal.",
|
"disabledTooltip": "Veuillez saisir une ville ou un code postal",
|
||||||
"unknownLocation": "Lieu inconnu",
|
"unknownLocation": "Lieu inconnu",
|
||||||
"search": "Rechercher",
|
"search": "Rechercher",
|
||||||
"table": {
|
"table": {
|
||||||
@@ -2075,7 +2081,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"integration": {
|
"integration": {
|
||||||
"noData": "Aucune intégration trouvée.",
|
"noData": "Aucune intégration trouvée",
|
||||||
"description": "Cliquez sur <here></here> pour créer une nouvelle intégration"
|
"description": "Cliquez sur <here></here> pour créer une nouvelle intégration"
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
@@ -2091,7 +2097,7 @@
|
|||||||
},
|
},
|
||||||
"video": {
|
"video": {
|
||||||
"name": "Flux vidéo",
|
"name": "Flux vidéo",
|
||||||
"description": "Intégre un flux vidéo ou une vidéo provenant d'une caméra ou d'un site web",
|
"description": "Intègre un flux vidéo ou une vidéo provenant d'une caméra ou d'un site web",
|
||||||
"option": {
|
"option": {
|
||||||
"feedUrl": {
|
"feedUrl": {
|
||||||
"label": "URL du flux"
|
"label": "URL du flux"
|
||||||
@@ -2104,7 +2110,7 @@
|
|||||||
"label": "Muet"
|
"label": "Muet"
|
||||||
},
|
},
|
||||||
"hasControls": {
|
"hasControls": {
|
||||||
"label": "Afficher les controles"
|
"label": "Afficher les contrôles"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -2330,7 +2336,7 @@
|
|||||||
"pause": "Mettre en pause l'élément",
|
"pause": "Mettre en pause l'élément",
|
||||||
"resume": "Reprendre l'élément",
|
"resume": "Reprendre l'élément",
|
||||||
"delete": {
|
"delete": {
|
||||||
"title": "Supprimer l'élement",
|
"title": "Supprimer l'élément",
|
||||||
"modalTitle": "Êtes-vous sûr de vouloir supprimer cette tâche ?",
|
"modalTitle": "Êtes-vous sûr de vouloir supprimer cette tâche ?",
|
||||||
"entry": "Supprimer l'entrée",
|
"entry": "Supprimer l'entrée",
|
||||||
"entryAndFiles": "Supprimer l'entrée et le(s) fichier(s)"
|
"entryAndFiles": "Supprimer l'entrée et le(s) fichier(s)"
|
||||||
@@ -2542,7 +2548,7 @@
|
|||||||
"allImagesAlreadyImported": "Toutes les images déjà importées",
|
"allImagesAlreadyImported": "Toutes les images déjà importées",
|
||||||
"onlyAdminCanImport": "Seuls les administrateurs peuvent importer depuis docker",
|
"onlyAdminCanImport": "Seuls les administrateurs peuvent importer depuis docker",
|
||||||
"selectAll": "Tout sélectionner",
|
"selectAll": "Tout sélectionner",
|
||||||
"deselectAll": "Tout déselectionner"
|
"deselectAll": "Tout désélectionner"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": {
|
||||||
"label": "Fournisseur"
|
"label": "Fournisseur"
|
||||||
@@ -2667,7 +2673,7 @@
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"integrationsDisconnected": "Aucune donnée disponible, toutes les intégrations sont déconnectées",
|
"integrationsDisconnected": "Aucune donnée disponible, toutes les intégrations sont déconnectées",
|
||||||
"unknownContentOption": "Option de contenu inconnue pour le widget d'état du réseau: "
|
"unknownContentOption": "Option de contenu inconnue pour le widget d'état du réseau : "
|
||||||
},
|
},
|
||||||
"name": "État du réseau",
|
"name": "État du réseau",
|
||||||
"description": "Afficher les périphériques connectés sur un réseau"
|
"description": "Afficher les périphériques connectés sur un réseau"
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Mémoire",
|
"memory": "Mémoire",
|
||||||
|
"gpu": "",
|
||||||
"network": "Réseau"
|
"network": "Réseau"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "RAM",
|
"memory": "RAM",
|
||||||
|
"gpu": "",
|
||||||
"network": "NET",
|
"network": "NET",
|
||||||
"up": "UP",
|
"up": "UP",
|
||||||
"down": "HORS SERVICE"
|
"down": "HORS SERVICE"
|
||||||
@@ -2757,7 +2765,7 @@
|
|||||||
"label": "Mode d'affichage",
|
"label": "Mode d'affichage",
|
||||||
"option": {
|
"option": {
|
||||||
"percentage": {
|
"percentage": {
|
||||||
"label": "Pourcentage (ex : 70%)"
|
"label": "Pourcentage (ex : 76%)"
|
||||||
},
|
},
|
||||||
"absolute": {
|
"absolute": {
|
||||||
"label": "Valeurs absolues (ex. : 800 Go / 1 To)"
|
"label": "Valeurs absolues (ex. : 800 Go / 1 To)"
|
||||||
@@ -2965,7 +2973,7 @@
|
|||||||
},
|
},
|
||||||
"customCss": {
|
"customCss": {
|
||||||
"label": "CSS personnalisé pour ce tableau",
|
"label": "CSS personnalisé pour ce tableau",
|
||||||
"description": "En outre, vous pouvez personnaliser votre tableau de bord à l'aide de CSS. Réservé aux utilisateurs expérimentés.",
|
"description": "De plus, personnalisez votre tableau de bord à l'aide de CSS, recommandé uniquement pour les utilisateurs expérimentés",
|
||||||
"customClassesAlert": {
|
"customClassesAlert": {
|
||||||
"title": "Classes personnalisées",
|
"title": "Classes personnalisées",
|
||||||
"description": "Vous pouvez ajouter des classes personnalisées aux éléments de votre tableau dans les options avancées de chaque élément et les utiliser dans le CSS personnalisé ci-dessus."
|
"description": "Vous pouvez ajouter des classes personnalisées aux éléments de votre tableau dans les options avancées de chaque élément et les utiliser dans le CSS personnalisé ci-dessus."
|
||||||
@@ -3243,7 +3251,7 @@
|
|||||||
"label": "Supprimer définitivement",
|
"label": "Supprimer définitivement",
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "Supprimer le tableau de bord",
|
"title": "Supprimer le tableau de bord",
|
||||||
"description": "Êtes-vous sûr de vouloir supprimer le tableau {name}?"
|
"description": "Êtes-vous sûr de vouloir supprimer le tableau {name} ?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "אפשר מיקום גיאוגרפי"
|
"label": "אפשר מיקום גיאוגרפי"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "הצג מידע זיכרון"
|
"label": "הצג מידע זיכרון"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "הצג זמן פעולה"
|
"label": "הצג זמן פעולה"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "מעבד",
|
"cpu": "מעבד",
|
||||||
"memory": "זיכרון",
|
"memory": "זיכרון",
|
||||||
|
"gpu": "",
|
||||||
"network": "רשת"
|
"network": "רשת"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "מעבד",
|
"cpu": "מעבד",
|
||||||
"memory": "זיכרון",
|
"memory": "זיכרון",
|
||||||
|
"gpu": "",
|
||||||
"network": "רשת",
|
"network": "רשת",
|
||||||
"up": "העלאה",
|
"up": "העלאה",
|
||||||
"down": "הורדה"
|
"down": "הורדה"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Dopusti geolociranje"
|
"label": "Dopusti geolociranje"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
"title": "Import token megadása",
|
"title": "Import token megadása",
|
||||||
"field": {
|
"field": {
|
||||||
"token": {
|
"token": {
|
||||||
"label": "",
|
"label": "Token",
|
||||||
"description": "Írja be a megjelenített import tokent az előző homarr példányból"
|
"description": "Írja be a megjelenített import tokent az előző homarr példányból"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -153,10 +153,10 @@
|
|||||||
"label": "Ikonok használata a pingekhez"
|
"label": "Ikonok használata a pingekhez"
|
||||||
},
|
},
|
||||||
"defaultSearchEngine": {
|
"defaultSearchEngine": {
|
||||||
"label": ""
|
"label": "Alapértelmezett kereső"
|
||||||
},
|
},
|
||||||
"openSearchInNewTab": {
|
"openSearchInNewTab": {
|
||||||
"label": ""
|
"label": "Nyissa meg a keresési eredményeket új ablakban"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -219,10 +219,10 @@
|
|||||||
"changeSearchPreferences": {
|
"changeSearchPreferences": {
|
||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
"message": ""
|
"message": "A keresési beállítások sikeresen megváltoztak"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": ""
|
"message": "A keresési beállításokat nem sikerült megváltoztatni"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -307,12 +307,12 @@
|
|||||||
"name": "Név",
|
"name": "Név",
|
||||||
"members": "Tagok",
|
"members": "Tagok",
|
||||||
"homeBoard": {
|
"homeBoard": {
|
||||||
"label": "",
|
"label": "Főoldal",
|
||||||
"description": ""
|
"description": "Csak a csoport számára elérhető oldalak választhatók ki"
|
||||||
},
|
},
|
||||||
"mobileBoard": {
|
"mobileBoard": {
|
||||||
"label": "",
|
"label": "Mobiloldal",
|
||||||
"description": ""
|
"description": "Csak a csoport számára elérhető oldalak választhatók ki"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permission": {
|
"permission": {
|
||||||
@@ -514,12 +514,12 @@
|
|||||||
"board": {
|
"board": {
|
||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
"title": "",
|
"title": "Beállítások elmentve",
|
||||||
"message": ""
|
"message": "Oldal beállítások sikeresen elmentve"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"title": "",
|
"title": "Nem sikerült menteni a beállításokat",
|
||||||
"message": ""
|
"message": "Az oldalbeállítások nem menthetők"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -527,21 +527,21 @@
|
|||||||
"changePosition": {
|
"changePosition": {
|
||||||
"notification": {
|
"notification": {
|
||||||
"success": {
|
"success": {
|
||||||
"message": ""
|
"message": "Pozíció sikeresen megváltozott"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": ""
|
"message": "Pozíció nem módosítható"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultGroup": {
|
"defaultGroup": {
|
||||||
"name": "",
|
"name": "Alapértelmezett csoport",
|
||||||
"description": ""
|
"description": "{name} - Minden bejelentkezett felhasználó"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"search": "",
|
"search": "Alkalmazás megtalálása",
|
||||||
"page": {
|
"page": {
|
||||||
"list": {
|
"list": {
|
||||||
"title": "Alkalmazások",
|
"title": "Alkalmazások",
|
||||||
@@ -599,12 +599,12 @@
|
|||||||
"label": "Leírás"
|
"label": "Leírás"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"label": ""
|
"label": "Url"
|
||||||
},
|
},
|
||||||
"useDifferentUrlForPing": {
|
"useDifferentUrlForPing": {
|
||||||
"checkbox": {
|
"checkbox": {
|
||||||
"label": "",
|
"label": "Más URL használata a ping-hez",
|
||||||
"description": ""
|
"description": "Hasznos, ha a Homarr közvetlenül, egy belső hosztnév vagy hálózaton keresztül fér hozzá, hogy elkerülje az internetszolgáltató által használt sávszélesség terhelését"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -612,17 +612,17 @@
|
|||||||
"select": {
|
"select": {
|
||||||
"label": "Alkalmazás kiválasztása",
|
"label": "Alkalmazás kiválasztása",
|
||||||
"notFound": "Nem található alkalmazás",
|
"notFound": "Nem található alkalmazás",
|
||||||
"search": "",
|
"search": "Alkalmazás keresése",
|
||||||
"noResults": "",
|
"noResults": "Nincs találat",
|
||||||
"action": "",
|
"action": "Kiválasztás {app}",
|
||||||
"title": ""
|
"title": "Válassz egy programot az oldalhoz adáshoz"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "",
|
"title": "Új alkalmazás készítése",
|
||||||
"description": "",
|
"description": "Új alkalmazás létrehozása ",
|
||||||
"action": ""
|
"action": "Alkalmazás készítő megnyitása"
|
||||||
},
|
},
|
||||||
"add": ""
|
"add": "Alkalmazás hozzáadása"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"integration": {
|
"integration": {
|
||||||
@@ -649,14 +649,14 @@
|
|||||||
"app": {
|
"app": {
|
||||||
"option": {
|
"option": {
|
||||||
"existing": {
|
"existing": {
|
||||||
"title": "",
|
"title": "Meglévő",
|
||||||
"label": ""
|
"label": "Meglévő alkalmazás kiválasztása"
|
||||||
},
|
},
|
||||||
"new": {
|
"new": {
|
||||||
"title": "",
|
"title": "Új",
|
||||||
"url": {
|
"url": {
|
||||||
"label": "",
|
"label": "Alkalmazás url",
|
||||||
"description": ""
|
"description": "Az url, amelyre az alkalmazás a műszerfalról történő eléréskor irányít"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -676,9 +676,9 @@
|
|||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"action": {
|
"action": {
|
||||||
"add": "",
|
"add": "Alkalmazás megtalálása",
|
||||||
"remove": "",
|
"remove": "Leválasztás",
|
||||||
"select": ""
|
"select": "Válasszon ki egy alkalmazást a hivatkozáshoz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -702,21 +702,21 @@
|
|||||||
"label": "Név"
|
"label": "Név"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"label": ""
|
"label": "Url"
|
||||||
},
|
},
|
||||||
"attemptSearchEngineCreation": {
|
"attemptSearchEngineCreation": {
|
||||||
"label": "",
|
"label": "Keresőmotor létrehozása",
|
||||||
"description": ""
|
"description": "A \"{kind}\" integráció használható a keresőmotorokkal. Jelölje be ezt a jelölőnégyzetet, ha automatikusan szeretné konfigurálni a keresőmotort."
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"sectionTitle": ""
|
"sectionTitle": "Hivattkozott alkalmazás"
|
||||||
},
|
},
|
||||||
"createApp": {
|
"createApp": {
|
||||||
"label": "",
|
"label": "Alkalmazás létrehozása",
|
||||||
"description": ""
|
"description": "Hozzon létre egy alkalmazást, amelynek ugyanaz a neve és ikonja van, mint az integrációnak. Hagyja üresen az alábbi beviteli mezőt, hogy az alkalmazás az integrációs URL-lel jöjjön létre."
|
||||||
},
|
},
|
||||||
"appHref": {
|
"appHref": {
|
||||||
"placeholder": ""
|
"placeholder": "Egyéni alkalmazás URL"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
@@ -730,59 +730,59 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"common": {
|
"common": {
|
||||||
"cause": {
|
"cause": {
|
||||||
"title": ""
|
"title": "Részletesebb leírás a problémáról"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unknown": {
|
"unknown": {
|
||||||
"title": "",
|
"title": "Ismeretlen hiba",
|
||||||
"description": ""
|
"description": "Egy ismeretlen hiba történt, nyissa meg a részleteket alul, hogy többet megtudjon"
|
||||||
},
|
},
|
||||||
"parse": {
|
"parse": {
|
||||||
"title": "",
|
"title": "Elemzési hiba",
|
||||||
"description": ""
|
"description": "A válasz nem volt elemezhető. Kérjük, ellenőrizze, hogy az URL a szolgáltatás alap URL-jére mutat-e."
|
||||||
},
|
},
|
||||||
"authorization": {
|
"authorization": {
|
||||||
"title": "",
|
"title": "Hitelesítési hiba",
|
||||||
"description": ""
|
"description": "A kérés nem volt engedélyezett. Kérjük, ellenőrizze, hogy a hitelesítő adatok helyesek-e, és hogy a megfelelő engedélyekkel vannak-e beállítva."
|
||||||
},
|
},
|
||||||
"statusCode": {
|
"statusCode": {
|
||||||
"title": "",
|
"title": "Válaszhiba",
|
||||||
"description": "",
|
"description": "Váratlan {statusCode} ({reason}) választ kapott a <url></url> címen. Kérjük, ellenőrizze, hogy az URL az integráció alap URL-jére mutat-e.",
|
||||||
"otherDescription": "",
|
"otherDescription": "Váratlan {statusCode} választ kapott a <url></url> címen. Kérjük, ellenőrizze, hogy az URL az integráció alap URL-jére mutat-e.",
|
||||||
"reason": {
|
"reason": {
|
||||||
"badRequest": "",
|
"badRequest": "Hibás kérés",
|
||||||
"notFound": "",
|
"notFound": "Nem található",
|
||||||
"tooManyRequests": "",
|
"tooManyRequests": "Túl sok kérés",
|
||||||
"internalServerError": "",
|
"internalServerError": "Belső szerverhiba",
|
||||||
"serviceUnavailable": "",
|
"serviceUnavailable": "A szolgáltatás nem elérhető",
|
||||||
"gatewayTimeout": ""
|
"gatewayTimeout": "Átjáró időtúllépés"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"certificate": {
|
"certificate": {
|
||||||
"title": "",
|
"title": "Tanúsítványhiba",
|
||||||
"description": {
|
"description": {
|
||||||
"expired": "",
|
"expired": "A tanúsítvány lejárt.",
|
||||||
"notYetValid": "",
|
"notYetValid": "A tanúsítvány még nem érvényes.",
|
||||||
"untrusted": "",
|
"untrusted": "A tanúsítvány nem megbízható.",
|
||||||
"hostnameMismatch": ""
|
"hostnameMismatch": "A tanúsítványban szereplő hosztnév nem egyezik meg az URL-lel."
|
||||||
},
|
},
|
||||||
"alert": {
|
"alert": {
|
||||||
"permission": {
|
"permission": {
|
||||||
"title": "",
|
"title": "Nincsenek megfelelő engedélyek",
|
||||||
"message": ""
|
"message": "Önnek nincs joga tanúsítványokat megbízhatónak nyilvánítani vagy feltölteni. Kérjük, lépjen kapcsolatba a rendszergazdájával, hogy feltölthesse a szükséges gyökér tanúsítványt."
|
||||||
},
|
},
|
||||||
"hostnameMismatch": {
|
"hostnameMismatch": {
|
||||||
"title": "",
|
"title": "Állomásnév eltérés",
|
||||||
"message": ""
|
"message": "A tanúsítványban szereplő hosztnév nem egyezik meg azzal a hosztnévvel, amelyhez kapcsolódik. Ez biztonsági kockázatot jelezhet, de mégis dönthet úgy, hogy megbízik ebben a tanúsítványban."
|
||||||
},
|
},
|
||||||
"extract": {
|
"extract": {
|
||||||
"title": "",
|
"title": "A tanúsító hatóság (CA) tanúsítványának kinyerése sikertelen",
|
||||||
"message": ""
|
"message": "Csak a lánc nélküli, önaláírt tanúsítványok tölthetők le automatikusan. Ha önaláírt tanúsítványt használ, kérjük, feltétlenül töltse fel manuálisan a tanúsító hatóság (CA) tanúsítványát. Az ehhez szükséges lépések leírását itt találja: <docsLink></docsLink>."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"retry": {
|
"retry": {
|
||||||
"label": ""
|
"label": "Újrapróbál"
|
||||||
},
|
},
|
||||||
"trust": {
|
"trust": {
|
||||||
"label": ""
|
"label": ""
|
||||||
@@ -920,8 +920,8 @@
|
|||||||
"message": "Az útvonal valószínűleg nem helyes"
|
"message": "Az útvonal valószínűleg nem helyes"
|
||||||
},
|
},
|
||||||
"tooManyRequests": {
|
"tooManyRequests": {
|
||||||
"title": "",
|
"title": "Túl sok kérés adott idő alatt",
|
||||||
"message": ""
|
"message": "Túl sok kérést küldött. Valószínűleg a célrendszer korlátozza vagy elutasítja a kéréseket"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -955,36 +955,36 @@
|
|||||||
"newLabel": "Új jelszó"
|
"newLabel": "Új jelszó"
|
||||||
},
|
},
|
||||||
"tokenId": {
|
"tokenId": {
|
||||||
"label": "",
|
"label": "Token azonosító",
|
||||||
"newLabel": ""
|
"newLabel": "Új token azonosító"
|
||||||
},
|
},
|
||||||
"realm": {
|
"realm": {
|
||||||
"label": "",
|
"label": "Tartomány",
|
||||||
"newLabel": ""
|
"newLabel": "Új tartomány"
|
||||||
},
|
},
|
||||||
"personalAccessToken": {
|
"personalAccessToken": {
|
||||||
"label": "",
|
"label": "Személyes Hozzáférési Token",
|
||||||
"newLabel": ""
|
"newLabel": "Új Személyes Hozzáférési Token"
|
||||||
},
|
},
|
||||||
"topic": {
|
"topic": {
|
||||||
"label": "",
|
"label": "Téma",
|
||||||
"newLabel": ""
|
"newLabel": "Új téma"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"label": "",
|
"label": "Url",
|
||||||
"newLabel": ""
|
"newLabel": "Új url"
|
||||||
},
|
},
|
||||||
"opnsenseApiKey": {
|
"opnsenseApiKey": {
|
||||||
"label": "",
|
"label": "API kulcs (Kulcs)",
|
||||||
"newLabel": ""
|
"newLabel": "Új API Kulcs (Kulcs)"
|
||||||
},
|
},
|
||||||
"opnsenseApiSecret": {
|
"opnsenseApiSecret": {
|
||||||
"label": "",
|
"label": "Api Kulcs (Titkos)",
|
||||||
"newLabel": ""
|
"newLabel": "Új API Kulcs (Titkos)"
|
||||||
},
|
},
|
||||||
"githubAppId": {
|
"githubAppId": {
|
||||||
"label": "",
|
"label": "App Azonosító",
|
||||||
"newLabel": ""
|
"newLabel": "Új App Azonosító"
|
||||||
},
|
},
|
||||||
"githubInstallationId": {
|
"githubInstallationId": {
|
||||||
"label": "",
|
"label": "",
|
||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Geolokáció engedélyezése"
|
"label": "Geolokáció engedélyezése"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Memóriainformációk megjelenítése"
|
"label": "Memóriainformációk megjelenítése"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Consenti geo-localizzazione"
|
"label": "Consenti geo-localizzazione"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": "Consenti modali"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Mostra Informazioni Memoria"
|
"label": "Mostra Informazioni Memoria"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": "Mostra informazioni sulla GPU"
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Mostra Uptime"
|
"label": "Mostra Uptime"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Memoria",
|
"memory": "Memoria",
|
||||||
|
"gpu": "GPU",
|
||||||
"network": "Rete"
|
"network": "Rete"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "MEM",
|
"memory": "MEM",
|
||||||
|
"gpu": "GPU",
|
||||||
"network": "NET",
|
"network": "NET",
|
||||||
"up": "SU",
|
"up": "SU",
|
||||||
"down": "DOWN"
|
"down": "DOWN"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "位置情報を許可する"
|
"label": "位置情報を許可する"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "メモリー情報を表示"
|
"label": "メモリー情報を表示"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "稼働時間を表示"
|
"label": "稼働時間を表示"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "メモリ",
|
"memory": "メモリ",
|
||||||
|
"gpu": "",
|
||||||
"network": "ネットワーク"
|
"network": "ネットワーク"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "MEM",
|
"memory": "MEM",
|
||||||
|
"gpu": "",
|
||||||
"network": "NET",
|
"network": "NET",
|
||||||
"up": "UP",
|
"up": "UP",
|
||||||
"down": "DOWN"
|
"down": "DOWN"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "지리적 위치 허용"
|
"label": "지리적 위치 허용"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Leisti nustatyti geografinę buvimo vietą"
|
"label": "Leisti nustatyti geografinę buvimo vietą"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Atļaut ģeogrāfiskās atrašanās vietas noteikšanu"
|
"label": "Atļaut ģeogrāfiskās atrašanās vietas noteikšanu"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Rādīt atmiņas informāciju"
|
"label": "Rādīt atmiņas informāciju"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Geolocatie toestaan"
|
"label": "Geolocatie toestaan"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Werkgeheugen info weergeven"
|
"label": "Werkgeheugen info weergeven"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Uptime weergeven"
|
"label": "Uptime weergeven"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "Processor",
|
"cpu": "Processor",
|
||||||
"memory": "Geheugen",
|
"memory": "Geheugen",
|
||||||
|
"gpu": "",
|
||||||
"network": "Netwerk"
|
"network": "Netwerk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "MEM",
|
"memory": "MEM",
|
||||||
|
"gpu": "",
|
||||||
"network": "NET",
|
"network": "NET",
|
||||||
"up": "OMHOOG",
|
"up": "OMHOOG",
|
||||||
"down": "OMLAAG"
|
"down": "OMLAAG"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Tillat geolokalisering"
|
"label": "Tillat geolokalisering"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Vis minneinfo"
|
"label": "Vis minneinfo"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Vis oppetid"
|
"label": "Vis oppetid"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Minne",
|
"memory": "Minne",
|
||||||
|
"gpu": "",
|
||||||
"network": "Nettverk"
|
"network": "Nettverk"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Minne",
|
"memory": "Minne",
|
||||||
|
"gpu": "",
|
||||||
"network": "NETT",
|
"network": "NETT",
|
||||||
"up": "OPP",
|
"up": "OPP",
|
||||||
"down": "NED"
|
"down": "NED"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Zezwalaj na geolokalizację"
|
"label": "Zezwalaj na geolokalizację"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Pokaż informacje o pamięci"
|
"label": "Pokaż informacje o pamięci"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Pokaż czas pracy"
|
"label": "Pokaż czas pracy"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "Procesor",
|
"cpu": "Procesor",
|
||||||
"memory": "Pamięć",
|
"memory": "Pamięć",
|
||||||
|
"gpu": "",
|
||||||
"network": "Sieć"
|
"network": "Sieć"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "Procesor",
|
"cpu": "Procesor",
|
||||||
"memory": "Pamięć",
|
"memory": "Pamięć",
|
||||||
|
"gpu": "",
|
||||||
"network": "Sieć",
|
"network": "Sieć",
|
||||||
"up": "Aktywny",
|
"up": "Aktywny",
|
||||||
"down": "Nieaktywny"
|
"down": "Nieaktywny"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Permitir geolocalização"
|
"label": "Permitir geolocalização"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": "Permitir modais"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Mostrar informações da memória"
|
"label": "Mostrar informações da memória"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": "Mostrar informações da GPU"
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Exibir tempo de atividade"
|
"label": "Exibir tempo de atividade"
|
||||||
},
|
},
|
||||||
@@ -1997,58 +2003,58 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tracearr": {
|
"tracearr": {
|
||||||
"name": "",
|
"name": "Tracearr",
|
||||||
"description": "",
|
"description": "Monitoramento avançado de servidor de mídia com dados detalhados de transmissão e transcodificação\n",
|
||||||
"option": {
|
"option": {
|
||||||
"showStreams": {
|
"showStreams": {
|
||||||
"label": ""
|
"label": "Exibir transmissões ativas"
|
||||||
},
|
},
|
||||||
"showStats": {
|
"showStats": {
|
||||||
"label": ""
|
"label": "Exibir resumo de estatísticas"
|
||||||
},
|
},
|
||||||
"showRecentActivity": {
|
"showRecentActivity": {
|
||||||
"label": ""
|
"label": "Exibir Atividade Recente"
|
||||||
},
|
},
|
||||||
"showViolations": {
|
"showViolations": {
|
||||||
"label": ""
|
"label": "Mostrar Violações"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"activeStreams": "",
|
"activeStreams": "Streams Ativas",
|
||||||
"totalUsers": "",
|
"totalUsers": "Total de usuários",
|
||||||
"transcodes": "",
|
"transcodes": "Transcodificações",
|
||||||
"bandwidth": ""
|
"bandwidth": "Largura da banda"
|
||||||
},
|
},
|
||||||
"streams": {
|
"streams": {
|
||||||
"empty": "",
|
"empty": "Sem transmissões ativas",
|
||||||
"user": "",
|
"user": "Usuário",
|
||||||
"media": "",
|
"media": "Mídia",
|
||||||
"state": "",
|
"state": "Estado",
|
||||||
"quality": "",
|
"quality": "Qualidade",
|
||||||
"device": "",
|
"device": "Dispositivo",
|
||||||
"progress": "",
|
"progress": "Progresso",
|
||||||
"videoDecision": {
|
"videoDecision": {
|
||||||
"directPlay": "",
|
"directPlay": "Reprodução direta",
|
||||||
"transcode": "",
|
"transcode": "Transcodificação",
|
||||||
"directStream": ""
|
"directStream": "Transmissão direta"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"recentActivity": {
|
"recentActivity": {
|
||||||
"title": "",
|
"title": "Atividade recente",
|
||||||
"empty": "",
|
"empty": "Nenhuma atividade recente",
|
||||||
"watched": "",
|
"watched": "Assistido",
|
||||||
"partial": ""
|
"partial": "Parcial"
|
||||||
},
|
},
|
||||||
"violations": {
|
"violations": {
|
||||||
"title": "",
|
"title": "Violações",
|
||||||
"empty": "",
|
"empty": "Não há violações recentes",
|
||||||
"rule": "",
|
"rule": "Regra",
|
||||||
"user": ""
|
"user": "Usuário"
|
||||||
},
|
},
|
||||||
"noSectionsEnabled": "",
|
"noSectionsEnabled": "Nenhuma seção habilitada",
|
||||||
"error": {
|
"error": {
|
||||||
"noIntegration": "",
|
"noIntegration": "Nenhuma integração Coolify configurada",
|
||||||
"internalServerError": ""
|
"internalServerError": "Falha ao buscar dados do Tracearr"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
@@ -2147,50 +2153,50 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"immich-serverStats": {
|
"immich-serverStats": {
|
||||||
"name": "",
|
"name": "Estatísticas do Servidor Immich",
|
||||||
"description": "",
|
"description": "Informações sobre sua instância do Immich",
|
||||||
"option": {
|
"option": {
|
||||||
"showUsers": {
|
"showUsers": {
|
||||||
"label": "",
|
"label": "Mostrar usuários",
|
||||||
"description": ""
|
"description": "Mostrar a contagem de usuários na sua instância"
|
||||||
},
|
},
|
||||||
"showPhotos": {
|
"showPhotos": {
|
||||||
"label": "",
|
"label": "Mostrar Fotos",
|
||||||
"description": ""
|
"description": "Mostrar a contagem de fotos na sua instância"
|
||||||
},
|
},
|
||||||
"showVideos": {
|
"showVideos": {
|
||||||
"label": "",
|
"label": "Mostrar vídeos",
|
||||||
"description": ""
|
"description": "Mostrar contagem de vídeos na sua instância"
|
||||||
},
|
},
|
||||||
"showStorage": {
|
"showStorage": {
|
||||||
"label": "",
|
"label": "Mostrar armazenamento",
|
||||||
"description": ""
|
"description": "Mostrar a quantidade total de armazenamento utilizado"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"users": "",
|
"users": "Usuários",
|
||||||
"photos": "",
|
"photos": "Fotos",
|
||||||
"videos": "",
|
"videos": "Vídeos",
|
||||||
"storage": ""
|
"storage": "Armazenamento Utilizado"
|
||||||
},
|
},
|
||||||
"immich-albumCarousel": {
|
"immich-albumCarousel": {
|
||||||
"name": "",
|
"name": "Álbum do Immich",
|
||||||
"option": {
|
"option": {
|
||||||
"albumId": {
|
"albumId": {
|
||||||
"label": "",
|
"label": "ID do Álbum",
|
||||||
"description": ""
|
"description": "Esta é uma identificação única que se refere ao seu álbum Immich. Você pode encontrar essa identificação abrindo um álbum e copiando a string única da URL, por exemplo 'a21aca09-9f05-47dc-b1a1-7b53c547226e'"
|
||||||
},
|
},
|
||||||
"rotationIntervalSeconds": {
|
"rotationIntervalSeconds": {
|
||||||
"label": "",
|
"label": "Intervalo em segundos",
|
||||||
"description": ""
|
"description": "Girar entre as imagens no álbum a cada X segundos. Deve ser pelo menos 1. Deve estar abaixo de 3600."
|
||||||
},
|
},
|
||||||
"showPhotoInfo": {
|
"showPhotoInfo": {
|
||||||
"label": "",
|
"label": "Mostrar informações da foto",
|
||||||
"description": ""
|
"description": "Exibir a data da imagem e o índice atual na parte inferior da apresentação"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"description": "",
|
"description": "Apresentação de imagens em seu álbum Imich",
|
||||||
"noAlbumSelected": "",
|
"noAlbumSelected": "Nenhum álbum selecionado, edite o widget e escolha um álbum",
|
||||||
"noPhotos": ""
|
"noPhotos": "Nenhuma foto neste álbum"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"name": "Cliente de Download",
|
"name": "Cliente de Download",
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "Memória",
|
"memory": "Memória",
|
||||||
|
"gpu": "GPU",
|
||||||
"network": "Rede"
|
"network": "Rede"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"memory": "MEM",
|
"memory": "MEM",
|
||||||
|
"gpu": "GPU",
|
||||||
"network": "REDE",
|
"network": "REDE",
|
||||||
"up": "ENVIO",
|
"up": "ENVIO",
|
||||||
"down": "RECEBIDO"
|
"down": "RECEBIDO"
|
||||||
@@ -3575,7 +3583,7 @@
|
|||||||
"label": "Clima"
|
"label": "Clima"
|
||||||
},
|
},
|
||||||
"tracearr": {
|
"tracearr": {
|
||||||
"label": ""
|
"label": "Tracearr"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"interval": {
|
"interval": {
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Permiteți geolocalizarea"
|
"label": "Permiteți geolocalizarea"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Afișare informații memorie"
|
"label": "Afișare informații memorie"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Разрешить геолокацию"
|
"label": "Разрешить геолокацию"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": "Разрешить модальные окна"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Показать информацию о памяти"
|
"label": "Показать информацию о памяти"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": "Показать информацию о GPU"
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Показывать время работы"
|
"label": "Показывать время работы"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "ЦП",
|
"cpu": "ЦП",
|
||||||
"memory": "Память",
|
"memory": "Память",
|
||||||
|
"gpu": "GPU",
|
||||||
"network": "Сеть"
|
"network": "Сеть"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "ЦП",
|
"cpu": "ЦП",
|
||||||
"memory": "ОЗУ",
|
"memory": "ОЗУ",
|
||||||
|
"gpu": "GPU",
|
||||||
"network": "Сеть",
|
"network": "Сеть",
|
||||||
"up": "Исх.",
|
"up": "Исх.",
|
||||||
"down": "Вх."
|
"down": "Вх."
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Povolenie geografickej lokalizácie"
|
"label": "Povolenie geografickej lokalizácie"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Zobraziť informácie o pamäti"
|
"label": "Zobraziť informácie o pamäti"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Zobraziť Uptime"
|
"label": "Zobraziť Uptime"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Omogočanje zemljepisne lokacije"
|
"label": "Omogočanje zemljepisne lokacije"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Tillåt geografisk lokalisering"
|
"label": "Tillåt geografisk lokalisering"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Visa minnesinformation"
|
"label": "Visa minnesinformation"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Coğrafi konuma izin ver (geolocation)"
|
"label": "Coğrafi konuma izin ver (geolocation)"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Bellek Bilgilerini Göster"
|
"label": "Bellek Bilgilerini Göster"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": "Çalışma Süresini Göster"
|
"label": "Çalışma Süresini Göster"
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "İşlemci",
|
"cpu": "İşlemci",
|
||||||
"memory": "Bellek",
|
"memory": "Bellek",
|
||||||
|
"gpu": "",
|
||||||
"network": "Ağ"
|
"network": "Ağ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "İşlemci",
|
"cpu": "İşlemci",
|
||||||
"memory": "BELLEK",
|
"memory": "BELLEK",
|
||||||
|
"gpu": "",
|
||||||
"network": "AĞ",
|
"network": "AĞ",
|
||||||
"up": "YÜKLEME",
|
"up": "YÜKLEME",
|
||||||
"down": "İNDİRME"
|
"down": "İNDİRME"
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Дозволити геолокацію"
|
"label": "Дозволити геолокацію"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "Показати інформацію про пам'ять"
|
"label": "Показати інформацію про пам'ять"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "Cho phép định vị"
|
"label": "Cho phép định vị"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@
|
|||||||
},
|
},
|
||||||
"allowGeolocation": {
|
"allowGeolocation": {
|
||||||
"label": "允許地址位置"
|
"label": "允許地址位置"
|
||||||
|
},
|
||||||
|
"allowModals": {
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -1858,6 +1861,9 @@
|
|||||||
"memory": {
|
"memory": {
|
||||||
"label": "顯示記憶體訊息"
|
"label": "顯示記憶體訊息"
|
||||||
},
|
},
|
||||||
|
"gpu": {
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
"showUptime": {
|
"showUptime": {
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
@@ -2725,6 +2731,7 @@
|
|||||||
"option": {
|
"option": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": ""
|
"network": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2741,6 +2748,7 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"cpu": "",
|
"cpu": "",
|
||||||
"memory": "",
|
"memory": "",
|
||||||
|
"gpu": "",
|
||||||
"network": "",
|
"network": "",
|
||||||
"up": "",
|
"up": "",
|
||||||
"down": ""
|
"down": ""
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"@mantine/core": "^8.3.18",
|
"@mantine/core": "^8.3.18",
|
||||||
"@mantine/dates": "^8.3.18",
|
"@mantine/dates": "^8.3.18",
|
||||||
"@mantine/hooks": "^8.3.18",
|
"@mantine/hooks": "^8.3.18",
|
||||||
"@tabler/icons-react": "^3.40.0",
|
"@tabler/icons-react": "^3.41.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"mantine-react-table": "2.0.0-beta.9",
|
"mantine-react-table": "2.0.0-beta.9",
|
||||||
"next": "16.2.1",
|
"next": "16.2.1",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homarr/definitions": "workspace:^0.1.0",
|
"@homarr/definitions": "workspace:^0.1.0",
|
||||||
"@homarr/translation": "workspace:^0.1.0",
|
"@homarr/translation": "workspace:^0.1.0",
|
||||||
"zod": "^4.2.1",
|
"zod": "^4.3.6",
|
||||||
"zod-form-data": "^3.0.1"
|
"zod-form-data": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -57,12 +57,13 @@ export const userBaseCreateSchema = z.object({
|
|||||||
password: userPasswordSchema,
|
password: userPasswordSchema,
|
||||||
confirmPassword: z.string(),
|
confirmPassword: z.string(),
|
||||||
email: z.string().email().or(z.string().length(0)).optional(),
|
email: z.string().email().or(z.string().length(0)).optional(),
|
||||||
groupIds: z.array(z.string()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userCreateSchema = addConfirmPasswordRefinement(userBaseCreateSchema);
|
export const userCreateSchema = addConfirmPasswordRefinement(userBaseCreateSchema).and(
|
||||||
|
z.object({ groupIds: z.array(z.string()) }),
|
||||||
|
);
|
||||||
|
|
||||||
export const userInitSchema = addConfirmPasswordRefinement(userBaseCreateSchema.omit({ groupIds: true }));
|
export const userInitSchema = addConfirmPasswordRefinement(userBaseCreateSchema);
|
||||||
|
|
||||||
export const userSignInSchema = z.object({
|
export const userSignInSchema = z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
@@ -111,12 +112,15 @@ const baseChangePasswordSchema = z.object({
|
|||||||
previousPassword: z.string().min(1),
|
previousPassword: z.string().min(1),
|
||||||
password: userPasswordSchema,
|
password: userPasswordSchema,
|
||||||
confirmPassword: z.string(),
|
confirmPassword: z.string(),
|
||||||
userId: z.string(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userChangePasswordSchema = addConfirmPasswordRefinement(baseChangePasswordSchema.omit({ userId: true }));
|
export const userChangePasswordSchema = addConfirmPasswordRefinement(baseChangePasswordSchema);
|
||||||
|
|
||||||
export const userChangePasswordApiSchema = addConfirmPasswordRefinement(baseChangePasswordSchema);
|
export const userChangePasswordApiSchema = addConfirmPasswordRefinement(baseChangePasswordSchema).and(
|
||||||
|
z.object({
|
||||||
|
userId: z.string(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const userChangeHomeBoardsSchema = z.object({
|
export const userChangeHomeBoardsSchema = z.object({
|
||||||
homeBoardId: z.string().nullable(),
|
homeBoardId: z.string().nullable(),
|
||||||
|
|||||||
@@ -51,23 +51,23 @@
|
|||||||
"@mantine/charts": "^8.3.18",
|
"@mantine/charts": "^8.3.18",
|
||||||
"@mantine/core": "^8.3.18",
|
"@mantine/core": "^8.3.18",
|
||||||
"@mantine/hooks": "^8.3.18",
|
"@mantine/hooks": "^8.3.18",
|
||||||
"@tabler/icons-react": "^3.40.0",
|
"@tabler/icons-react": "^3.41.0",
|
||||||
"@tiptap/extension-color": "3.20.4",
|
"@tiptap/extension-color": "3.21.0",
|
||||||
"@tiptap/extension-highlight": "3.20.4",
|
"@tiptap/extension-highlight": "3.21.0",
|
||||||
"@tiptap/extension-image": "3.20.4",
|
"@tiptap/extension-image": "3.21.0",
|
||||||
"@tiptap/extension-link": "^3.20.4",
|
"@tiptap/extension-link": "^3.21.0",
|
||||||
"@tiptap/extension-placeholder": "^3.20.4",
|
"@tiptap/extension-placeholder": "^3.21.0",
|
||||||
"@tiptap/extension-table": "3.20.4",
|
"@tiptap/extension-table": "3.21.0",
|
||||||
"@tiptap/extension-table-cell": "3.20.4",
|
"@tiptap/extension-table-cell": "3.21.0",
|
||||||
"@tiptap/extension-table-header": "3.20.4",
|
"@tiptap/extension-table-header": "3.21.0",
|
||||||
"@tiptap/extension-table-row": "3.20.4",
|
"@tiptap/extension-table-row": "3.21.0",
|
||||||
"@tiptap/extension-task-item": "3.20.4",
|
"@tiptap/extension-task-item": "3.21.0",
|
||||||
"@tiptap/extension-task-list": "3.20.4",
|
"@tiptap/extension-task-list": "3.21.0",
|
||||||
"@tiptap/extension-text-align": "3.20.4",
|
"@tiptap/extension-text-align": "3.21.0",
|
||||||
"@tiptap/extension-text-style": "3.20.4",
|
"@tiptap/extension-text-style": "3.21.0",
|
||||||
"@tiptap/extension-underline": "3.20.4",
|
"@tiptap/extension-underline": "3.21.0",
|
||||||
"@tiptap/react": "^3.20.4",
|
"@tiptap/react": "^3.21.0",
|
||||||
"@tiptap/starter-kit": "^3.20.4",
|
"@tiptap/starter-kit": "^3.21.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
"mantine-form-zod-resolver": "^1.3.0",
|
"mantine-form-zod-resolver": "^1.3.0",
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"recharts": "^2.15.4",
|
"recharts": "^2.15.4",
|
||||||
"video.js": "^8.23.7",
|
"video.js": "^8.23.7",
|
||||||
"zod": "^4.2.1"
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { WidgetOptionType } from "../options";
|
import type { WidgetOptionType } from "../options";
|
||||||
import { WidgetAppInput } from "./widget-app-input";
|
import { WidgetAppInput } from "./widget-app-input";
|
||||||
|
import { WidgetDynamicSelectInput } from "./widget-dynamic-select-input";
|
||||||
import { WidgetLocationInput } from "./widget-location-input";
|
import { WidgetLocationInput } from "./widget-location-input";
|
||||||
import { WidgetMultiTextInput } from "./widget-multi-text-input";
|
import { WidgetMultiTextInput } from "./widget-multi-text-input";
|
||||||
import { WidgetMultiReleasesRepositoriesInput } from "./widget-multiReleasesRepositories-input";
|
import { WidgetMultiReleasesRepositoriesInput } from "./widget-multiReleasesRepositories-input";
|
||||||
@@ -23,6 +24,7 @@ const mapping = {
|
|||||||
app: WidgetAppInput,
|
app: WidgetAppInput,
|
||||||
sortableItemList: WidgetSortedItemListInput,
|
sortableItemList: WidgetSortedItemListInput,
|
||||||
multiReleasesRepositories: WidgetMultiReleasesRepositoriesInput,
|
multiReleasesRepositories: WidgetMultiReleasesRepositoriesInput,
|
||||||
|
dynamicSelect: WidgetDynamicSelectInput,
|
||||||
} satisfies Record<WidgetOptionType, unknown>;
|
} satisfies Record<WidgetOptionType, unknown>;
|
||||||
|
|
||||||
export const getInputForType = <TType extends WidgetOptionType>(type: TType) => {
|
export const getInputForType = <TType extends WidgetOptionType>(type: TType) => {
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Group, Loader, Select } from "@mantine/core";
|
||||||
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
|
import { IconCheck } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
import { translateIfNecessary } from "@homarr/translation";
|
||||||
|
import { useI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
|
import type { CommonWidgetInputProps } from "./common";
|
||||||
|
import { useWidgetInputTranslation } from "./common";
|
||||||
|
import { useFormContext } from "./form";
|
||||||
|
|
||||||
|
export interface DynamicSelectOption {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WidgetDynamicSelectInput = ({ property, kind, options }: CommonWidgetInputProps<"dynamicSelect">) => {
|
||||||
|
const t = useI18n();
|
||||||
|
const tWidget = useWidgetInputTranslation(kind, property);
|
||||||
|
const form = useFormContext();
|
||||||
|
const inputProps = form.getInputProps(`options.${property}`);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 300);
|
||||||
|
const { isPending, options: selectOptions } = options.useOptions(debouncedSearch, form.values.integrationIds);
|
||||||
|
const value = inputProps.value as DynamicSelectOption | null;
|
||||||
|
const onChange = inputProps.onChange as (value: DynamicSelectOption | null) => void;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
label={tWidget("label")}
|
||||||
|
data={selectOptions
|
||||||
|
.map((option) =>
|
||||||
|
typeof option === "string"
|
||||||
|
? option
|
||||||
|
: {
|
||||||
|
value: option.value,
|
||||||
|
label: translateIfNecessary(t, option.label) ?? option.value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.concat(
|
||||||
|
typeof value === "object" &&
|
||||||
|
value !== null &&
|
||||||
|
!selectOptions.some((option) =>
|
||||||
|
typeof option === "string" ? option === value.value : option.value === value.value,
|
||||||
|
)
|
||||||
|
? {
|
||||||
|
value: value.value,
|
||||||
|
label: value.label,
|
||||||
|
}
|
||||||
|
: [],
|
||||||
|
)}
|
||||||
|
searchValue={search}
|
||||||
|
onSearchChange={setSearch}
|
||||||
|
leftSection={isPending && <Loader size="xs" />}
|
||||||
|
renderOption={({ option, checked }) => {
|
||||||
|
return (
|
||||||
|
<Group flex="1" gap="xs">
|
||||||
|
{option.label}
|
||||||
|
{checked && (
|
||||||
|
<IconCheck
|
||||||
|
style={{ marginInlineStart: "auto" }}
|
||||||
|
color="currentColor"
|
||||||
|
opacity={0.6}
|
||||||
|
size={18}
|
||||||
|
stroke={1.5}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
description={options.withDescription ? tWidget("description") : undefined}
|
||||||
|
searchable
|
||||||
|
{...inputProps}
|
||||||
|
value={value?.value ?? null}
|
||||||
|
onChange={(value) =>
|
||||||
|
onChange(
|
||||||
|
value
|
||||||
|
? {
|
||||||
|
value,
|
||||||
|
label:
|
||||||
|
selectOptions.filter((option) => typeof option === "object").find((option) => option.value === value)
|
||||||
|
?.label ?? value,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -42,6 +42,7 @@ import * as smartHomeExecuteAutomation from "./smart-home/execute-automation";
|
|||||||
import * as stockPrice from "./stocks";
|
import * as stockPrice from "./stocks";
|
||||||
import * as systemDisks from "./system-disks";
|
import * as systemDisks from "./system-disks";
|
||||||
import * as systemResources from "./system-resources";
|
import * as systemResources from "./system-resources";
|
||||||
|
import * as timetable from "./timetable";
|
||||||
import * as tracearr from "./tracearr";
|
import * as tracearr from "./tracearr";
|
||||||
import * as video from "./video";
|
import * as video from "./video";
|
||||||
import * as weather from "./weather";
|
import * as weather from "./weather";
|
||||||
@@ -82,6 +83,7 @@ export const widgetImports = {
|
|||||||
systemResources,
|
systemResources,
|
||||||
coolify,
|
coolify,
|
||||||
systemDisks,
|
systemDisks,
|
||||||
|
timetable,
|
||||||
"immich-serverStats": immichServerStats,
|
"immich-serverStats": immichServerStats,
|
||||||
"immich-albumCarousel": immichAlbumCarousel,
|
"immich-albumCarousel": immichAlbumCarousel,
|
||||||
tracearr,
|
tracearr,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { ZodType } from "zod/v4";
|
|||||||
|
|
||||||
import type { IntegrationKind } from "@homarr/definitions";
|
import type { IntegrationKind } from "@homarr/definitions";
|
||||||
|
|
||||||
|
import type { DynamicSelectOption } from "./_inputs/widget-dynamic-select-input";
|
||||||
import type { inferSelectOptionValue, SelectOption } from "./_inputs/widget-select-input";
|
import type { inferSelectOptionValue, SelectOption } from "./_inputs/widget-select-input";
|
||||||
import type { ReleasesRepository } from "./releases/releases-repository";
|
import type { ReleasesRepository } from "./releases/releases-repository";
|
||||||
|
|
||||||
@@ -47,6 +48,16 @@ interface SelectInput<TOptions extends readonly SelectOption[]> extends CommonIn
|
|||||||
searchable?: boolean;
|
searchable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DynamicSelectInput extends CommonInput<DynamicSelectOption | null> {
|
||||||
|
useOptions: (
|
||||||
|
query: string,
|
||||||
|
integrationIds: string[],
|
||||||
|
) => {
|
||||||
|
isPending: boolean;
|
||||||
|
options: DynamicSelectOption[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface NumberInput extends CommonInput<number> {
|
interface NumberInput extends CommonInput<number> {
|
||||||
validate: z.ZodNumber;
|
validate: z.ZodNumber;
|
||||||
step?: number;
|
step?: number;
|
||||||
@@ -82,6 +93,12 @@ const optionsFactory = {
|
|||||||
searchable: input.searchable ?? false,
|
searchable: input.searchable ?? false,
|
||||||
withDescription: input.withDescription ?? false,
|
withDescription: input.withDescription ?? false,
|
||||||
}),
|
}),
|
||||||
|
dynamicSelect: (input: DynamicSelectInput) => ({
|
||||||
|
type: "dynamicSelect" as const,
|
||||||
|
defaultValue: input.defaultValue ?? null,
|
||||||
|
useOptions: input.useOptions,
|
||||||
|
withDescription: input.withDescription ?? false,
|
||||||
|
}),
|
||||||
select: <const TOptions extends SelectOption[]>(input: SelectInput<TOptions>) => ({
|
select: <const TOptions extends SelectOption[]>(input: SelectInput<TOptions>) => ({
|
||||||
type: "select" as const,
|
type: "select" as const,
|
||||||
defaultValue: (input.defaultValue ?? input.options[0]) as inferSelectOptionValue<TOptions[number]>,
|
defaultValue: (input.defaultValue ?? input.options[0]) as inferSelectOptionValue<TOptions[number]>,
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { Badge, Center, Group, Stack, Text } from "@mantine/core";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
import { clientApi } from "@homarr/api/client";
|
||||||
|
import { useScopedI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
|
import type { DynamicSelectOption } from "../_inputs/widget-dynamic-select-input";
|
||||||
|
import type { WidgetComponentProps } from "../definition";
|
||||||
|
|
||||||
|
export default function TimetableWidget({ options, integrationIds }: WidgetComponentProps<"timetable">) {
|
||||||
|
// It will always have at least one integration as otherwise the NoIntegrationSelectedError would be thrown in item-content.tsx
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const integrationId = integrationIds[0]!;
|
||||||
|
const t = useScopedI18n("widget.timetable");
|
||||||
|
|
||||||
|
if (!options.station) {
|
||||||
|
return <Center h="100%">{t("noStation")}</Center>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <TimetableWidgetInner station={options.station} integrationId={integrationId} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimetableWidgetInnerProps {
|
||||||
|
station: DynamicSelectOption;
|
||||||
|
integrationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimetableWidgetInner = ({ station, integrationId }: TimetableWidgetInnerProps) => {
|
||||||
|
const [timetable] = clientApi.widget.timetable.getTimetable.useSuspenseQuery({
|
||||||
|
integrationId,
|
||||||
|
stationId: station.value,
|
||||||
|
limit: 10,
|
||||||
|
});
|
||||||
|
const t = useScopedI18n("widget.timetable");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack w="100%" gap="xs" p="sm">
|
||||||
|
<Text fw="bold">{t("title", { station: station.label })}</Text>
|
||||||
|
{timetable.entries.map((entry) => (
|
||||||
|
<Group key={`${entry.timestamp.toISOString()}-${entry.location}`} justify="space-between" w="100%">
|
||||||
|
<Group gap="sm">
|
||||||
|
{entry.line && (
|
||||||
|
<Badge
|
||||||
|
size="sm"
|
||||||
|
color={entry.line.color ?? undefined}
|
||||||
|
variant={entry.line.color ? "filled" : "default"}
|
||||||
|
w={32}
|
||||||
|
p={0}
|
||||||
|
radius={0}
|
||||||
|
>
|
||||||
|
{entry.line.name}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
<Text size="sm">
|
||||||
|
{dayjs(entry.timestamp).format("HH:mm")}{" "}
|
||||||
|
{entry.delay >= 1 && (
|
||||||
|
<Text size="sm" span c="red">
|
||||||
|
+{entry.delay}'
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text size="sm">{entry.location}</Text>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{entry.platform && (
|
||||||
|
<Text size="sm" c={entry.platform.hasChanged ? "red" : undefined}>
|
||||||
|
{entry.platform.name}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { IconBusStop } from "@tabler/icons-react";
|
||||||
|
|
||||||
|
import { clientApi } from "@homarr/api/client";
|
||||||
|
|
||||||
|
import { createWidgetDefinition } from "../definition";
|
||||||
|
import { optionsBuilder } from "../options";
|
||||||
|
|
||||||
|
export const { componentLoader, definition } = createWidgetDefinition("timetable", {
|
||||||
|
icon: IconBusStop,
|
||||||
|
supportedIntegrations: ["searchCh"],
|
||||||
|
createOptions() {
|
||||||
|
return optionsBuilder.from((factory) => ({
|
||||||
|
station: factory.dynamicSelect({
|
||||||
|
useOptions(query, integrationIds) {
|
||||||
|
const { data: stations, isPending } = clientApi.widget.timetable.searchStations.useQuery(
|
||||||
|
{
|
||||||
|
integrationId: integrationIds[0] ?? "",
|
||||||
|
query,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: integrationIds.length > 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isPending,
|
||||||
|
options:
|
||||||
|
stations?.map((station) => ({
|
||||||
|
label: station.name,
|
||||||
|
value: station.id,
|
||||||
|
})) ?? [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
}).withDynamicImport(() => import("./component"));
|
||||||
Generated
+916
-1077
File diff suppressed because it is too large.
Load diff
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user