Compare commits
No files matched your search
@@ -5,6 +5,7 @@ node_modules
|
||||
npm-debug.log
|
||||
README.md
|
||||
.next
|
||||
.turbo
|
||||
.git
|
||||
dev
|
||||
.build
|
||||
|
||||
@@ -33,6 +33,12 @@ body:
|
||||
options:
|
||||
# The below comment is used to insert a new version with on-release.yml
|
||||
#NEXT_VERSION#
|
||||
- 1.53.0
|
||||
- 1.52.0
|
||||
- 1.51.0
|
||||
- 1.50.1
|
||||
- 1.50.0
|
||||
- 1.49.1
|
||||
- 1.49.0
|
||||
- 1.48.0
|
||||
- 1.47.0
|
||||
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
- uses: actions/setup-node@v6
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
with:
|
||||
node-version: 24.12.0
|
||||
node-version: 24.13.0
|
||||
cache: "pnpm"
|
||||
- run: npm i -g pnpm
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
prebuilt-debian-validate-arm64:
|
||||
runs-on: ubuntu-24.04-
|
||||
runs-on: ubuntu-24.04-arm
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -2,7 +2,7 @@ permissions: {}
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
trigger-docs-release:
|
||||
@@ -37,6 +37,7 @@ jobs:
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
update-bug-report-template:
|
||||
name: Update Bug Report Template
|
||||
runs-on: ubuntu-latest
|
||||
@@ -54,23 +55,20 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ steps.obtainToken.outputs.token }}
|
||||
ref: dev
|
||||
- name: Setup
|
||||
uses: ./tooling/github/setup
|
||||
- run: pnpm run scripts:update-bug-report-template
|
||||
env:
|
||||
NEXT_VERSION: ${{ github.event.release.tag_name }}
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.email "175486441+homarr-releases[bot]@users.noreply.github.com"
|
||||
git config --global user.name "Releases Homarr"
|
||||
git checkout -b update-bug-report-template
|
||||
git add .
|
||||
git commit -m "chore: update bug report template"
|
||||
|
||||
- name: Create Pull Request
|
||||
id: create-pull-request
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
add-paths: .github/ISSUE_TEMPLATE/bug_report.yml
|
||||
commit-message: "chore: update bug report template"
|
||||
author: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
|
||||
token: ${{ steps.obtainToken.outputs.token }}
|
||||
branch: update-bug-report-template
|
||||
base: dev
|
||||
|
||||
+129
@@ -1,3 +1,132 @@
|
||||
## [1.53.1](https://github.com/homarr-labs/homarr/compare/v1.53.0...v1.53.1) (2026-02-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @ianvs/prettier-plugin-sort-imports to ^4.7.1 ([#5021](https://github.com/homarr-labs/homarr/issues/5021)) ([5946e6c](https://github.com/homarr-labs/homarr/commit/5946e6c7193eded77b98c76aedff71a7bb912574))
|
||||
* **deps:** update dependency @octokit/auth-app to ^8.2.0 ([#5009](https://github.com/homarr-labs/homarr/issues/5009)) ([4a86361](https://github.com/homarr-labs/homarr/commit/4a86361134da404462b1a9f8ff98fe3504fade0d))
|
||||
* **deps:** update dependency dotenv to ^17.2.4 ([#5029](https://github.com/homarr-labs/homarr/issues/5029)) ([e8c7f45](https://github.com/homarr-labs/homarr/commit/e8c7f454768044540ee37d75fc73654b74315390))
|
||||
* **deps:** update dependency fast-xml-parser to ^5.3.5 ([#5050](https://github.com/homarr-labs/homarr/issues/5050)) ([30f561f](https://github.com/homarr-labs/homarr/commit/30f561f85ad2abb90ccb9fa3292155d51f1f379b))
|
||||
* **deps:** update dependency fastify to ^5.7.4 ([#5027](https://github.com/homarr-labs/homarr/issues/5027)) ([18e16d6](https://github.com/homarr-labs/homarr/commit/18e16d687f8137e5b5c55cf2385436b714a2d27b))
|
||||
* **deps:** update dependency glob to ^13.0.1 ([#5013](https://github.com/homarr-labs/homarr/issues/5013)) ([3b21c3e](https://github.com/homarr-labs/homarr/commit/3b21c3ef765add0f7cdc8f66ae8f1b7aac3cf51e))
|
||||
* **deps:** update dependency isomorphic-dompurify to ^2.36.0 ([#5041](https://github.com/homarr-labs/homarr/issues/5041)) ([e561fc5](https://github.com/homarr-labs/homarr/commit/e561fc550fab2207212ae108a5177a248795cf66))
|
||||
* **deps:** update dependency jotai to ^2.17.1 ([#5019](https://github.com/homarr-labs/homarr/issues/5019)) ([da96566](https://github.com/homarr-labs/homarr/commit/da965668d021ebebd8c8a7e2a4560577a6b0828b))
|
||||
* **deps:** update dependency ldapts to v8.1.5 ([#5004](https://github.com/homarr-labs/homarr/issues/5004)) ([c3932b1](https://github.com/homarr-labs/homarr/commit/c3932b18f03b86e8ee049c14ace63119fd2ab0d6))
|
||||
* **deps:** update dependency ldapts to v8.1.6 ([#5018](https://github.com/homarr-labs/homarr/issues/5018)) ([c92d487](https://github.com/homarr-labs/homarr/commit/c92d487ceaa9159579ef4352d763429887953bb5))
|
||||
* **deps:** update dependency mysql2 to v3.16.3 ([#5014](https://github.com/homarr-labs/homarr/issues/5014)) ([04b3598](https://github.com/homarr-labs/homarr/commit/04b35980f5997e744a455a847f28b1a676f9861e))
|
||||
* **deps:** update dependency next-intl to v4.8.2 ([#5005](https://github.com/homarr-labs/homarr/issues/5005)) ([b67fecd](https://github.com/homarr-labs/homarr/commit/b67fecd3963bc3112ab8ecd91385bbe13c982290))
|
||||
* **deps:** update dependency tsdav to ^2.1.7 ([#5033](https://github.com/homarr-labs/homarr/issues/5033)) ([d694b1a](https://github.com/homarr-labs/homarr/commit/d694b1a5410833a791d57fb3dd4650003510140e))
|
||||
* **deps:** update dependency video.js to ^8.23.7 ([#5034](https://github.com/homarr-labs/homarr/issues/5034)) ([6efabd4](https://github.com/homarr-labs/homarr/commit/6efabd436849b9fdea0d060fe7beaa9c67424c29))
|
||||
* **deps:** update tiptap monorepo to v3.19.0 ([#5012](https://github.com/homarr-labs/homarr/issues/5012)) ([ad41a91](https://github.com/homarr-labs/homarr/commit/ad41a918ef72f322015a02e9601c43728e680b34))
|
||||
* **deps:** update turbo monorepo to ^2.8.2 ([#5010](https://github.com/homarr-labs/homarr/issues/5010)) ([b479fea](https://github.com/homarr-labs/homarr/commit/b479fea6eb0b025b55668201160a5e57267de2e7))
|
||||
* **deps:** update turbo monorepo to ^2.8.3 ([#5016](https://github.com/homarr-labs/homarr/issues/5016)) ([f1d2f07](https://github.com/homarr-labs/homarr/commit/f1d2f07f3888e7567b56ae61a9d137255a2b8529))
|
||||
* **sections:** dynamic sections vanish when adding category ([#5046](https://github.com/homarr-labs/homarr/issues/5046)) ([60fcb71](https://github.com/homarr-labs/homarr/commit/60fcb71bd2616af3e99b20cbbcd1b0417a0fff76))
|
||||
* **seerr:** use reasonable limits for amount of returned entries ([#5001](https://github.com/homarr-labs/homarr/issues/5001)) ([be1a26a](https://github.com/homarr-labs/homarr/commit/be1a26a98d2ed9d25969ded5ae64705c001f71dd))
|
||||
|
||||
## [1.53.0](https://github.com/homarr-labs/homarr/compare/v1.52.0...v1.53.0) (2026-02-06)
|
||||
|
||||
### Features
|
||||
|
||||
* **analytics:** consolidate server analytics into single comprehensive Umami event ([#4938](https://github.com/homarr-labs/homarr/issues/4938)) ([10848db](https://github.com/homarr-labs/homarr/commit/10848db595b2254b1485ea168d1c327b907d9ff8))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @next/eslint-plugin-next to v16.1.5 ([#4959](https://github.com/homarr-labs/homarr/issues/4959)) ([ec40309](https://github.com/homarr-labs/homarr/commit/ec40309cde2280cf689856d4f78fb1500b583130))
|
||||
* **deps:** update dependency @paralleldrive/cuid2 to ^3.3.0 ([#4952](https://github.com/homarr-labs/homarr/issues/4952)) ([a9db68b](https://github.com/homarr-labs/homarr/commit/a9db68bd3eaea6a4564cd6842054ea60cc45e3a3))
|
||||
* **deps:** update dependency @tanstack/react-query-devtools to ^5.91.3 ([#4985](https://github.com/homarr-labs/homarr/issues/4985)) ([1115ad1](https://github.com/homarr-labs/homarr/commit/1115ad1364bca84dee33aa42499c986bdad2c528))
|
||||
* **deps:** update dependency fast-xml-parser to ^5.3.4 ([#4982](https://github.com/homarr-labs/homarr/issues/4982)) ([460d53c](https://github.com/homarr-labs/homarr/commit/460d53cafa1abeebf4dea7b1bb1e920267c2f9fe))
|
||||
* **deps:** update dependency fastify to ^5.7.2 ([#4955](https://github.com/homarr-labs/homarr/issues/4955)) ([0eab26c](https://github.com/homarr-labs/homarr/commit/0eab26cdace47c0b19519372a3daa6998e0c8207))
|
||||
* **deps:** update dependency jotai to ^2.17.0 ([#4972](https://github.com/homarr-labs/homarr/issues/4972)) ([e67483f](https://github.com/homarr-labs/homarr/commit/e67483fea56f0b06fa54e68b8dd62fd0b5dedadd))
|
||||
* **deps:** update dependency ldapts to v8.1.4 ([#4993](https://github.com/homarr-labs/homarr/issues/4993)) ([c2d9f18](https://github.com/homarr-labs/homarr/commit/c2d9f18e4ef28f063e4abf1175cd90ba746410b7))
|
||||
* **deps:** update dependency mysql2 to v3.16.2 ([#4961](https://github.com/homarr-labs/homarr/issues/4961)) ([5c6eafb](https://github.com/homarr-labs/homarr/commit/5c6eafb272436b78d4668d562d50001bca48e6d3))
|
||||
* **deps:** update dependency next-intl to v4.8.0 ([#4977](https://github.com/homarr-labs/homarr/issues/4977)) ([c3a7ab5](https://github.com/homarr-labs/homarr/commit/c3a7ab52ee3acbcf47255d5813317c3cf67ea49f))
|
||||
* **deps:** update dependency next-intl to v4.8.1 ([#4986](https://github.com/homarr-labs/homarr/issues/4986)) ([62b6939](https://github.com/homarr-labs/homarr/commit/62b6939b92f29618cc6d13a189b65df1922779ba))
|
||||
* **deps:** update dependency pg to ^8.18.0 ([#4989](https://github.com/homarr-labs/homarr/issues/4989)) ([c3071a3](https://github.com/homarr-labs/homarr/commit/c3071a3c95f3a30438050aa37c65950cccabf681))
|
||||
* **deps:** update dependency typescript-eslint to ^8.54.0 ([#4958](https://github.com/homarr-labs/homarr/issues/4958)) ([e88d126](https://github.com/homarr-labs/homarr/commit/e88d126355b96ac5060b552047c191f8b284461c))
|
||||
* **deps:** update dependency video.js to ^8.23.6 ([#4992](https://github.com/homarr-labs/homarr/issues/4992)) ([4209f7c](https://github.com/homarr-labs/homarr/commit/4209f7c70e75870f56b93a97f3b6053962ee3ff7))
|
||||
* **deps:** update mantine monorepo to ^8.3.14 ([#4998](https://github.com/homarr-labs/homarr/issues/4998)) ([0fafa50](https://github.com/homarr-labs/homarr/commit/0fafa505ac1c7f4ddb33ee5e8dedca9a883164ba))
|
||||
* **deps:** update nextjs monorepo to v16.1.6 ([#4969](https://github.com/homarr-labs/homarr/issues/4969)) ([7c3fe3d](https://github.com/homarr-labs/homarr/commit/7c3fe3d6d47879c67950b3dc4d7e39adbc06c146))
|
||||
* **deps:** update react monorepo to v19.2.4 ([#4960](https://github.com/homarr-labs/homarr/issues/4960)) ([d3e0b47](https://github.com/homarr-labs/homarr/commit/d3e0b47875998fc766e1c5f0481244fc659b0c49))
|
||||
* **deps:** update tiptap monorepo to v3.18.0 ([#4975](https://github.com/homarr-labs/homarr/issues/4975)) ([24e24dc](https://github.com/homarr-labs/homarr/commit/24e24dc634a9e5a25ab934541225996e62ac31be))
|
||||
* **deps:** update trpc monorepo to ^11.9.0 ([#4970](https://github.com/homarr-labs/homarr/issues/4970)) ([aeb6e0f](https://github.com/homarr-labs/homarr/commit/aeb6e0f40659bf1c514380a6c96858afdaf7e6db))
|
||||
* **deps:** update turbo monorepo to ^2.8.0 ([#4978](https://github.com/homarr-labs/homarr/issues/4978)) ([054ca57](https://github.com/homarr-labs/homarr/commit/054ca57188656ddac7aaa16f792269eaa2ef319b))
|
||||
* **deps:** update turbo monorepo to ^2.8.1 ([#4991](https://github.com/homarr-labs/homarr/issues/4991)) ([43b6246](https://github.com/homarr-labs/homarr/commit/43b6246876debccbe7239c8ee67753e17527e469))
|
||||
|
||||
## [1.52.0](https://github.com/homarr-labs/homarr/compare/v1.51.0...v1.52.0) (2026-01-30)
|
||||
|
||||
### Features
|
||||
|
||||
* **installations:** support docker secrets file for encryption key ([#4946](https://github.com/homarr-labs/homarr/issues/4946)) ([9ba8213](https://github.com/homarr-labs/homarr/commit/9ba82133da2ae472952358f84d8ee1758c0f108a))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @tanstack/react-query to ^5.90.20 ([#4939](https://github.com/homarr-labs/homarr/issues/4939)) ([840940b](https://github.com/homarr-labs/homarr/commit/840940bab663e728b8516ac48f913fef2b5c61b7))
|
||||
* **deps:** update dependency next to v16.1.5 [security] ([#4937](https://github.com/homarr-labs/homarr/issues/4937)) ([aeb1df1](https://github.com/homarr-labs/homarr/commit/aeb1df122926c7bed71bea9b21f1c42c45cc9b3e))
|
||||
* **deps:** update dependency pg to ^8.17.2 ([#4916](https://github.com/homarr-labs/homarr/issues/4916)) ([a50a29a](https://github.com/homarr-labs/homarr/commit/a50a29a778689efcca0afc2b640fdb497e6e7d98))
|
||||
* **deps:** update dependency prettier to ^3.8.1 ([#4925](https://github.com/homarr-labs/homarr/issues/4925)) ([a727b8e](https://github.com/homarr-labs/homarr/commit/a727b8e323b9fb7cba536909a51fabde0a992a69))
|
||||
* **deps:** update dependency sass to ^1.97.3 ([#4927](https://github.com/homarr-labs/homarr/issues/4927)) ([b1658a5](https://github.com/homarr-labs/homarr/commit/b1658a5d01b5c12f391af9d326ec98b3da000f9e))
|
||||
* **deps:** update dependency typescript-eslint to ^8.53.1 ([#4904](https://github.com/homarr-labs/homarr/issues/4904)) ([c58da00](https://github.com/homarr-labs/homarr/commit/c58da0095738352515d6d8c5e81b3c34c8726ffe))
|
||||
* **deps:** update mantine monorepo to ^8.3.13 ([#4911](https://github.com/homarr-labs/homarr/issues/4911)) ([f2eec74](https://github.com/homarr-labs/homarr/commit/f2eec748494480e773b45a309aa89ae3862e2b05))
|
||||
* **deps:** update nextjs monorepo to v16.1.4 ([#4909](https://github.com/homarr-labs/homarr/issues/4909)) ([99a2574](https://github.com/homarr-labs/homarr/commit/99a2574d80ad5a7dfe3b9c92d43bd4b9423bf22d))
|
||||
* **deps:** update tiptap monorepo to v3.16.0 ([#4912](https://github.com/homarr-labs/homarr/issues/4912)) ([2354510](https://github.com/homarr-labs/homarr/commit/23545101b61e456fd886eedb4b3d86b8cc606639))
|
||||
* **deps:** update tiptap monorepo to v3.17.0 ([#4940](https://github.com/homarr-labs/homarr/issues/4940)) ([d030261](https://github.com/homarr-labs/homarr/commit/d03026187cb6451d727167b02ac138f9c7ccc754))
|
||||
* **deps:** update tiptap monorepo to v3.17.1 ([#4945](https://github.com/homarr-labs/homarr/issues/4945)) ([b354085](https://github.com/homarr-labs/homarr/commit/b354085c7adfb6e5f3cad51b5b825426e072e874))
|
||||
* **deps:** update turbo monorepo to ^2.7.6 ([#4947](https://github.com/homarr-labs/homarr/issues/4947)) ([e078237](https://github.com/homarr-labs/homarr/commit/e078237e31d11029987c6c4370fd8f65023b87bc))
|
||||
|
||||
## [1.51.0](https://github.com/homarr-labs/homarr/compare/v1.50.1...v1.51.0) (2026-01-23)
|
||||
|
||||
### Features
|
||||
|
||||
* **integrations:** add Coolify integration and widget ([#4789](https://github.com/homarr-labs/homarr/issues/4789)) ([81dac58](https://github.com/homarr-labs/homarr/commit/81dac580050a86e34135beef09b876089777f1c7))
|
||||
* **widgets:** add disks widget ([#4445](https://github.com/homarr-labs/homarr/issues/4445)) ([62caa71](https://github.com/homarr-labs/homarr/commit/62caa71a48c38f59f109666e83f26019586a5ca0))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @tanstack/react-query to ^5.90.17 ([#4873](https://github.com/homarr-labs/homarr/issues/4873)) ([624bd55](https://github.com/homarr-labs/homarr/commit/624bd55b117b2b9f9109aecdeabf1515c567656e))
|
||||
* **deps:** update dependency @tanstack/react-query to ^5.90.19 ([#4888](https://github.com/homarr-labs/homarr/issues/4888)) ([fa84bd5](https://github.com/homarr-labs/homarr/commit/fa84bd569fdf6c9aecfcb9b4d8fb20d14215160b))
|
||||
* **deps:** update dependency better-sqlite3 to ^12.6.0 ([#4853](https://github.com/homarr-labs/homarr/issues/4853)) ([9d4fb9e](https://github.com/homarr-labs/homarr/commit/9d4fb9eb462a0297618c70f619618f94d8857e1f))
|
||||
* **deps:** update dependency better-sqlite3 to ^12.6.2 ([#4891](https://github.com/homarr-labs/homarr/issues/4891)) ([63a0793](https://github.com/homarr-labs/homarr/commit/63a0793c6ea02456a49a07635b927adf3a314ee0))
|
||||
* **deps:** update dependency fastify to ^5.7.0 ([#4882](https://github.com/homarr-labs/homarr/issues/4882)) ([b9bc9dd](https://github.com/homarr-labs/homarr/commit/b9bc9dd38d22f03a0faa59a68056192c3f1519d4))
|
||||
* **deps:** update dependency fastify to ^5.7.1 ([#4887](https://github.com/homarr-labs/homarr/issues/4887)) ([02e722a](https://github.com/homarr-labs/homarr/commit/02e722aa4531e816bb563628ebbb7e8b0cf93ce3))
|
||||
* **deps:** update dependency ioredis to v5.9.2 ([#4881](https://github.com/homarr-labs/homarr/issues/4881)) ([e3ba1ee](https://github.com/homarr-labs/homarr/commit/e3ba1ee931b0e53fb94bf0e50fae81eecfade237))
|
||||
* **deps:** update dependency jotai to ^2.16.2 ([#4869](https://github.com/homarr-labs/homarr/issues/4869)) ([2e58acc](https://github.com/homarr-labs/homarr/commit/2e58acc3bd5da474bfe0636a58b43759947ef2d2))
|
||||
* **deps:** update dependency mysql2 to v3.16.1 ([#4886](https://github.com/homarr-labs/homarr/issues/4886)) ([40ee667](https://github.com/homarr-labs/homarr/commit/40ee667832124075b0730a54a4b8019365ab8794))
|
||||
* **deps:** update dependency pg to ^8.17.0 ([#4874](https://github.com/homarr-labs/homarr/issues/4874)) ([b923891](https://github.com/homarr-labs/homarr/commit/b923891fac4cd61c5a1a8505d5bcfb737255196f))
|
||||
* **deps:** update dependency pg to ^8.17.1 ([#4877](https://github.com/homarr-labs/homarr/issues/4877)) ([fcd4171](https://github.com/homarr-labs/homarr/commit/fcd417198f4a1afc250455bf25594a32f61b1070))
|
||||
* **deps:** update dependency prettier to ^3.8.0 ([#4876](https://github.com/homarr-labs/homarr/issues/4876)) ([3ade915](https://github.com/homarr-labs/homarr/commit/3ade915d35540578a7f193baf99cbdbf89103b97))
|
||||
* **deps:** update dependency react-error-boundary to ^6.1.0 ([#4884](https://github.com/homarr-labs/homarr/issues/4884)) ([41b02b6](https://github.com/homarr-labs/homarr/commit/41b02b63d9a1f9b73cc54371bbc1b6161a4d76d0))
|
||||
* **deps:** update dependency typescript-eslint to ^8.53.0 ([#4863](https://github.com/homarr-labs/homarr/issues/4863)) ([e9020e6](https://github.com/homarr-labs/homarr/commit/e9020e68ecf9e33df983108e7ab239776874413c))
|
||||
* **deps:** update nextjs monorepo to v16.1.2 ([#4875](https://github.com/homarr-labs/homarr/issues/4875)) ([c6be8c5](https://github.com/homarr-labs/homarr/commit/c6be8c5475722e5a6dbc886e5e7f24ef8a04c470))
|
||||
* **deps:** update nextjs monorepo to v16.1.3 ([#4889](https://github.com/homarr-labs/homarr/issues/4889)) ([5a8e864](https://github.com/homarr-labs/homarr/commit/5a8e864630b61595b53a9e849973460ffdce5fd2))
|
||||
* **deps:** update turbo monorepo to ^2.7.4 ([#4861](https://github.com/homarr-labs/homarr/issues/4861)) ([0c38e2f](https://github.com/homarr-labs/homarr/commit/0c38e2f290fc6dcb9608b338ee38409afb3beabe))
|
||||
* **deps:** update turbo monorepo to ^2.7.5 ([#4892](https://github.com/homarr-labs/homarr/issues/4892)) ([c847f60](https://github.com/homarr-labs/homarr/commit/c847f6004dc4c166969ee1248ec64b7d27ba8362))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **memory:** lazy routers ([#4868](https://github.com/homarr-labs/homarr/issues/4868)) ([1623fba](https://github.com/homarr-labs/homarr/commit/1623fbaaeb41b16e13dcd401a00f70711a283bf9))
|
||||
|
||||
## [1.50.1](https://github.com/homarr-labs/homarr/compare/v1.50.0...v1.50.1) (2026-01-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency ioredis to v5.9.0 ([#4827](https://github.com/homarr-labs/homarr/issues/4827)) ([155d4f2](https://github.com/homarr-labs/homarr/commit/155d4f2270b3b35c4ad696499b0aaa0b10100989))
|
||||
* **deps:** update dependency ioredis to v5.9.1 ([#4844](https://github.com/homarr-labs/homarr/issues/4844)) ([5d00642](https://github.com/homarr-labs/homarr/commit/5d006422688cf3fe7440ae3ab66ad3b2cc9286a8))
|
||||
* **deps:** update dependency ldapts to v8.1.3 ([#4841](https://github.com/homarr-labs/homarr/issues/4841)) ([7fa9907](https://github.com/homarr-labs/homarr/commit/7fa990712bddc88bbb3bbe01e758bfef7cb4dea3))
|
||||
* **deps:** update dependency react-error-boundary to ^6.0.2 ([#4821](https://github.com/homarr-labs/homarr/issues/4821)) ([e9ba102](https://github.com/homarr-labs/homarr/commit/e9ba102cf668c0ef54628d3dbc6e6cbda2d059b5))
|
||||
* **deps:** update dependency react-error-boundary to ^6.0.3 ([#4847](https://github.com/homarr-labs/homarr/issues/4847)) ([c24006d](https://github.com/homarr-labs/homarr/commit/c24006dca691f26a7187c0ba112d9ed2ec424bb0))
|
||||
* **deps:** update dependency sass to ^1.97.2 ([#4835](https://github.com/homarr-labs/homarr/issues/4835)) ([07ba531](https://github.com/homarr-labs/homarr/commit/07ba53165c90d675aa54c5ad3fca930fef5cd35e))
|
||||
* **deps:** update dependency typescript-eslint to ^8.52.0 ([#4830](https://github.com/homarr-labs/homarr/issues/4830)) ([fa8af6a](https://github.com/homarr-labs/homarr/commit/fa8af6a453b846af2305db3c318c8c1c303a3a88))
|
||||
* **deps:** update dependency undici to v7.17.0 ([#4826](https://github.com/homarr-labs/homarr/issues/4826)) ([21a91b1](https://github.com/homarr-labs/homarr/commit/21a91b1489ff1a1f0a1428a3f50f00db8932da72))
|
||||
* **deps:** update dependency undici to v7.18.0 ([#4829](https://github.com/homarr-labs/homarr/issues/4829)) ([b76ab29](https://github.com/homarr-labs/homarr/commit/b76ab2961d84081c9ef04d76193f21a1068e72d9))
|
||||
* **deps:** update dependency undici to v7.18.2 ([#4837](https://github.com/homarr-labs/homarr/issues/4837)) ([16733c2](https://github.com/homarr-labs/homarr/commit/16733c2a0efabcbc5b693a8b6f82833f6fc344b7))
|
||||
* **deps:** update dependency ws to ^8.19.0 ([#4833](https://github.com/homarr-labs/homarr/issues/4833)) ([629359e](https://github.com/homarr-labs/homarr/commit/629359e3e44ddad880842cfd576edb514f80d61a))
|
||||
* **deps:** update mantine monorepo to ^8.3.11 ([#4822](https://github.com/homarr-labs/homarr/issues/4822)) ([0364e10](https://github.com/homarr-labs/homarr/commit/0364e102cd6c0f560bc49e0b9ccd71df0b75eed6))
|
||||
* **deps:** update mantine monorepo to ^8.3.12 ([#4848](https://github.com/homarr-labs/homarr/issues/4848)) ([275a346](https://github.com/homarr-labs/homarr/commit/275a346afaaebfa894ef2193f70966986a138dbb))
|
||||
* **deps:** update tiptap monorepo to v3.15.0 ([#4832](https://github.com/homarr-labs/homarr/issues/4832)) ([ab3ee4f](https://github.com/homarr-labs/homarr/commit/ab3ee4f78a739bdb3b49a81652420df6ec143b62))
|
||||
* **deps:** update tiptap monorepo to v3.15.1 ([#4838](https://github.com/homarr-labs/homarr/issues/4838)) ([d479b63](https://github.com/homarr-labs/homarr/commit/d479b63a803f2295ccdf20fcd31c418a4aab148c))
|
||||
* **deps:** update tiptap monorepo to v3.15.2 ([#4842](https://github.com/homarr-labs/homarr/issues/4842)) ([55c2666](https://github.com/homarr-labs/homarr/commit/55c2666f9d27ca34b6d028070f0d7b84e4c40e97))
|
||||
* **deps:** update tiptap monorepo to v3.15.3 ([#4843](https://github.com/homarr-labs/homarr/issues/4843)) ([b26cf0e](https://github.com/homarr-labs/homarr/commit/b26cf0e65fd8150e241465628df2cb8b5a72088e))
|
||||
* **deps:** update turbo monorepo to ^2.7.3 ([#4831](https://github.com/homarr-labs/homarr/issues/4831)) ([50410fe](https://github.com/homarr-labs/homarr/commit/50410fedb3f33ed131f923257e61ae2dca4e0288))
|
||||
|
||||
## [1.50.0](https://github.com/homarr-labs/homarr/compare/v1.49.1...v1.50.0) (2026-01-09)
|
||||
|
||||
### Features
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:24.12.0-alpine AS base
|
||||
FROM node:24.13.0-alpine AS base
|
||||
|
||||
FROM base AS builder
|
||||
RUN apk add --no-cache libc6-compat
|
||||
|
||||
+24
-24
@@ -48,21 +48,21 @@
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@homarr/widgets": "workspace:^0.1.0",
|
||||
"@mantine/colors-generator": "^8.3.10",
|
||||
"@mantine/core": "^8.3.10",
|
||||
"@mantine/dropzone": "^8.3.10",
|
||||
"@mantine/hooks": "^8.3.10",
|
||||
"@mantine/modals": "^8.3.10",
|
||||
"@mantine/tiptap": "^8.3.10",
|
||||
"@mantine/colors-generator": "^8.3.14",
|
||||
"@mantine/core": "^8.3.14",
|
||||
"@mantine/dropzone": "^8.3.14",
|
||||
"@mantine/hooks": "^8.3.14",
|
||||
"@mantine/modals": "^8.3.14",
|
||||
"@mantine/tiptap": "^8.3.14",
|
||||
"@million/lint": "1.0.14",
|
||||
"@tabler/icons-react": "^3.36.1",
|
||||
"@tanstack/react-query": "^5.90.16",
|
||||
"@tanstack/react-query-devtools": "^5.91.2",
|
||||
"@tanstack/react-query": "^5.90.20",
|
||||
"@tanstack/react-query-devtools": "^5.91.3",
|
||||
"@tanstack/react-query-next-experimental": "^5.91.0",
|
||||
"@trpc/client": "^11.8.1",
|
||||
"@trpc/next": "^11.8.1",
|
||||
"@trpc/react-query": "^11.8.1",
|
||||
"@trpc/server": "^11.8.1",
|
||||
"@trpc/client": "^11.9.0",
|
||||
"@trpc/next": "^11.9.0",
|
||||
"@trpc/react-query": "^11.9.0",
|
||||
"@trpc/server": "^11.9.0",
|
||||
"@xterm/addon-canvas": "^0.7.0",
|
||||
"@xterm/addon-fit": "0.11.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
@@ -70,20 +70,20 @@
|
||||
"chroma-js": "^3.2.0",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.2.3",
|
||||
"dotenv": "^17.2.4",
|
||||
"flag-icons": "^7.5.0",
|
||||
"glob": "^13.0.0",
|
||||
"isomorphic-dompurify": "^2.35.0",
|
||||
"jotai": "^2.16.1",
|
||||
"glob": "^13.0.1",
|
||||
"isomorphic-dompurify": "^2.36.0",
|
||||
"jotai": "^2.17.1",
|
||||
"mantine-react-table": "2.0.0-beta.9",
|
||||
"next": "16.1.1",
|
||||
"next": "16.1.6",
|
||||
"postcss-preset-mantine": "^1.18.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"react-error-boundary": "^6.0.1",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"react-error-boundary": "^6.1.0",
|
||||
"react-simple-code-editor": "^0.14.1",
|
||||
"sass": "^1.97.1",
|
||||
"sass": "^1.97.3",
|
||||
"superjson": "2.2.6",
|
||||
"swagger-ui-react": "^5.31.0",
|
||||
"use-deep-compare-effect": "^1.8.1",
|
||||
@@ -94,15 +94,15 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/chroma-js": "3.1.2",
|
||||
"@types/node": "^24.10.4",
|
||||
"@types/node": "^24.10.12",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "19.2.7",
|
||||
"@types/react": "19.2.13",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/swagger-ui-react": "^5.18.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^9.39.2",
|
||||
"node-loader": "^2.1.0",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { startTransition } from "react";
|
||||
import { Button, Card, Group, Stack, Switch, Text } from "@mantine/core";
|
||||
import { IconArrowRight } from "@tabler/icons-react";
|
||||
import type { z } from "zod/v4";
|
||||
@@ -24,16 +23,6 @@ export const InitSettings = () => {
|
||||
});
|
||||
const form = useZodForm(settingsInitSchema, { initialValues: defaultServerSettings });
|
||||
|
||||
form.watch("analytics.enableGeneral", ({ value }) => {
|
||||
if (!value) {
|
||||
startTransition(() => {
|
||||
form.setFieldValue("analytics.enableWidgetData", false);
|
||||
form.setFieldValue("analytics.enableIntegrationData", false);
|
||||
form.setFieldValue("analytics.enableUserData", false);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handleSubmitAsync = async (values: z.infer<typeof settingsInitSchema>) => {
|
||||
await mutateAsync(values);
|
||||
};
|
||||
@@ -47,24 +36,6 @@ export const InitSettings = () => {
|
||||
|
||||
<Stack gap="xs">
|
||||
<AnalyticsRow kind="general" {...form.getInputProps("analytics.enableGeneral", { type: "checkbox" })} />
|
||||
|
||||
<Stack gap="xs" ps="md" w="100%">
|
||||
<AnalyticsRow
|
||||
kind="integrationData"
|
||||
disabled={!form.values.analytics.enableGeneral}
|
||||
{...form.getInputProps("analytics.enableWidgetData", { type: "checkbox" })}
|
||||
/>
|
||||
<AnalyticsRow
|
||||
kind="widgetData"
|
||||
disabled={!form.values.analytics.enableGeneral}
|
||||
{...form.getInputProps("analytics.enableIntegrationData", { type: "checkbox" })}
|
||||
/>
|
||||
<AnalyticsRow
|
||||
kind="usersData"
|
||||
disabled={!form.values.analytics.enableGeneral}
|
||||
{...form.getInputProps("analytics.enableUserData", { type: "checkbox" })}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
@@ -24,15 +24,6 @@ export const AnalyticsSettings = ({ initialData }: AnalyticsSettingsProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!updatedValues.enableGeneral &&
|
||||
(updatedValues.enableWidgetData || updatedValues.enableIntegrationData || updatedValues.enableUserData)
|
||||
) {
|
||||
updatedValues.enableIntegrationData = false;
|
||||
updatedValues.enableUserData = false;
|
||||
updatedValues.enableWidgetData = false;
|
||||
}
|
||||
|
||||
void mutateAsync({
|
||||
settingsKey: "analytics",
|
||||
value: updatedValues,
|
||||
@@ -54,30 +45,6 @@ export const AnalyticsSettings = ({ initialData }: AnalyticsSettingsProps) => {
|
||||
<LoadingOverlay visible={isPending} zIndex={1000} overlayProps={{ radius: "sm", blur: 2 }} />
|
||||
<Stack>
|
||||
<SwitchSetting form={form} formKey="enableGeneral" title={t("general.title")} text={t("general.text")} />
|
||||
<SwitchSetting
|
||||
form={form}
|
||||
formKey="enableIntegrationData"
|
||||
ms="xl"
|
||||
title={t("integrationData.title")}
|
||||
text={t("integrationData.text")}
|
||||
disabled={!form.values.enableGeneral}
|
||||
/>
|
||||
<SwitchSetting
|
||||
form={form}
|
||||
formKey="enableWidgetData"
|
||||
ms="xl"
|
||||
title={t("widgetData.title")}
|
||||
text={t("widgetData.text")}
|
||||
disabled={!form.values.enableGeneral}
|
||||
/>
|
||||
<SwitchSetting
|
||||
form={form}
|
||||
formKey="enableUserData"
|
||||
ms="xl"
|
||||
title={t("usersData.title")}
|
||||
text={t("usersData.text")}
|
||||
disabled={!form.values.enableGeneral}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
|
||||
@@ -105,7 +105,7 @@ export const WidgetPreviewPageContent = ({ kind, integrationData }: WidgetPrevie
|
||||
<ErrorBoundary
|
||||
onReset={reset}
|
||||
fallbackRender={({ resetErrorBoundary, error }) => (
|
||||
<WidgetError kind={kind} error={error as unknown} resetErrorBoundary={resetErrorBoundary} />
|
||||
<WidgetError kind={kind} error={error} resetErrorBoundary={resetErrorBoundary} />
|
||||
)}
|
||||
>
|
||||
<Comp
|
||||
|
||||
@@ -108,7 +108,7 @@ const InnerContent = ({ item, ...dimensions }: InnerContentProps) => {
|
||||
fallbackRender={({ resetErrorBoundary, error }) => (
|
||||
<>
|
||||
<BoardItemMenu offset={4} item={newItem} resetErrorBoundary={resetErrorBoundary} />
|
||||
<WidgetError kind={item.kind} error={error as unknown} resetErrorBoundary={resetErrorBoundary} />
|
||||
<WidgetError kind={item.kind} error={error} resetErrorBoundary={resetErrorBoundary} />
|
||||
</>
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -58,6 +58,7 @@ export const useCategoryActions = () => {
|
||||
...section,
|
||||
yOffset: section.yOffset + 2,
|
||||
})),
|
||||
...previous.sections.filter((section) => section.kind !== "category" && section.kind !== "empty"),
|
||||
] satisfies Section[],
|
||||
}));
|
||||
},
|
||||
|
||||
@@ -38,20 +38,20 @@
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@homarr/widgets": "workspace:^0.1.0",
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.2.3",
|
||||
"fastify": "^5.6.2",
|
||||
"dotenv": "^17.2.4",
|
||||
"fastify": "^5.7.4",
|
||||
"superjson": "2.2.6",
|
||||
"undici": "7.16.0"
|
||||
"undici": "7.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node": "^24.10.4",
|
||||
"@types/node": "^24.10.12",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "^0.27.2",
|
||||
"esbuild": "^0.27.3",
|
||||
"eslint": "^9.39.2",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -25,18 +25,18 @@
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"dotenv": "^17.2.4",
|
||||
"tsx": "4.20.4",
|
||||
"ws": "^8.18.3"
|
||||
"ws": "^8.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
"esbuild": "^0.27.2",
|
||||
"esbuild": "^0.27.3",
|
||||
"eslint": "^9.39.2",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:24.12.0-trixie AS base
|
||||
FROM node:24.13.0-trixie AS base
|
||||
WORKDIR /app
|
||||
COPY package.json .
|
||||
COPY pnpm-lock.yaml .
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"name": "homarr-prebuilt-debian",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^12.5.0"
|
||||
"better-sqlite3": "^12.6.2"
|
||||
},
|
||||
"packageManager": "pnpm@10.27.0",
|
||||
"packageManager": "pnpm@10.29.1",
|
||||
"engines": {
|
||||
"node": ">=24.12.0",
|
||||
"pnpm": ">=10.27.0"
|
||||
"node": ">=24.13.0",
|
||||
"pnpm": ">=10.29.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
|
||||
+5
-5
@@ -9,16 +9,16 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
better-sqlite3:
|
||||
specifier: ^12.5.0
|
||||
version: 12.5.0
|
||||
specifier: ^12.6.2
|
||||
version: 12.6.2
|
||||
|
||||
packages:
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
better-sqlite3@12.5.0:
|
||||
resolution: {integrity: sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==}
|
||||
better-sqlite3@12.6.2:
|
||||
resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==}
|
||||
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
|
||||
|
||||
bindings@1.5.0:
|
||||
@@ -147,7 +147,7 @@ snapshots:
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
better-sqlite3@12.5.0:
|
||||
better-sqlite3@12.6.2:
|
||||
dependencies:
|
||||
bindings: 1.5.0
|
||||
prebuild-install: 7.1.3
|
||||
|
||||
+27
-20
@@ -78,6 +78,13 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/coolify" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/coolify.svg" alt="Coolify" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Coolify</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/dash-dot" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/dashdot.png" alt="Dash." width="90" height="90" />
|
||||
<br/>
|
||||
@@ -97,15 +104,15 @@
|
||||
<br/>
|
||||
<p align="center">Docker<br/>Hub</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/emby" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/emby.svg" alt="Emby" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Emby</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/github" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/github.svg" alt="Github" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -146,15 +153,15 @@
|
||||
<br/>
|
||||
<p align="center">Jellyfin</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/jellyseerr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyseerr.svg" alt="Jellyseerr" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Jellyseerr</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/lidarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/lidarr.svg" alt="Lidarr" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -195,15 +202,15 @@
|
||||
<br/>
|
||||
<p align="center">NZBGet</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/open-media-vault" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/openmediavault.svg" alt="OpenMediaVault" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">OpenMediaVault</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/opnsense" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/opnsense.svg" alt="OPNsense" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -244,15 +251,15 @@
|
||||
<br/>
|
||||
<p align="center">Proxmox</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/q-bittorent" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/qbittorrent.svg" alt="qBittorrent" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">qBittorrent</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/quay" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/quay.png" alt="Quay" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -293,15 +300,15 @@
|
||||
<br/>
|
||||
<p align="center">Tdarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/transmission" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/transmission.svg" alt="Transmission" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Transmission</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<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" />
|
||||
<br/>
|
||||
|
||||
+25
-25
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homarr",
|
||||
"version": "1.50.0",
|
||||
"version": "1.53.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "cross-env CI=true turbo build",
|
||||
@@ -39,30 +39,30 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@semantic-release/github": "^12.0.2",
|
||||
"@semantic-release/npm": "^13.1.3",
|
||||
"@semantic-release/github": "^12.0.5",
|
||||
"@semantic-release/npm": "^13.1.4",
|
||||
"@semantic-release/release-notes-generator": "^14.1.0",
|
||||
"@testcontainers/redis": "^11.11.0",
|
||||
"@turbo/gen": "^2.7.2",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"@vitest/coverage-v8": "^4.0.16",
|
||||
"@vitest/ui": "^4.0.16",
|
||||
"@turbo/gen": "^2.8.3",
|
||||
"@vitejs/plugin-react": "^5.1.3",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"@vitest/ui": "^4.0.18",
|
||||
"conventional-changelog-conventionalcommits": "^9.1.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"jsdom": "^27.4.0",
|
||||
"jsdom": "^28.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"prettier": "^3.7.4",
|
||||
"semantic-release": "^25.0.2",
|
||||
"prettier": "^3.8.1",
|
||||
"semantic-release": "^25.0.3",
|
||||
"testcontainers": "^11.11.0",
|
||||
"turbo": "^2.7.2",
|
||||
"turbo": "^2.8.3",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-tsconfig-paths": "^6.0.3",
|
||||
"vitest": "^4.0.16"
|
||||
"vite-tsconfig-paths": "^6.1.0",
|
||||
"vitest": "^4.0.18"
|
||||
},
|
||||
"packageManager": "pnpm@10.27.0",
|
||||
"packageManager": "pnpm@10.29.1",
|
||||
"engines": {
|
||||
"node": ">=24.12.0",
|
||||
"pnpm": ">=10.27.0"
|
||||
"node": ">=24.13.0",
|
||||
"pnpm": ">=10.29.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
@@ -77,25 +77,25 @@
|
||||
"tree-sitter-json"
|
||||
],
|
||||
"overrides": {
|
||||
"@babel/helpers@<7.26.10": ">=7.28.4",
|
||||
"@babel/runtime@<7.26.10": ">=7.28.4",
|
||||
"axios@>=1.0.0 <1.8.2": ">=1.13.2",
|
||||
"@babel/helpers@<7.26.10": ">=7.28.6",
|
||||
"@babel/runtime@<7.26.10": ">=7.28.6",
|
||||
"axios@>=1.0.0 <1.8.2": ">=1.13.5",
|
||||
"brace-expansion@>=2.0.0 <=2.0.1": ">=4.0.1",
|
||||
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
|
||||
"esbuild@<=0.24.2": ">=0.27.2",
|
||||
"esbuild@<=0.24.2": ">=0.27.3",
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
||||
"hono@<4.6.5": ">=4.11.3",
|
||||
"hono@<4.6.5": ">=4.11.9",
|
||||
"linkifyjs@<4.3.2": ">=4.3.2",
|
||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
|
||||
"prismjs@<1.30.0": ">=1.30.0",
|
||||
"proxmox-api>undici": "7.16.0",
|
||||
"react-is": "^19.2.3",
|
||||
"rollup@>=4.0.0 <4.22.4": ">=4.54.0",
|
||||
"proxmox-api>undici": "7.19.2",
|
||||
"react-is": "^19.2.4",
|
||||
"rollup@>=4.0.0 <4.22.4": ">=4.57.1",
|
||||
"sha.js@<=2.4.11": ">=2.4.12",
|
||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.1",
|
||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.1",
|
||||
"tmp@<=0.2.3": ">=0.2.5",
|
||||
"vite@>=5.0.0 <=5.4.18": ">=7.3.0"
|
||||
"vite@>=5.0.0 <=5.4.18": ">=7.3.1"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@types/node-unifi": "patches/@types__node-unifi.patch",
|
||||
|
||||
@@ -22,8 +22,11 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/auth": "workspace:^0.1.0",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/docker": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@umami/node": "^0.4.0",
|
||||
"superjson": "2.2.6"
|
||||
|
||||
@@ -1,17 +1,52 @@
|
||||
import type { UmamiEventData } from "@umami/node";
|
||||
import { Umami } from "@umami/node";
|
||||
|
||||
import { isProviderEnabled } from "@homarr/auth/server";
|
||||
import { Stopwatch } from "@homarr/common";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import { count, db } from "@homarr/db";
|
||||
import { isMysql, isPostgresql } from "@homarr/db/collection";
|
||||
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
|
||||
import { integrations, items, users } from "@homarr/db/schema";
|
||||
import type { defaultServerSettings } from "@homarr/server-settings";
|
||||
import {
|
||||
apiKeys,
|
||||
apps,
|
||||
boards,
|
||||
cronJobConfigurations,
|
||||
groups,
|
||||
iconRepositories,
|
||||
icons,
|
||||
integrations,
|
||||
invites,
|
||||
itemLayouts,
|
||||
items,
|
||||
layouts,
|
||||
medias,
|
||||
searchEngines,
|
||||
sectionLayouts,
|
||||
sections,
|
||||
users,
|
||||
} from "@homarr/db/schema";
|
||||
import { env as dockerEnv } from "@homarr/docker/env";
|
||||
|
||||
import { Stopwatch } from "../../common/src";
|
||||
import packageJson from "../../../package.json";
|
||||
import { UMAMI_HOST_URL, UMAMI_WEBSITE_ID } from "./constants";
|
||||
|
||||
const logger = createLogger({ module: "analytics" });
|
||||
|
||||
const getDatabaseType = (): "mysql" | "postgresql" | "sqlite" => {
|
||||
if (isMysql()) return "mysql";
|
||||
if (isPostgresql()) return "postgresql";
|
||||
return "sqlite";
|
||||
};
|
||||
|
||||
const getEnabledAuthProviders = (): string[] => {
|
||||
const providers: string[] = [];
|
||||
if (isProviderEnabled("credentials")) providers.push("credentials");
|
||||
if (isProviderEnabled("oidc")) providers.push("oidc");
|
||||
if (isProviderEnabled("ldap")) providers.push("ldap");
|
||||
return providers;
|
||||
};
|
||||
|
||||
export const sendServerAnalyticsAsync = async () => {
|
||||
const stopWatch = new Stopwatch();
|
||||
const analyticsSettings = await getServerSettingByKeyAsync(db, "analytics");
|
||||
@@ -27,67 +62,48 @@ export const sendServerAnalyticsAsync = async () => {
|
||||
websiteId: UMAMI_WEBSITE_ID,
|
||||
});
|
||||
|
||||
await sendIntegrationDataAsync(umamiInstance, analyticsSettings);
|
||||
await sendWidgetDataAsync(umamiInstance, analyticsSettings);
|
||||
await sendUserDataAsync(umamiInstance, analyticsSettings);
|
||||
const enabledAuthProviders = getEnabledAuthProviders();
|
||||
|
||||
logger.info(`Sent all analytics in ${stopWatch.getElapsedInHumanWords()}`);
|
||||
};
|
||||
const analyticsData: UmamiEventData = {
|
||||
homarrVersion: packageJson.version,
|
||||
databaseType: getDatabaseType(),
|
||||
dockerEnabled: dockerEnv.ENABLE_DOCKER ? 1 : 0,
|
||||
kubernetesEnabled: dockerEnv.ENABLE_KUBERNETES ? 1 : 0,
|
||||
authCredentials: enabledAuthProviders.includes("credentials") ? 1 : 0,
|
||||
authOidc: enabledAuthProviders.includes("oidc") ? 1 : 0,
|
||||
authLdap: enabledAuthProviders.includes("ldap") ? 1 : 0,
|
||||
countUsers: await db.$count(users),
|
||||
countBoards: await db.$count(boards),
|
||||
countGroups: await db.$count(groups),
|
||||
countApps: await db.$count(apps),
|
||||
countWidgets: await db.$count(items),
|
||||
countSections: await db.$count(sections),
|
||||
countIntegrations: await db.$count(integrations),
|
||||
countSearchEngines: await db.$count(searchEngines),
|
||||
countIconRepositories: await db.$count(iconRepositories),
|
||||
countIcons: await db.$count(icons),
|
||||
countApiKeys: await db.$count(apiKeys),
|
||||
countInvites: await db.$count(invites),
|
||||
countMedias: await db.$count(medias),
|
||||
countLayouts: await db.$count(layouts),
|
||||
countItemLayouts: await db.$count(itemLayouts),
|
||||
countSectionLayouts: await db.$count(sectionLayouts),
|
||||
countCronJobConfigs: await db.$count(cronJobConfigurations),
|
||||
};
|
||||
|
||||
const sendWidgetDataAsync = async (umamiInstance: Umami, analyticsSettings: typeof defaultServerSettings.analytics) => {
|
||||
if (!analyticsSettings.enableWidgetData) {
|
||||
return;
|
||||
}
|
||||
const widgetCount = await db.$count(items);
|
||||
|
||||
const response = await umamiInstance.track("server-widget-data", {
|
||||
countWidgets: widgetCount,
|
||||
});
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn("Unable to send track event data to Umami instance");
|
||||
};
|
||||
|
||||
const sendUserDataAsync = async (umamiInstance: Umami, analyticsSettings: typeof defaultServerSettings.analytics) => {
|
||||
if (!analyticsSettings.enableUserData) {
|
||||
return;
|
||||
}
|
||||
const userCount = await db.$count(users);
|
||||
|
||||
const response = await umamiInstance.track("server-user-data", {
|
||||
countUsers: userCount,
|
||||
});
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn("Unable to send track event data to Umami instance");
|
||||
};
|
||||
|
||||
const sendIntegrationDataAsync = async (
|
||||
umamiInstance: Umami,
|
||||
analyticsSettings: typeof defaultServerSettings.analytics,
|
||||
) => {
|
||||
if (!analyticsSettings.enableIntegrationData) {
|
||||
return;
|
||||
}
|
||||
const integrationKinds = await db
|
||||
.select({ kind: integrations.kind, count: count(integrations.id) })
|
||||
.from(integrations)
|
||||
.groupBy(integrations.kind);
|
||||
|
||||
const map: UmamiEventData = {};
|
||||
|
||||
integrationKinds.forEach((integrationKind) => {
|
||||
map[integrationKind.kind] = integrationKind.count;
|
||||
analyticsData[`integration_${integrationKind.kind}`] = integrationKind.count;
|
||||
});
|
||||
|
||||
const response = await umamiInstance.track("server-integration-data-kind", map);
|
||||
if (response.ok) {
|
||||
return;
|
||||
const response = await umamiInstance.track("server-analytics-v2", analyticsData);
|
||||
if (!response.ok) {
|
||||
logger.warn("Unable to send analytics to Umami instance");
|
||||
}
|
||||
|
||||
logger.warn("Unable to send track event data to Umami instance");
|
||||
logger.info(`Sent analytics in ${stopWatch.getElapsedInHumanWords()}`);
|
||||
};
|
||||
@@ -41,15 +41,15 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@kubernetes/client-node": "^1.4.0",
|
||||
"@tanstack/react-query": "^5.90.16",
|
||||
"@trpc/client": "^11.8.1",
|
||||
"@trpc/react-query": "^11.8.1",
|
||||
"@trpc/server": "^11.8.1",
|
||||
"@trpc/tanstack-react-query": "^11.8.1",
|
||||
"@tanstack/react-query": "^5.90.20",
|
||||
"@trpc/client": "^11.9.0",
|
||||
"@trpc/react-query": "^11.9.0",
|
||||
"@trpc/server": "^11.9.0",
|
||||
"@trpc/tanstack-react-query": "^11.9.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"next": "16.1.1",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"superjson": "2.2.6",
|
||||
"trpc-to-openapi": "^3.1.0",
|
||||
"zod": "^4.2.1"
|
||||
@@ -59,7 +59,7 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.39.2",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
+26
-48
@@ -1,54 +1,32 @@
|
||||
import { apiKeysRouter } from "./router/apiKeys";
|
||||
import { appRouter as innerAppRouter } from "./router/app";
|
||||
import { boardRouter } from "./router/board";
|
||||
import { certificateRouter } from "./router/certificates/certificate-router";
|
||||
import { cronJobsRouter } from "./router/cron-jobs";
|
||||
import { dockerRouter } from "./router/docker/docker-router";
|
||||
import { groupRouter } from "./router/group";
|
||||
import { homeRouter } from "./router/home";
|
||||
import { iconsRouter } from "./router/icons";
|
||||
import { importRouter } from "./router/import/import-router";
|
||||
import { infoRouter } from "./router/info";
|
||||
import { integrationRouter } from "./router/integration/integration-router";
|
||||
import { inviteRouter } from "./router/invite";
|
||||
import { kubernetesRouter } from "./router/kubernetes/router/kubernetes-router";
|
||||
import { locationRouter } from "./router/location";
|
||||
import { logRouter } from "./router/log";
|
||||
import { mediaRouter } from "./router/medias/media-router";
|
||||
import { onboardRouter } from "./router/onboard/onboard-router";
|
||||
import { searchEngineRouter } from "./router/search-engine/search-engine-router";
|
||||
import { sectionRouter } from "./router/section/section-router";
|
||||
import { serverSettingsRouter } from "./router/serverSettings";
|
||||
import { updateCheckerRouter } from "./router/update-checker";
|
||||
import { userRouter } from "./router/user";
|
||||
import { widgetRouter } from "./router/widgets";
|
||||
import { lazy } from "@trpc/server";
|
||||
|
||||
import { createTRPCRouter } from "./trpc";
|
||||
|
||||
export const appRouter = createTRPCRouter({
|
||||
user: userRouter,
|
||||
group: groupRouter,
|
||||
invite: inviteRouter,
|
||||
integration: integrationRouter,
|
||||
board: boardRouter,
|
||||
section: sectionRouter,
|
||||
app: innerAppRouter,
|
||||
searchEngine: searchEngineRouter,
|
||||
widget: widgetRouter,
|
||||
location: locationRouter,
|
||||
log: logRouter,
|
||||
icon: iconsRouter,
|
||||
import: importRouter,
|
||||
onboard: onboardRouter,
|
||||
home: homeRouter,
|
||||
docker: dockerRouter,
|
||||
kubernetes: kubernetesRouter,
|
||||
serverSettings: serverSettingsRouter,
|
||||
cronJobs: cronJobsRouter,
|
||||
apiKeys: apiKeysRouter,
|
||||
media: mediaRouter,
|
||||
updateChecker: updateCheckerRouter,
|
||||
certificates: certificateRouter,
|
||||
info: infoRouter,
|
||||
user: lazy(() => import("./router/user").then((mod) => mod.userRouter)),
|
||||
group: lazy(() => import("./router/group").then((mod) => mod.groupRouter)),
|
||||
invite: lazy(() => import("./router/invite").then((mod) => mod.inviteRouter)),
|
||||
integration: lazy(() => import("./router/integration/integration-router").then((mod) => mod.integrationRouter)),
|
||||
board: lazy(() => import("./router/board").then((mod) => mod.boardRouter)),
|
||||
section: lazy(() => import("./router/section/section-router").then((mod) => mod.sectionRouter)),
|
||||
app: lazy(() => import("./router/app").then((mod) => mod.appRouter)),
|
||||
searchEngine: lazy(() => import("./router/search-engine/search-engine-router").then((mod) => mod.searchEngineRouter)),
|
||||
widget: lazy(() => import("./router/widgets").then((mod) => mod.widgetRouter)),
|
||||
location: lazy(() => import("./router/location").then((mod) => mod.locationRouter)),
|
||||
log: lazy(() => import("./router/log").then((mod) => mod.logRouter)),
|
||||
icon: lazy(() => import("./router/icons").then((mod) => mod.iconsRouter)),
|
||||
import: lazy(() => import("./router/import/import-router").then((mod) => mod.importRouter)),
|
||||
onboard: lazy(() => import("./router/onboard/onboard-router").then((mod) => mod.onboardRouter)),
|
||||
home: lazy(() => import("./router/home").then((mod) => mod.homeRouter)),
|
||||
docker: lazy(() => import("./router/docker/docker-router").then((mod) => mod.dockerRouter)),
|
||||
kubernetes: lazy(() => import("./router/kubernetes/router/kubernetes-router").then((mod) => mod.kubernetesRouter)),
|
||||
serverSettings: lazy(() => import("./router/serverSettings").then((mod) => mod.serverSettingsRouter)),
|
||||
cronJobs: lazy(() => import("./router/cron-jobs").then((mod) => mod.cronJobsRouter)),
|
||||
apiKeys: lazy(() => import("./router/apiKeys").then((mod) => mod.apiKeysRouter)),
|
||||
media: lazy(() => import("./router/medias/media-router").then((mod) => mod.mediaRouter)),
|
||||
updateChecker: lazy(() => import("./router/update-checker").then((mod) => mod.updateCheckerRouter)),
|
||||
certificates: lazy(() => import("./router/certificates/certificate-router").then((mod) => mod.certificateRouter)),
|
||||
info: lazy(() => import("./router/info").then((mod) => mod.infoRouter)),
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import { createId } from "@homarr/common";
|
||||
import type { Database, InferSelectModel } from "@homarr/db";
|
||||
import { asc, eq, inArray, like } from "@homarr/db";
|
||||
import { apps } from "@homarr/db/schema";
|
||||
import { selectAppSchema } from "@homarr/db/validationSchemas";
|
||||
@@ -11,7 +13,7 @@ import { byIdSchema, paginatedSchema } from "@homarr/validation/common";
|
||||
|
||||
import { convertIntersectionToZodObject } from "../schema-merger";
|
||||
import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publicProcedure } from "../trpc";
|
||||
import { canUserSeeAppAsync } from "./app/app-access-control";
|
||||
import { AppAccessControl } from "./app/app-access-control";
|
||||
|
||||
const defaultIcon = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/homarr.svg";
|
||||
|
||||
@@ -89,9 +91,8 @@ export const appRouter = createTRPCRouter({
|
||||
.output(selectAppSchema)
|
||||
.meta({ openapi: { method: "GET", path: "/api/apps/{id}", tags: ["apps"], protect: true } })
|
||||
.query(async ({ ctx, input }) => {
|
||||
const app = await ctx.db.query.apps.findFirst({
|
||||
where: eq(apps.id, input.id),
|
||||
});
|
||||
const repository = new AppRepository(ctx.db, ctx.session?.user ?? null);
|
||||
const app = await repository.getByIdAsync(input.id);
|
||||
|
||||
if (!app) {
|
||||
throw new TRPCError({
|
||||
@@ -100,20 +101,11 @@ export const appRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
|
||||
const canUserSeeApp = await canUserSeeAppAsync(ctx.session?.user ?? null, app.id);
|
||||
if (!canUserSeeApp) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "App not found",
|
||||
});
|
||||
}
|
||||
|
||||
return app;
|
||||
}),
|
||||
byIds: publicProcedure.input(z.array(z.string())).query(async ({ ctx, input }) => {
|
||||
return await ctx.db.query.apps.findMany({
|
||||
where: inArray(apps.id, input),
|
||||
});
|
||||
const repository = new AppRepository(ctx.db, ctx.session?.user ?? null);
|
||||
return await repository.getByIdsAsync(input);
|
||||
}),
|
||||
create: permissionRequiredProcedure
|
||||
.requiresPermission("app-create")
|
||||
@@ -187,3 +179,30 @@ export const appRouter = createTRPCRouter({
|
||||
await ctx.db.delete(apps).where(eq(apps.id, input.id));
|
||||
}),
|
||||
});
|
||||
|
||||
type App = InferSelectModel<typeof apps>;
|
||||
|
||||
export class AppRepository {
|
||||
private readonly accessControl: AppAccessControl;
|
||||
|
||||
constructor(
|
||||
private db: Database,
|
||||
user: Session["user"] | null,
|
||||
) {
|
||||
this.accessControl = new AppAccessControl(db, user);
|
||||
}
|
||||
|
||||
public async getByIdAsync(id: string): Promise<App | null> {
|
||||
const apps = await this.getByIdsAsync([id]);
|
||||
return apps[0] ?? null;
|
||||
}
|
||||
|
||||
public async getByIdsAsync(ids: string[]): Promise<App[]> {
|
||||
const canUserSeeApps = await this.accessControl.canUserSeeAppsAsync(ids);
|
||||
const dbApps = await this.db.query.apps.findMany({
|
||||
where: inArray(apps.id, ids),
|
||||
});
|
||||
|
||||
return canUserSeeApps ? dbApps : [];
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,52 @@
|
||||
import SuperJSON from "superjson";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import { db, eq, or } from "@homarr/db";
|
||||
import type { Database } from "@homarr/db";
|
||||
import { eq, or } from "@homarr/db";
|
||||
import { items } from "@homarr/db/schema";
|
||||
|
||||
import type { WidgetComponentProps } from "../../../../widgets/src";
|
||||
|
||||
export const canUserSeeAppAsync = async (user: Session["user"] | null, appId: string) => {
|
||||
return await canUserSeeAppsAsync(user, [appId]);
|
||||
};
|
||||
export class AppAccessControl {
|
||||
constructor(
|
||||
private db: Database,
|
||||
private user: Session["user"] | null,
|
||||
) {}
|
||||
|
||||
export const canUserSeeAppsAsync = async (user: Session["user"] | null, appIds: string[]) => {
|
||||
if (user) return true;
|
||||
async canUserSeeAppAsync(appId: string) {
|
||||
return await this.canUserSeeAppsAsync([appId]);
|
||||
}
|
||||
|
||||
const appIdsOnPublicBoards = await getAllAppIdsOnPublicBoardsAsync();
|
||||
return appIds.every((appId) => appIdsOnPublicBoards.includes(appId));
|
||||
};
|
||||
async canUserSeeAppsAsync(appIds: string[]) {
|
||||
// Currently any user can see all apps if they are logged in
|
||||
if (this.user) return true;
|
||||
|
||||
const getAllAppIdsOnPublicBoardsAsync = async () => {
|
||||
const itemsWithApps = await db.query.items.findMany({
|
||||
where: or(eq(items.kind, "app"), eq(items.kind, "bookmarks")),
|
||||
with: {
|
||||
board: {
|
||||
columns: {
|
||||
isPublic: true,
|
||||
const appIdsOnPublicBoards = await this.getAllAppIdsOnPublicBoardsAsync();
|
||||
return appIds.every((appId) => appIdsOnPublicBoards.includes(appId));
|
||||
}
|
||||
|
||||
private async getAllAppIdsOnPublicBoardsAsync() {
|
||||
const itemsWithApps = await this.db.query.items.findMany({
|
||||
where: or(eq(items.kind, "app"), eq(items.kind, "bookmarks")),
|
||||
with: {
|
||||
board: {
|
||||
columns: {
|
||||
isPublic: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return itemsWithApps
|
||||
.filter((item) => item.board.isPublic)
|
||||
.flatMap((item) => {
|
||||
if (item.kind === "app") {
|
||||
const parsedOptions = SuperJSON.parse<WidgetComponentProps<"app">["options"]>(item.options);
|
||||
return [parsedOptions.appId];
|
||||
}
|
||||
|
||||
return itemsWithApps
|
||||
.filter((item) => item.board.isPublic)
|
||||
.flatMap((item) => {
|
||||
if (item.kind === "app") {
|
||||
const parsedOptions = SuperJSON.parse<WidgetComponentProps<"app">["options"]>(item.options);
|
||||
return [parsedOptions.appId];
|
||||
} else if (item.kind === "bookmarks") {
|
||||
const parsedOptions = SuperJSON.parse<WidgetComponentProps<"bookmarks">["options"]>(item.options);
|
||||
return parsedOptions.items;
|
||||
}
|
||||
|
||||
throw new Error("Failed to get app ids from board. Invalid item kind: 'test'");
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,15 @@
|
||||
import { lazy } from "@trpc/server";
|
||||
|
||||
import { createTRPCRouter } from "../../../trpc";
|
||||
import { clusterRouter } from "./cluster";
|
||||
import { configMapsRouter } from "./configMaps";
|
||||
import { ingressesRouter } from "./ingresses";
|
||||
import { namespacesRouter } from "./namespaces";
|
||||
import { nodesRouter } from "./nodes";
|
||||
import { podsRouter } from "./pods";
|
||||
import { secretsRouter } from "./secrets";
|
||||
import { servicesRouter } from "./services";
|
||||
import { volumesRouter } from "./volumes";
|
||||
|
||||
export const kubernetesRouter = createTRPCRouter({
|
||||
nodes: nodesRouter,
|
||||
cluster: clusterRouter,
|
||||
namespaces: namespacesRouter,
|
||||
ingresses: ingressesRouter,
|
||||
services: servicesRouter,
|
||||
pods: podsRouter,
|
||||
secrets: secretsRouter,
|
||||
configMaps: configMapsRouter,
|
||||
volumes: volumesRouter,
|
||||
nodes: lazy(() => import("./nodes").then((mod) => mod.nodesRouter)),
|
||||
cluster: lazy(() => import("./cluster").then((mod) => mod.clusterRouter)),
|
||||
namespaces: lazy(() => import("./namespaces").then((mod) => mod.namespacesRouter)),
|
||||
ingresses: lazy(() => import("./ingresses").then((mod) => mod.ingressesRouter)),
|
||||
services: lazy(() => import("./services").then((mod) => mod.servicesRouter)),
|
||||
pods: lazy(() => import("./pods").then((mod) => mod.podsRouter)),
|
||||
secrets: lazy(() => import("./secrets").then((mod) => mod.secretsRouter)),
|
||||
configMaps: lazy(() => import("./configMaps").then((mod) => mod.configMapsRouter)),
|
||||
volumes: lazy(() => import("./volumes").then((mod) => mod.volumesRouter)),
|
||||
});
|
||||
@@ -13,6 +13,14 @@ import * as appAccessControl from "../app/app-access-control";
|
||||
// Mock the auth module to return an empty session
|
||||
vi.mock("@homarr/auth", () => ({ auth: () => ({}) as Session }));
|
||||
|
||||
const createFakeAccessControl = (canAccess: boolean) =>
|
||||
vi.fn(
|
||||
class {
|
||||
canUserSeeAppAsync = async (_: string) => await Promise.resolve(canAccess);
|
||||
canUserSeeAppsAsync = async (_: string[]) => await Promise.resolve(canAccess);
|
||||
} as unknown as typeof appAccessControl.AppAccessControl,
|
||||
);
|
||||
|
||||
const createDefaultSession = (permissions: GroupPermissionKey[] = []): Session => ({
|
||||
user: { id: createId(), permissions, colorScheme: "light" },
|
||||
expires: new Date().toISOString(),
|
||||
@@ -77,7 +85,7 @@ describe("byId should return an app by id", () => {
|
||||
deviceType: undefined,
|
||||
session: null,
|
||||
});
|
||||
vi.spyOn(appAccessControl, "canUserSeeAppAsync").mockReturnValue(Promise.resolve(true));
|
||||
vi.spyOn(appAccessControl, "AppAccessControl").mockImplementation(createFakeAccessControl(true));
|
||||
|
||||
await db.insert(apps).values([
|
||||
{
|
||||
@@ -118,7 +126,7 @@ describe("byId should return an app by id", () => {
|
||||
href: "https://mantine.dev",
|
||||
},
|
||||
]);
|
||||
vi.spyOn(appAccessControl, "canUserSeeAppAsync").mockReturnValue(Promise.resolve(false));
|
||||
vi.spyOn(appAccessControl, "AppAccessControl").mockImplementation(createFakeAccessControl(false));
|
||||
|
||||
// Act
|
||||
const actAsync = async () => await caller.byId({ id: "2" });
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import { createId } from "@homarr/common";
|
||||
import type { InferInsertModel } from "@homarr/db";
|
||||
import { apps } from "@homarr/db/schema";
|
||||
import { createDb } from "@homarr/db/test";
|
||||
import * as ping from "@homarr/ping";
|
||||
|
||||
@@ -9,22 +12,31 @@ import { appRouter } from "../../widgets/app";
|
||||
// Mock the auth module to return an empty session
|
||||
vi.mock("@homarr/auth", () => ({ auth: () => ({}) as Session }));
|
||||
vi.mock("@homarr/ping", () => ({ sendPingRequestAsync: async () => await Promise.resolve(null) }));
|
||||
vi.mock("../../app/app-access-control", () => ({
|
||||
AppAccessControl: vi.fn(
|
||||
class {
|
||||
canUserSeeAppAsync = async (_: string) => await Promise.resolve(true);
|
||||
canUserSeeAppsAsync = async (_: string[]) => await Promise.resolve(true);
|
||||
},
|
||||
),
|
||||
}));
|
||||
|
||||
describe("ping should call sendPingRequestAsync with url and return result", () => {
|
||||
test("ping with error response should return error and url", async () => {
|
||||
// Arrange
|
||||
const spy = vi.spyOn(ping, "sendPingRequestAsync");
|
||||
vi.spyOn(ping, "sendPingRequestAsync").mockImplementation(() => Promise.resolve({ error: "error" }));
|
||||
const url = "http://localhost";
|
||||
const db = createDb();
|
||||
const app = createApp({ url });
|
||||
await db.insert(apps).values(app);
|
||||
const caller = appRouter.createCaller({
|
||||
db,
|
||||
deviceType: undefined,
|
||||
session: null,
|
||||
});
|
||||
spy.mockImplementation(() => Promise.resolve({ error: "error" }));
|
||||
|
||||
// Act
|
||||
const result = await caller.ping({ url });
|
||||
const result = await caller.ping({ id: app.id });
|
||||
|
||||
// Assert
|
||||
expect(result.url).toBe(url);
|
||||
@@ -33,21 +45,32 @@ describe("ping should call sendPingRequestAsync with url and return result", ()
|
||||
|
||||
test("ping with success response should return statusCode and url", async () => {
|
||||
// Arrange
|
||||
const spy = vi.spyOn(ping, "sendPingRequestAsync");
|
||||
vi.spyOn(ping, "sendPingRequestAsync").mockImplementation(() =>
|
||||
Promise.resolve({ statusCode: 200, durationMs: 123 }),
|
||||
);
|
||||
const url = "http://localhost";
|
||||
const db = createDb();
|
||||
const app = createApp({ url });
|
||||
await db.insert(apps).values(app);
|
||||
const caller = appRouter.createCaller({
|
||||
db,
|
||||
deviceType: undefined,
|
||||
session: null,
|
||||
});
|
||||
spy.mockImplementation(() => Promise.resolve({ statusCode: 200, durationMs: 123 }));
|
||||
|
||||
// Act
|
||||
const result = await caller.ping({ url });
|
||||
const result = await caller.ping({ id: app.id });
|
||||
|
||||
// Assert
|
||||
expect(result.url).toBe(url);
|
||||
expect("statusCode" in result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
const createApp = ({ url }: { url: string }) =>
|
||||
({
|
||||
id: createId(),
|
||||
iconUrl: "",
|
||||
name: "Test App",
|
||||
href: url,
|
||||
}) satisfies InferInsertModel<typeof apps>;
|
||||
@@ -1,43 +1,97 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
|
||||
import { sendPingRequestAsync } from "@homarr/ping";
|
||||
import { pingChannel, pingUrlChannel } from "@homarr/redis";
|
||||
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
import { AppRepository } from "../app";
|
||||
|
||||
export const appRouter = createTRPCRouter({
|
||||
ping: publicProcedure.input(z.object({ url: z.string() })).query(async ({ input }) => {
|
||||
const pingResult = await sendPingRequestAsync(input.url);
|
||||
ping: publicProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
|
||||
const boardSettings = await getServerSettingByKeyAsync(ctx.db, "board");
|
||||
if (boardSettings.forceDisableStatus) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: "Ping functionality is disabled by server settings",
|
||||
});
|
||||
}
|
||||
|
||||
const repository = new AppRepository(ctx.db, ctx.session?.user ?? null);
|
||||
const app = await repository.getByIdAsync(input.id);
|
||||
|
||||
if (!app) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "App not found",
|
||||
});
|
||||
}
|
||||
|
||||
const pingUrl = app.pingUrl ?? app.href;
|
||||
|
||||
if (!pingUrl) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "No URL to ping configured for this app",
|
||||
});
|
||||
}
|
||||
|
||||
const pingResult = await sendPingRequestAsync(pingUrl);
|
||||
|
||||
return {
|
||||
url: input.url,
|
||||
url: pingUrl,
|
||||
...pingResult,
|
||||
};
|
||||
}),
|
||||
updatedPing: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
url: z.string(),
|
||||
id: z.string(),
|
||||
}),
|
||||
)
|
||||
.subscription(async ({ input }) => {
|
||||
await pingUrlChannel.addAsync(input.url);
|
||||
.subscription(async ({ ctx, input }) => {
|
||||
const boardSettings = await getServerSettingByKeyAsync(ctx.db, "board");
|
||||
if (boardSettings.forceDisableStatus) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: "Ping functionality is disabled by server settings",
|
||||
});
|
||||
}
|
||||
|
||||
const pingResult = await sendPingRequestAsync(input.url);
|
||||
const repository = new AppRepository(ctx.db, ctx.session?.user ?? null);
|
||||
const app = await repository.getByIdAsync(input.id);
|
||||
|
||||
if (!app) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "App not found",
|
||||
});
|
||||
}
|
||||
|
||||
const pingUrl = app.pingUrl ?? app.href;
|
||||
|
||||
if (!pingUrl) {
|
||||
throw new TRPCError({
|
||||
code: "CONFLICT",
|
||||
message: "No URL to ping configured for this app",
|
||||
});
|
||||
}
|
||||
|
||||
await pingUrlChannel.addAsync(pingUrl);
|
||||
|
||||
return observable<{ url: string; statusCode: number; durationMs: number } | { url: string; error: string }>(
|
||||
(emit) => {
|
||||
emit.next({ url: input.url, ...pingResult });
|
||||
const unsubscribe = pingChannel.subscribe((message) => {
|
||||
// Only emit if same url
|
||||
if (message.url !== input.url) return;
|
||||
if (message.url !== pingUrl) return;
|
||||
emit.next(message);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
void pingUrlChannel.removeAsync(input.url);
|
||||
void pingUrlChannel.removeAsync(pingUrl);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { observable } from "@trpc/server/observable";
|
||||
|
||||
import type { CoolifyInstanceInfo } from "@homarr/integrations/types";
|
||||
import { coolifyRequestHandler } from "@homarr/request-handler/coolify";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
export const coolifyRouter = createTRPCRouter({
|
||||
getInstancesInfo: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", "coolify"))
|
||||
.query(async ({ ctx }) => {
|
||||
const results = await Promise.all(
|
||||
ctx.integrations.map(async (integration) => {
|
||||
const innerHandler = coolifyRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
|
||||
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
instanceInfo: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return results;
|
||||
}),
|
||||
subscribeInstancesInfo: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", "coolify"))
|
||||
.subscription(({ ctx }) => {
|
||||
return observable<{ integrationId: string; instanceInfo: CoolifyInstanceInfo; timestamp: Date }>((emit) => {
|
||||
const unsubscribes = ctx.integrations.map((integration) => {
|
||||
const innerHandler = coolifyRequestHandler.handler(integration, {});
|
||||
return innerHandler.subscribe((instanceInfo) => {
|
||||
emit.next({
|
||||
integrationId: integration.id,
|
||||
instanceInfo,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
||||
};
|
||||
});
|
||||
}),
|
||||
});
|
||||
@@ -1,46 +1,28 @@
|
||||
import { lazy } from "@trpc/server";
|
||||
|
||||
import { createTRPCRouter } from "../../trpc";
|
||||
import { appRouter } from "./app";
|
||||
import { calendarRouter } from "./calendar";
|
||||
import { dnsHoleRouter } from "./dns-hole";
|
||||
import { downloadsRouter } from "./downloads";
|
||||
import { firewallRouter } from "./firewall";
|
||||
import { healthMonitoringRouter } from "./health-monitoring";
|
||||
import { indexerManagerRouter } from "./indexer-manager";
|
||||
import { mediaReleaseRouter } from "./media-release";
|
||||
import { mediaRequestsRouter } from "./media-requests";
|
||||
import { mediaServerRouter } from "./media-server";
|
||||
import { mediaTranscodingRouter } from "./media-transcoding";
|
||||
import { minecraftRouter } from "./minecraft";
|
||||
import { networkControllerRouter } from "./network-controller";
|
||||
import { notebookRouter } from "./notebook";
|
||||
import { notificationsRouter } from "./notifications";
|
||||
import { optionsRouter } from "./options";
|
||||
import { releasesRouter } from "./releases";
|
||||
import { rssFeedRouter } from "./rssFeed";
|
||||
import { smartHomeRouter } from "./smart-home";
|
||||
import { stockPriceRouter } from "./stocks";
|
||||
import { weatherRouter } from "./weather";
|
||||
|
||||
export const widgetRouter = createTRPCRouter({
|
||||
notebook: notebookRouter,
|
||||
weather: weatherRouter,
|
||||
app: appRouter,
|
||||
dnsHole: dnsHoleRouter,
|
||||
smartHome: smartHomeRouter,
|
||||
stockPrice: stockPriceRouter,
|
||||
mediaServer: mediaServerRouter,
|
||||
mediaRelease: mediaReleaseRouter,
|
||||
calendar: calendarRouter,
|
||||
downloads: downloadsRouter,
|
||||
mediaRequests: mediaRequestsRouter,
|
||||
rssFeed: rssFeedRouter,
|
||||
indexerManager: indexerManagerRouter,
|
||||
healthMonitoring: healthMonitoringRouter,
|
||||
mediaTranscoding: mediaTranscodingRouter,
|
||||
minecraft: minecraftRouter,
|
||||
options: optionsRouter,
|
||||
releases: releasesRouter,
|
||||
networkController: networkControllerRouter,
|
||||
firewall: firewallRouter,
|
||||
notifications: notificationsRouter,
|
||||
coolify: lazy(() => import("./coolify").then((mod) => mod.coolifyRouter)),
|
||||
notebook: lazy(() => import("./notebook").then((mod) => mod.notebookRouter)),
|
||||
weather: lazy(() => import("./weather").then((mod) => mod.weatherRouter)),
|
||||
app: lazy(() => import("./app").then((mod) => mod.appRouter)),
|
||||
dnsHole: lazy(() => import("./dns-hole").then((mod) => mod.dnsHoleRouter)),
|
||||
smartHome: lazy(() => import("./smart-home").then((mod) => mod.smartHomeRouter)),
|
||||
stockPrice: lazy(() => import("./stocks").then((mod) => mod.stockPriceRouter)),
|
||||
mediaServer: lazy(() => import("./media-server").then((mod) => mod.mediaServerRouter)),
|
||||
mediaRelease: lazy(() => import("./media-release").then((mod) => mod.mediaReleaseRouter)),
|
||||
calendar: lazy(() => import("./calendar").then((mod) => mod.calendarRouter)),
|
||||
downloads: lazy(() => import("./downloads").then((mod) => mod.downloadsRouter)),
|
||||
mediaRequests: lazy(() => import("./media-requests").then((mod) => mod.mediaRequestsRouter)),
|
||||
rssFeed: lazy(() => import("./rssFeed").then((mod) => mod.rssFeedRouter)),
|
||||
indexerManager: lazy(() => import("./indexer-manager").then((mod) => mod.indexerManagerRouter)),
|
||||
healthMonitoring: lazy(() => import("./health-monitoring").then((mod) => mod.healthMonitoringRouter)),
|
||||
mediaTranscoding: lazy(() => import("./media-transcoding").then((mod) => mod.mediaTranscodingRouter)),
|
||||
minecraft: lazy(() => import("./minecraft").then((mod) => mod.minecraftRouter)),
|
||||
options: lazy(() => import("./options").then((mod) => mod.optionsRouter)),
|
||||
releases: lazy(() => import("./releases").then((mod) => mod.releasesRouter)),
|
||||
networkController: lazy(() => import("./network-controller").then((mod) => mod.networkControllerRouter)),
|
||||
firewall: lazy(() => import("./firewall").then((mod) => mod.firewallRouter)),
|
||||
notifications: lazy(() => import("./notifications").then((mod) => mod.notificationsRouter)),
|
||||
});
|
||||
@@ -33,11 +33,11 @@
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"cookies": "^0.9.1",
|
||||
"ldapts": "8.1.2",
|
||||
"next": "16.1.1",
|
||||
"ldapts": "8.1.6",
|
||||
"next": "16.1.6",
|
||||
"next-auth": "5.0.0-beta.30",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -47,7 +47,7 @@
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"@types/cookies": "0.9.2",
|
||||
"eslint": "^9.39.2",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,8 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/api": "workspace:^0.1.0",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
|
||||
@@ -28,13 +28,13 @@
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"dotenv": "^17.2.3"
|
||||
"dotenv": "^17.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"esbuild": "^0.27.2",
|
||||
"esbuild": "^0.27.3",
|
||||
"eslint": "^9.39.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -27,13 +27,14 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@paralleldrive/cuid2": "^3.1.0",
|
||||
"@paralleldrive/cuid2": "^3.3.0",
|
||||
"cookie": "1.1.1",
|
||||
"dayjs": "^1.11.19",
|
||||
"next": "16.1.1",
|
||||
"next": "16.1.6",
|
||||
"octokit": "^5.0.5",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"undici": "7.16.0",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"undici": "7.18.2",
|
||||
"zod": "^4.2.1",
|
||||
"zod-validation-error": "^5.0.0"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { SerializeOptions } from "cookie";
|
||||
import { parse, serialize } from "cookie";
|
||||
import { parseCookie, stringifySetCookie } from "cookie";
|
||||
|
||||
export function parseCookies(cookieString: string) {
|
||||
return parse(cookieString);
|
||||
return parseCookie(cookieString);
|
||||
}
|
||||
|
||||
export function setClientCookie(name: string, value: string, options: SerializeOptions = {}) {
|
||||
document.cookie = serialize(name, value, options);
|
||||
document.cookie = stringifySetCookie(name, value, options);
|
||||
}
|
||||
@@ -38,12 +38,12 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@t3-oss/env-nextjs": "^0.13.10",
|
||||
"better-sqlite3": "^12.5.0",
|
||||
"better-sqlite3": "^12.6.2",
|
||||
"dns-caching": "^0.2.9",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"ioredis": "5.8.2",
|
||||
"mysql2": "3.16.0",
|
||||
"pg": "^8.16.3",
|
||||
"ioredis": "5.9.2",
|
||||
"mysql2": "3.16.3",
|
||||
"pg": "^8.18.0",
|
||||
"superjson": "2.2.6",
|
||||
"winston": "3.19.0",
|
||||
"zod": "^4.2.1"
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||
"@tanstack/react-query": "^5.90.16",
|
||||
"@trpc/client": "^11.8.1",
|
||||
"@trpc/server": "^11.8.1",
|
||||
"@trpc/tanstack-react-query": "^11.8.1",
|
||||
"@tanstack/react-query": "^5.90.20",
|
||||
"@trpc/client": "^11.9.0",
|
||||
"@trpc/server": "^11.9.0",
|
||||
"@trpc/tanstack-react-query": "^11.9.0",
|
||||
"node-cron": "^4.2.1",
|
||||
"react": "19.2.3",
|
||||
"react": "19.2.4",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -41,7 +41,7 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/react": "19.2.7",
|
||||
"@types/react": "19.2.13",
|
||||
"eslint": "^9.39.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EVERY_5_SECONDS } from "@homarr/cron-jobs-core/expressions";
|
||||
import { coolifyRequestHandler } from "@homarr/request-handler/coolify";
|
||||
import { clusterInfoRequestHandler, systemInfoRequestHandler } from "@homarr/request-handler/health-monitoring";
|
||||
import { createRequestIntegrationJobHandler } from "@homarr/request-handler/lib/cached-request-integration-job-handler";
|
||||
|
||||
@@ -9,16 +10,20 @@ export const healthMonitoringJob = createCronJob("healthMonitoring", EVERY_5_SEC
|
||||
(integration, itemOptions: Record<string, never>) => {
|
||||
const { kind } = integration;
|
||||
|
||||
if (kind !== "proxmox" && kind !== "mock") {
|
||||
if (kind !== "proxmox" && kind !== "mock" && kind !== "coolify") {
|
||||
return systemInfoRequestHandler.handler({ ...integration, kind }, itemOptions);
|
||||
}
|
||||
if (kind === "coolify") {
|
||||
return coolifyRequestHandler.handler({ ...integration, kind }, itemOptions);
|
||||
}
|
||||
return clusterInfoRequestHandler.handler({ ...integration, kind }, itemOptions);
|
||||
},
|
||||
{
|
||||
widgetKinds: ["healthMonitoring", "systemResources"],
|
||||
widgetKinds: ["healthMonitoring", "systemResources", "coolify"],
|
||||
getInput: {
|
||||
healthMonitoring: () => ({}),
|
||||
systemResources: () => ({}),
|
||||
coolify: () => ({}),
|
||||
},
|
||||
},
|
||||
),
|
||||
|
||||
@@ -47,17 +47,17 @@
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.10",
|
||||
"@paralleldrive/cuid2": "^3.1.0",
|
||||
"@mantine/core": "^8.3.14",
|
||||
"@paralleldrive/cuid2": "^3.3.0",
|
||||
"@testcontainers/mysql": "^11.11.0",
|
||||
"@testcontainers/postgresql": "^11.11.0",
|
||||
"better-sqlite3": "^12.5.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"better-sqlite3": "^12.6.2",
|
||||
"dotenv": "^17.2.4",
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"mysql2": "3.16.0",
|
||||
"pg": "^8.16.3",
|
||||
"mysql2": "3.16.3",
|
||||
"pg": "^8.18.0",
|
||||
"superjson": "2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -67,9 +67,9 @@
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/pg": "^8.16.0",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "^0.27.2",
|
||||
"esbuild": "^0.27.3",
|
||||
"eslint": "^9.39.2",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"fast-xml-parser": "^5.3.3",
|
||||
"fast-xml-parser": "^5.3.5",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -305,6 +305,14 @@ export const integrationDefs = {
|
||||
category: ["healthMonitoring"],
|
||||
documentationUrl: createDocumentationLink("/docs/integrations/unraid"),
|
||||
},
|
||||
coolify: {
|
||||
name: "Coolify",
|
||||
secretKinds: [["apiKey"]],
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/coolify.svg",
|
||||
category: ["healthMonitoring"],
|
||||
// @ts-expect-error - docs page will be created when integration is merged
|
||||
documentationUrl: createDocumentationLink("/docs/integrations/coolify"),
|
||||
},
|
||||
// This integration only returns mock data, it is used during development (but can also be used in production by directly going to the create page)
|
||||
mock: {
|
||||
name: "Mock",
|
||||
|
||||
@@ -29,5 +29,7 @@ export const widgetKinds = [
|
||||
"firewall",
|
||||
"notifications",
|
||||
"systemResources",
|
||||
"coolify",
|
||||
"systemDisks",
|
||||
] as const;
|
||||
export type WidgetKind = (typeof widgetKinds)[number];
|
||||
@@ -32,7 +32,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/dockerode": "^3.3.47",
|
||||
"@types/dockerode": "^4.0.1",
|
||||
"eslint": "^9.39.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/form": "^8.3.10",
|
||||
"@mantine/form": "^8.3.14",
|
||||
"mantine-form-zod-resolver": "^1.3.0",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.10",
|
||||
"react": "19.2.3",
|
||||
"@mantine/core": "^8.3.14",
|
||||
"react": "19.2.4",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -39,14 +39,14 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@jellyfin/sdk": "^0.13.0",
|
||||
"@octokit/auth-app": "^8.1.2",
|
||||
"@octokit/auth-app": "^8.2.0",
|
||||
"ical.js": "^2.2.1",
|
||||
"maria2": "^0.4.1",
|
||||
"node-ical": "^0.22.1",
|
||||
"octokit": "^5.0.5",
|
||||
"proxmox-api": "1.1.1",
|
||||
"tsdav": "^2.1.6",
|
||||
"undici": "7.16.0",
|
||||
"tsdav": "^2.1.7",
|
||||
"undici": "7.18.2",
|
||||
"xml2js": "^0.6.2",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { IntegrationKind } from "@homarr/definitions";
|
||||
|
||||
import { AdGuardHomeIntegration } from "../adguard-home/adguard-home-integration";
|
||||
import { CodebergIntegration } from "../codeberg/codeberg-integration";
|
||||
import { CoolifyIntegration } from "../coolify/coolify-integration";
|
||||
import { DashDotIntegration } from "../dashdot/dashdot-integration";
|
||||
import { DockerHubIntegration } from "../docker-hub/docker-hub-integration";
|
||||
import { Aria2Integration } from "../download-client/aria2/aria2-integration";
|
||||
@@ -103,6 +104,7 @@ export const integrationCreators = {
|
||||
mock: MockIntegration,
|
||||
truenas: TrueNasIntegration,
|
||||
unraid: UnraidIntegration,
|
||||
coolify: CoolifyIntegration,
|
||||
} satisfies Record<IntegrationKind, IntegrationInstance | [(input: IntegrationInput) => Promise<Integration>]>;
|
||||
|
||||
type IntegrationInstanceOfKind<TKind extends keyof typeof integrationCreators> = {
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
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 type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||
import type {
|
||||
CoolifyApplication,
|
||||
CoolifyApplicationWithContext,
|
||||
CoolifyEnvironment,
|
||||
CoolifyEnvironmentVariable,
|
||||
CoolifyHealthcheck,
|
||||
CoolifyInstanceInfo,
|
||||
CoolifyProject,
|
||||
CoolifyProjectWithEnvironments,
|
||||
CoolifyResource,
|
||||
CoolifyServer,
|
||||
CoolifyService,
|
||||
CoolifyServiceWithContext,
|
||||
} from "./coolify-types";
|
||||
import {
|
||||
coolifyApplicationLogSchema,
|
||||
coolifyApplicationSchema,
|
||||
coolifyEnvironmentSchema,
|
||||
coolifyEnvironmentVariableSchema,
|
||||
coolifyHealthcheckSchema,
|
||||
coolifyProjectSchema,
|
||||
coolifyProjectWithEnvironmentsSchema,
|
||||
coolifyResourceSchema,
|
||||
coolifyServerSchema,
|
||||
coolifyServiceSchema,
|
||||
} from "./coolify-types";
|
||||
|
||||
export class CoolifyIntegration extends Integration {
|
||||
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
|
||||
const healthUrl = this.url("/api/health");
|
||||
const healthResponse = await input.fetchAsync(healthUrl, {});
|
||||
|
||||
if (!healthResponse.ok) {
|
||||
throw new ResponseError(healthResponse);
|
||||
}
|
||||
|
||||
// Test API key authentication
|
||||
const versionUrl = this.url("/api/v1/version");
|
||||
const versionResponse = await input.fetchAsync(versionUrl, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!versionResponse.ok) {
|
||||
throw new ResponseError(versionResponse);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Coolify version information
|
||||
* https://coolify.io/docs/api-reference/api/operations/version
|
||||
*/
|
||||
public async getVersionAsync(): Promise<string> {
|
||||
const url = this.url("/api/v1/version");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
const version = await response.text();
|
||||
return version.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Coolify healthcheck status
|
||||
* https://coolify.io/docs/api-reference/api/operations/healthcheck
|
||||
*/
|
||||
public async getHealthcheckAsync(): Promise<CoolifyHealthcheck> {
|
||||
const url = this.url("/api/v1/healthcheck");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyHealthcheckSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* List all applications
|
||||
* https://coolify.io/docs/api-reference/api/operations/list-applications
|
||||
*/
|
||||
public async listApplicationsAsync(): Promise<CoolifyApplication[]> {
|
||||
const url = this.url("/api/v1/applications");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyApplicationSchema.array().parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application by UUID
|
||||
* https://coolify.io/docs/api-reference/api/operations/get-application-by-uuid
|
||||
*/
|
||||
public async getApplicationByUuidAsync(uuid: string): Promise<CoolifyApplication> {
|
||||
const url = this.url(`/api/v1/applications/${uuid}`);
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyApplicationSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application logs by UUID
|
||||
* https://coolify.io/docs/api-reference/api/operations/get-application-logs-by-uuid
|
||||
*/
|
||||
public async getApplicationLogsAsync(uuid: string): Promise<string> {
|
||||
const url = this.url(`/api/v1/applications/${uuid}/logs`);
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
const result = coolifyApplicationLogSchema.parse(await response.json());
|
||||
return result.logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* List environment variables by application UUID
|
||||
* https://coolify.io/docs/api-reference/api/operations/list-envs-by-application-uuid
|
||||
*/
|
||||
public async listEnvsByApplicationUuidAsync(uuid: string): Promise<CoolifyEnvironmentVariable[]> {
|
||||
const url = this.url(`/api/v1/applications/${uuid}/envs`);
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyEnvironmentVariableSchema.array().parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* List projects
|
||||
* https://coolify.io/docs/api-reference/api/operations/list-projects
|
||||
*/
|
||||
public async listProjectsAsync(): Promise<CoolifyProject[]> {
|
||||
const url = this.url("/api/v1/projects");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyProjectSchema.array().parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project by UUID (includes environments)
|
||||
* https://coolify.io/docs/api-reference/api/operations/get-project-by-uuid
|
||||
*/
|
||||
public async getProjectByUuidAsync(uuid: string): Promise<CoolifyProjectWithEnvironments> {
|
||||
const url = this.url(`/api/v1/projects/${uuid}`);
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyProjectWithEnvironmentsSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get environments
|
||||
* https://coolify.io/docs/api-reference/api/operations/get-environments
|
||||
*/
|
||||
public async getEnvironmentsAsync(): Promise<CoolifyEnvironment[]> {
|
||||
const url = this.url("/api/v1/environments");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyEnvironmentSchema.array().parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get environment by name or UUID
|
||||
* https://coolify.io/docs/api-reference/api/operations/get-environment-by-name-or-uuid
|
||||
*/
|
||||
public async getEnvironmentByNameOrUuidAsync(nameOrUuid: string): Promise<CoolifyEnvironment> {
|
||||
const url = this.url(`/api/v1/environments/${nameOrUuid}`);
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyEnvironmentSchema.parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* List resources
|
||||
* https://coolify.io/docs/api-reference/api/operations/list-resources
|
||||
*/
|
||||
public async listResourcesAsync(): Promise<CoolifyResource[]> {
|
||||
const url = this.url("/api/v1/resources");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyResourceSchema.array().parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* List servers
|
||||
* https://coolify.io/docs/api-reference/api/operations/list-servers
|
||||
*/
|
||||
public async listServersAsync(): Promise<CoolifyServer[]> {
|
||||
const url = this.url("/api/v1/servers");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyServerSchema.array().parse(await response.json());
|
||||
}
|
||||
|
||||
/**
|
||||
* List services
|
||||
* https://coolify.io/docs/api-reference/api/operations/list-services
|
||||
*/
|
||||
public async listServicesAsync(): Promise<CoolifyService[]> {
|
||||
const url = this.url("/api/v1/services");
|
||||
const response = await fetchWithTrustedCertificatesAsync(url, {
|
||||
headers: this.getAuthHeaders(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ResponseError(response);
|
||||
}
|
||||
|
||||
return coolifyServiceSchema.array().parse(await response.json());
|
||||
}
|
||||
|
||||
public async getInstanceInfoAsync(): Promise<CoolifyInstanceInfo> {
|
||||
const [version, applications, services, projectsWithEnvs, servers] = await Promise.all([
|
||||
this.getVersionAsync(),
|
||||
this.listApplicationsAsync(),
|
||||
this.listServicesAsync(),
|
||||
this.listProjectsWithEnvironmentsAsync(),
|
||||
this.listServersAsync(),
|
||||
]);
|
||||
|
||||
const envToProjectMap = new Map<
|
||||
number,
|
||||
{ projectName: string; projectUuid: string; environmentName: string; environmentUuid?: string }
|
||||
>();
|
||||
for (const project of projectsWithEnvs) {
|
||||
for (const env of project.environments ?? []) {
|
||||
if (env.id != null) {
|
||||
envToProjectMap.set(env.id, {
|
||||
projectName: project.name,
|
||||
projectUuid: project.uuid,
|
||||
environmentName: env.name,
|
||||
environmentUuid: env.uuid ?? undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const applicationsWithContext: CoolifyApplicationWithContext[] = applications.map((app) => {
|
||||
const context = app.environment_id ? envToProjectMap.get(app.environment_id) : undefined;
|
||||
return {
|
||||
...app,
|
||||
projectName: context?.projectName,
|
||||
projectUuid: context?.projectUuid,
|
||||
environmentName: context?.environmentName,
|
||||
environmentUuid: context?.environmentUuid,
|
||||
};
|
||||
});
|
||||
|
||||
const servicesWithContext: CoolifyServiceWithContext[] = services.map((service) => {
|
||||
const context = service.environment_id ? envToProjectMap.get(service.environment_id) : undefined;
|
||||
const fqdn = service.fqdn ?? service.applications?.find((app) => app.fqdn)?.fqdn ?? undefined;
|
||||
return {
|
||||
...service,
|
||||
fqdn,
|
||||
projectName: context?.projectName,
|
||||
projectUuid: context?.projectUuid,
|
||||
environmentName: context?.environmentName,
|
||||
environmentUuid: context?.environmentUuid,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
version,
|
||||
applications: applicationsWithContext,
|
||||
services: servicesWithContext,
|
||||
servers,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* List all projects with their environments
|
||||
*/
|
||||
private async listProjectsWithEnvironmentsAsync(): Promise<CoolifyProjectWithEnvironments[]> {
|
||||
const projects = await this.listProjectsAsync();
|
||||
const projectsWithEnvs = await Promise.all(projects.map((project) => this.getProjectByUuidAsync(project.uuid)));
|
||||
return projectsWithEnvs;
|
||||
}
|
||||
|
||||
private getAuthHeaders(): Record<string, string> {
|
||||
return {
|
||||
Authorization: `Bearer ${this.getSecretValue("apiKey")}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Coolify API Types and Schemas based on https://coolify.io/docs/api-reference/api
|
||||
import { z } from "zod/v4";
|
||||
|
||||
// Health check schema
|
||||
export const coolifyHealthcheckSchema = z.object({
|
||||
status: z.string(),
|
||||
});
|
||||
|
||||
export type CoolifyHealthcheck = z.infer<typeof coolifyHealthcheckSchema>;
|
||||
|
||||
// Application log schema
|
||||
export const coolifyApplicationLogSchema = z.object({
|
||||
logs: z.string(),
|
||||
});
|
||||
|
||||
export type CoolifyApplicationLog = z.infer<typeof coolifyApplicationLogSchema>;
|
||||
|
||||
// Environment variable schema
|
||||
export const coolifyEnvironmentVariableSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
key: z.string(),
|
||||
value: z.string(),
|
||||
is_build_time: z.boolean().nullish(),
|
||||
is_preview: z.boolean().nullish(),
|
||||
application_id: z.number().nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyEnvironmentVariable = z.infer<typeof coolifyEnvironmentVariableSchema>;
|
||||
|
||||
// Application schema (only fields used in the widget)
|
||||
export const coolifyApplicationSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
uuid: z.string(),
|
||||
name: z.string(),
|
||||
status: z.string().nullish(),
|
||||
fqdn: z.string().nullish(),
|
||||
environment_id: z.number().nullish(),
|
||||
server_id: z.number().nullish(),
|
||||
destination_id: z.number().nullish(),
|
||||
updated_at: z.string().nullish(),
|
||||
last_online_at: z.string().nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyApplication = z.infer<typeof coolifyApplicationSchema>;
|
||||
|
||||
// Project schema
|
||||
export const coolifyProjectSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
uuid: z.string(),
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
export type CoolifyProject = z.infer<typeof coolifyProjectSchema>;
|
||||
|
||||
// Environment schema
|
||||
export const coolifyEnvironmentSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
uuid: z.string().nullish(),
|
||||
name: z.string(),
|
||||
project_id: z.number().nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyEnvironment = z.infer<typeof coolifyEnvironmentSchema>;
|
||||
|
||||
// Project with environments schema
|
||||
export const coolifyProjectWithEnvironmentsSchema = coolifyProjectSchema.extend({
|
||||
environments: z.array(coolifyEnvironmentSchema).nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyProjectWithEnvironments = z.infer<typeof coolifyProjectWithEnvironmentsSchema>;
|
||||
|
||||
// Resource schema
|
||||
export const coolifyResourceSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
uuid: z.string(),
|
||||
name: z.string(),
|
||||
type: z.string(),
|
||||
status: z.string().nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyResource = z.infer<typeof coolifyResourceSchema>;
|
||||
|
||||
// Server settings schema
|
||||
export const coolifyServerSettingsSchema = z.object({
|
||||
server_id: z.number().nullish(),
|
||||
is_build_server: z.boolean().nullish(),
|
||||
is_reachable: z.boolean().nullish(),
|
||||
is_usable: z.boolean().nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyServerSettings = z.infer<typeof coolifyServerSettingsSchema>;
|
||||
|
||||
// Server schema
|
||||
export const coolifyServerSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
uuid: z.string(),
|
||||
name: z.string(),
|
||||
ip: z.string().nullish(),
|
||||
is_reachable: z.boolean().nullish(),
|
||||
is_usable: z.boolean().nullish(),
|
||||
settings: coolifyServerSettingsSchema.nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyServer = z.infer<typeof coolifyServerSchema>;
|
||||
|
||||
// Service application schema (nested in services)
|
||||
export const coolifyServiceApplicationSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
uuid: z.string(),
|
||||
name: z.string(),
|
||||
fqdn: z.string().nullish(),
|
||||
status: z.string().nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyServiceApplication = z.infer<typeof coolifyServiceApplicationSchema>;
|
||||
|
||||
// Service schema
|
||||
export const coolifyServiceSchema = z.object({
|
||||
id: z.number().nullish(),
|
||||
uuid: z.string(),
|
||||
name: z.string(),
|
||||
status: z.string().nullish(),
|
||||
fqdn: z.string().nullish(),
|
||||
environment_id: z.number().nullish(),
|
||||
server_id: z.number().nullish(),
|
||||
destination_id: z.number().nullish(),
|
||||
updated_at: z.string().nullish(),
|
||||
applications: z.array(coolifyServiceApplicationSchema).nullish(),
|
||||
});
|
||||
|
||||
export type CoolifyService = z.infer<typeof coolifyServiceSchema>;
|
||||
|
||||
// Extended types with context (not from API, added by integration)
|
||||
export interface CoolifyApplicationWithContext extends CoolifyApplication {
|
||||
projectName?: string;
|
||||
projectUuid?: string;
|
||||
environmentName?: string;
|
||||
environmentUuid?: string;
|
||||
}
|
||||
|
||||
export interface CoolifyServiceWithContext extends CoolifyService {
|
||||
projectName?: string;
|
||||
projectUuid?: string;
|
||||
environmentName?: string;
|
||||
environmentUuid?: string;
|
||||
}
|
||||
|
||||
// Aggregated information for the Coolify instance
|
||||
export interface CoolifyInstanceInfo {
|
||||
version: string;
|
||||
applications: CoolifyApplicationWithContext[];
|
||||
services: CoolifyServiceWithContext[];
|
||||
servers: CoolifyServer[];
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
import { Request, Response } from "undici";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
||||
|
||||
import { CoolifyIntegration } from "../coolify-integration";
|
||||
import type {
|
||||
CoolifyApplication,
|
||||
CoolifyProject,
|
||||
CoolifyProjectWithEnvironments,
|
||||
CoolifyServer,
|
||||
CoolifyService,
|
||||
} from "../coolify-types";
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/http", () => ({
|
||||
fetchWithTrustedCertificatesAsync: vi.fn(),
|
||||
}));
|
||||
|
||||
const TEST_API_KEY = "test-api-key-12345";
|
||||
const TEST_URL = "https://coolify.example.com";
|
||||
|
||||
type MockResponseData = string | Record<string, unknown> | unknown[];
|
||||
|
||||
const mockFetchWithTrustedCertificates = vi.mocked(fetchWithTrustedCertificatesAsync);
|
||||
|
||||
const setupMockFetch = (responses: Record<string, MockResponseData>) => {
|
||||
mockFetchWithTrustedCertificates.mockImplementation((url) => {
|
||||
const urlString = typeof url === "string" ? url : url instanceof Request ? url.url : url.toString();
|
||||
const urlObj = new URL(urlString);
|
||||
const path = urlObj.pathname;
|
||||
|
||||
if (path in responses) {
|
||||
const data = responses[path];
|
||||
const body = typeof data === "string" ? data : JSON.stringify(data);
|
||||
return Promise.resolve(
|
||||
new Response(body, {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
}) as unknown as Awaited<ReturnType<typeof fetchWithTrustedCertificatesAsync>>,
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve(
|
||||
new Response(JSON.stringify({ error: "Not Found" }), {
|
||||
status: 404,
|
||||
headers: { "content-type": "application/json" },
|
||||
}) as unknown as Awaited<ReturnType<typeof fetchWithTrustedCertificatesAsync>>,
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const createCoolifyIntegration = () => {
|
||||
return new CoolifyIntegration({
|
||||
id: "test-coolify",
|
||||
name: "Test Coolify",
|
||||
url: TEST_URL,
|
||||
externalUrl: null,
|
||||
decryptedSecrets: [{ kind: "apiKey", value: TEST_API_KEY }],
|
||||
});
|
||||
};
|
||||
|
||||
describe("CoolifyIntegration", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("getVersionAsync", () => {
|
||||
test("should return version string from Coolify API", async () => {
|
||||
setupMockFetch({
|
||||
"/api/v1/version": "4.0.0-beta.460",
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.getVersionAsync();
|
||||
|
||||
expect(result).toBe("4.0.0-beta.460");
|
||||
expect(mockFetchWithTrustedCertificates).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should throw error when API returns error", async () => {
|
||||
mockFetchWithTrustedCertificates.mockResolvedValue(
|
||||
new Response("Unauthorized", {
|
||||
status: 401,
|
||||
headers: { "content-type": "text/plain" },
|
||||
}) as unknown as Awaited<ReturnType<typeof fetchWithTrustedCertificatesAsync>>,
|
||||
);
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
await expect(integration.getVersionAsync()).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("listServersAsync", () => {
|
||||
test("should return list of servers", async () => {
|
||||
const servers: CoolifyServer[] = [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "server-uuid-1",
|
||||
name: "Production Server",
|
||||
ip: "192.168.1.100",
|
||||
is_reachable: true,
|
||||
is_usable: true,
|
||||
settings: { is_build_server: false },
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
uuid: "server-uuid-2",
|
||||
name: "Build Server",
|
||||
ip: "192.168.1.101",
|
||||
is_reachable: true,
|
||||
is_usable: true,
|
||||
settings: { is_build_server: true },
|
||||
},
|
||||
];
|
||||
|
||||
setupMockFetch({
|
||||
"/api/v1/servers": servers,
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.listServersAsync();
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]?.name).toBe("Production Server");
|
||||
expect(result[1]?.settings?.is_build_server).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("listApplicationsAsync", () => {
|
||||
test("should return list of applications", async () => {
|
||||
const applications: CoolifyApplication[] = [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "app-uuid-1",
|
||||
name: "my-app",
|
||||
fqdn: "https://myapp.example.com",
|
||||
status: "running",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
uuid: "app-uuid-2",
|
||||
name: "another-app",
|
||||
fqdn: "https://another.example.com",
|
||||
status: "stopped",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
];
|
||||
|
||||
setupMockFetch({
|
||||
"/api/v1/applications": applications,
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.listApplicationsAsync();
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]?.status).toBe("running");
|
||||
expect(result[1]?.status).toBe("stopped");
|
||||
});
|
||||
});
|
||||
|
||||
describe("listServicesAsync", () => {
|
||||
test("should return list of services", async () => {
|
||||
const services: CoolifyService[] = [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "service-uuid-1",
|
||||
name: "postgresql",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
];
|
||||
|
||||
setupMockFetch({
|
||||
"/api/v1/services": services,
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.listServicesAsync();
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]?.name).toBe("postgresql");
|
||||
});
|
||||
});
|
||||
|
||||
describe("listProjectsAsync", () => {
|
||||
test("should return list of projects", async () => {
|
||||
const projects: CoolifyProject[] = [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "proj-uuid-1",
|
||||
name: "Production",
|
||||
},
|
||||
];
|
||||
|
||||
setupMockFetch({
|
||||
"/api/v1/projects": projects,
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.listProjectsAsync();
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]?.name).toBe("Production");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getInstanceInfoAsync", () => {
|
||||
test("should aggregate all instance data with project context", async () => {
|
||||
const projectWithEnvs: CoolifyProjectWithEnvironments = {
|
||||
id: 1,
|
||||
uuid: "proj-uuid-1",
|
||||
name: "Production",
|
||||
environments: [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "env-uuid-1",
|
||||
name: "production",
|
||||
project_id: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
setupMockFetch({
|
||||
"/api/v1/version": "4.0.0-beta.460",
|
||||
"/api/v1/applications": [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "app-uuid-1",
|
||||
name: "my-app",
|
||||
status: "running",
|
||||
environment_id: 1,
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
],
|
||||
"/api/v1/services": [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "service-uuid-1",
|
||||
name: "postgres",
|
||||
environment_id: 1,
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
],
|
||||
"/api/v1/projects": [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "proj-uuid-1",
|
||||
name: "Production",
|
||||
},
|
||||
],
|
||||
"/api/v1/servers": [
|
||||
{
|
||||
id: 1,
|
||||
uuid: "server-uuid-1",
|
||||
name: "Server 1",
|
||||
ip: "192.168.1.1",
|
||||
is_reachable: true,
|
||||
is_usable: true,
|
||||
settings: { is_build_server: false },
|
||||
},
|
||||
],
|
||||
"/api/v1/projects/proj-uuid-1": projectWithEnvs,
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.getInstanceInfoAsync();
|
||||
|
||||
expect(result.version).toBe("4.0.0-beta.460");
|
||||
expect(result.applications).toHaveLength(1);
|
||||
expect(result.services).toHaveLength(1);
|
||||
expect(result.servers).toHaveLength(1);
|
||||
expect(result.applications[0]?.projectName).toBe("Production");
|
||||
expect(result.applications[0]?.environmentName).toBe("production");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getHealthcheckAsync", () => {
|
||||
test("should return healthcheck status", async () => {
|
||||
setupMockFetch({
|
||||
"/api/v1/healthcheck": { status: "healthy" },
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.getHealthcheckAsync();
|
||||
|
||||
expect(result.status).toBe("healthy");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getApplicationByUuidAsync", () => {
|
||||
test("should return application details by UUID", async () => {
|
||||
const application: CoolifyApplication = {
|
||||
id: 1,
|
||||
uuid: "app-uuid-1",
|
||||
name: "my-app",
|
||||
fqdn: "https://app.example.com",
|
||||
status: "running",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
};
|
||||
|
||||
setupMockFetch({
|
||||
"/api/v1/applications/app-uuid-1": application,
|
||||
});
|
||||
|
||||
const integration = createCoolifyIntegration();
|
||||
const result = await integration.getApplicationByUuidAsync("app-uuid-1");
|
||||
|
||||
expect(result.uuid).toBe("app-uuid-1");
|
||||
expect(result.name).toBe("my-app");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -55,7 +55,7 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
|
||||
cpuTemp: cpuLoad.averageTemperature,
|
||||
availablePkgUpdates: 0,
|
||||
rebootRequired: false,
|
||||
smart: [],
|
||||
smart: [], // API endpoint does not provide S.M.A.R.T data.
|
||||
uptime: info.uptime,
|
||||
version: `${info.operatingSystemVersion}`,
|
||||
loadAverage: {
|
||||
|
||||
@@ -25,6 +25,7 @@ export { TrueNasIntegration } from "./truenas/truenas-integration";
|
||||
export { UnraidIntegration } from "./unraid/unraid-integration";
|
||||
export { OPNsenseIntegration } from "./opnsense/opnsense-integration";
|
||||
export { ICalIntegration } from "./ical/ical-integration";
|
||||
export { CoolifyIntegration } from "./coolify/coolify-integration";
|
||||
|
||||
// Types
|
||||
export type { IntegrationInput } from "./base/integration";
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface SystemHealthMonitoring {
|
||||
deviceName: string;
|
||||
temperature: number | null;
|
||||
overallStatus: string;
|
||||
healthy: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ export class SystemHealthMonitoringMockService implements ISystemHealthMonitorin
|
||||
deviceName: "Mock Device",
|
||||
temperature: Math.floor(Math.random() * 100), // Random temperature between 0 and 99
|
||||
overallStatus: "OK",
|
||||
healthy: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -62,6 +62,8 @@ export class OpenMediaVaultIntegration extends Integration implements ISystemHea
|
||||
const smart = smartResult.data.response.map((smart) => ({
|
||||
deviceName: smart.devicename,
|
||||
temperature: smart.temperature,
|
||||
// https://github.com/openmediavault/openmediavault/blob/2eb871d05cec6ff49603134d3acead561da01e4d/deb/openmediavault/usr/share/php/openmediavault/system/storage/smartinformation.inc#L592-L605
|
||||
healthy: smart.overallstatus === "GOOD",
|
||||
overallStatus: smart.overallstatus,
|
||||
}));
|
||||
|
||||
|
||||
@@ -115,9 +115,8 @@ export class OverseerrIntegration
|
||||
}
|
||||
|
||||
public async getRequestsAsync(): Promise<MediaRequest[]> {
|
||||
//Ensure to get all pending request first
|
||||
const pendingRequests = await fetchWithTrustedCertificatesAsync(
|
||||
this.url("/api/v1/request", { take: -1, filter: "pending" }),
|
||||
this.url("/api/v1/request", { take: 20, filter: "pending" }),
|
||||
{
|
||||
headers: {
|
||||
"X-Api-Key": this.getSecretValue("apiKey"),
|
||||
@@ -125,7 +124,6 @@ export class OverseerrIntegration
|
||||
},
|
||||
);
|
||||
|
||||
//Change 20 to integration setting (set to -1 for all)
|
||||
const allRequests = await fetchWithTrustedCertificatesAsync(this.url("/api/v1/request", { take: 20 }), {
|
||||
headers: {
|
||||
"X-Api-Key": this.getSecretValue("apiKey"),
|
||||
@@ -223,7 +221,7 @@ export class OverseerrIntegration
|
||||
}
|
||||
|
||||
public async getUsersAsync(): Promise<RequestUser[]> {
|
||||
const response = await fetchWithTrustedCertificatesAsync(this.url("/api/v1/user", { take: -1 }), {
|
||||
const response = await fetchWithTrustedCertificatesAsync(this.url("/api/v1/user", { take: 10, sort: "requests" }), {
|
||||
headers: {
|
||||
"X-Api-Key": this.getSecretValue("apiKey"),
|
||||
},
|
||||
|
||||
@@ -112,6 +112,27 @@ export class TrueNasIntegration extends Integration implements ISystemHealthMoni
|
||||
});
|
||||
}
|
||||
|
||||
private async getPoolsAsync() {
|
||||
logger.debug("Retrieving pools", {
|
||||
url: this.wsUrl(),
|
||||
});
|
||||
|
||||
const response = await this.requestAsync("pool.query", [
|
||||
[],
|
||||
{
|
||||
extra: {
|
||||
is_upgraded: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
const result = await poolSchema.parseAsync(response);
|
||||
logger.debug("Retrieved pools", {
|
||||
url: this.wsUrl(),
|
||||
count: result.length,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves data using the reporting method
|
||||
* @see https://www.truenas.com/docs/api/scale_websocket_api.html#reporting
|
||||
@@ -225,6 +246,7 @@ export class TrueNasIntegration extends Integration implements ISystemHealthMoni
|
||||
const cpuData = this.extractLatestReportingData(reporting, "cpu");
|
||||
const cpuTempData = this.extractLatestReportingData(reporting, "cputemp");
|
||||
const memoryData = this.extractLatestReportingData(reporting, "memory");
|
||||
const datasets = await this.getPoolsAsync();
|
||||
|
||||
const netdata = await this.getReportingNetdataAsync();
|
||||
|
||||
@@ -236,14 +258,24 @@ export class TrueNasIntegration extends Integration implements ISystemHealthMoni
|
||||
cpuTemp: Math.max(...cpuTempData.filter((_item, i) => i > 0)),
|
||||
memAvailableInBytes: systemInformation.physmem,
|
||||
memUsedInBytes: memoryData[1] ?? 0, // Index 0 is UNIX timestamp, Index 1 is free space in bytes
|
||||
fileSystem: [],
|
||||
fileSystem: datasets.map((dataset) => ({
|
||||
deviceName: dataset.name,
|
||||
available: `${dataset.size}`, // TODO: can we use number instead of string here?
|
||||
used: `${dataset.allocated}`,
|
||||
percentage: (dataset.allocated / dataset.size) * 100,
|
||||
})),
|
||||
availablePkgUpdates: 0,
|
||||
network: {
|
||||
up: upload * NETWORK_MULTIPLIER,
|
||||
down: download * NETWORK_MULTIPLIER,
|
||||
},
|
||||
loadAverage: null,
|
||||
smart: [],
|
||||
smart: datasets.map((dataset) => ({
|
||||
deviceName: dataset.name,
|
||||
healthy: dataset.healthy,
|
||||
overallStatus: dataset.status,
|
||||
temperature: null,
|
||||
})),
|
||||
uptime: systemInformation.uptime_seconds,
|
||||
version: systemInformation.version,
|
||||
cpuModelName: systemInformation.model,
|
||||
@@ -351,6 +383,17 @@ const reportingItemSchema = z.object({
|
||||
|
||||
type ReportingItem = z.infer<typeof reportingItemSchema>;
|
||||
|
||||
const poolSchema = z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
status: z.string(),
|
||||
healthy: z.boolean(),
|
||||
free: z.number().min(0),
|
||||
size: z.number(),
|
||||
allocated: z.number(),
|
||||
}),
|
||||
);
|
||||
|
||||
const reportingNetDataSchema = z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
|
||||
@@ -11,3 +11,4 @@ export * from "./proxmox/proxmox-types";
|
||||
export * from "./unifi-controller/unifi-controller-types";
|
||||
export * from "./opnsense/opnsense-types";
|
||||
export * from "./interfaces/media-releases";
|
||||
export * from "./coolify/coolify-types";
|
||||
@@ -67,6 +67,8 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit
|
||||
deviceName: disk.name,
|
||||
temperature: disk.temp ?? null,
|
||||
overallStatus: disk.status,
|
||||
// See ArrayDiskStatus from https://studio.apollographql.com/public/Unraid-API/variant/current/explorer
|
||||
healthy: disk.status === "DISK_OK",
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,12 +33,12 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.10",
|
||||
"@mantine/core": "^8.3.14",
|
||||
"@tabler/icons-react": "^3.36.1",
|
||||
"dayjs": "^1.11.19",
|
||||
"next": "16.1.1",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
"dependencies": {
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.10",
|
||||
"@mantine/hooks": "^8.3.10",
|
||||
"react": "19.2.3"
|
||||
"@mantine/core": "^8.3.14",
|
||||
"@mantine/hooks": "^8.3.14",
|
||||
"react": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/notifications": "^8.3.10",
|
||||
"@mantine/notifications": "^8.3.14",
|
||||
"@tabler/icons-react": "^3.36.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.10",
|
||||
"@mantine/hooks": "^8.3.10",
|
||||
"@mantine/core": "^8.3.14",
|
||||
"@mantine/hooks": "^8.3.14",
|
||||
"adm-zip": "0.5.16",
|
||||
"next": "16.1.1",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"superjson": "2.2.6",
|
||||
"zod": "^4.2.1",
|
||||
"zod-form-data": "^3.0.1"
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"@homarr/core": "workspace:^",
|
||||
"@homarr/db": "workspace:^",
|
||||
"@homarr/definitions": "workspace:^",
|
||||
"ioredis": "5.8.2",
|
||||
"ioredis": "5.9.2",
|
||||
"superjson": "2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"dayjs": "^1.11.19",
|
||||
"octokit": "^5.0.5",
|
||||
"superjson": "2.2.6",
|
||||
"undici": "7.16.0",
|
||||
"undici": "7.18.2",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { createIntegrationAsync } from "@homarr/integrations";
|
||||
import type { CoolifyInstanceInfo } from "@homarr/integrations/types";
|
||||
|
||||
import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-request-handler";
|
||||
|
||||
export const coolifyRequestHandler = createCachedIntegrationRequestHandler<
|
||||
CoolifyInstanceInfo,
|
||||
"coolify",
|
||||
Record<string, never>
|
||||
>({
|
||||
async requestAsync(integration, _input) {
|
||||
const integrationInstance = await createIntegrationAsync(integration);
|
||||
return await integrationInstance.getInstanceInfoAsync();
|
||||
},
|
||||
cacheDuration: dayjs.duration(30, "seconds"),
|
||||
queryKey: "coolifyInfo",
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-
|
||||
|
||||
export const systemInfoRequestHandler = createCachedIntegrationRequestHandler<
|
||||
SystemHealthMonitoring,
|
||||
Exclude<IntegrationKindByCategory<"healthMonitoring">, "proxmox">,
|
||||
Exclude<IntegrationKindByCategory<"healthMonitoring">, "proxmox" | "coolify">,
|
||||
Record<string, never>
|
||||
>({
|
||||
async requestAsync(integration, _input) {
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
"dependencies": {
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@mantine/dates": "^8.3.10",
|
||||
"next": "16.1.1",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
"@mantine/dates": "^8.3.14",
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
|
||||
@@ -33,14 +33,14 @@
|
||||
"@homarr/settings": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.10",
|
||||
"@mantine/hooks": "^8.3.10",
|
||||
"@mantine/spotlight": "^8.3.10",
|
||||
"@mantine/core": "^8.3.14",
|
||||
"@mantine/hooks": "^8.3.14",
|
||||
"@mantine/spotlight": "^8.3.14",
|
||||
"@tabler/icons-react": "^3.36.1",
|
||||
"jotai": "^2.16.1",
|
||||
"next": "16.1.1",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"jotai": "^2.17.1",
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"use-deep-compare-effect": "^1.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
"dayjs": "^1.11.19",
|
||||
"deepmerge": "4.3.1",
|
||||
"mantine-react-table": "2.0.0-beta.9",
|
||||
"next": "16.1.1",
|
||||
"next-intl": "4.7.0",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
"next": "16.1.6",
|
||||
"next-intl": "4.8.2",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "",
|
||||
"noResults": "",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "危险",
|
||||
"noResults": "未找到结果",
|
||||
"unsavedChanges": "你有未保存的更改 !",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "显示预览",
|
||||
"hide": "隐藏预览"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "获取容器统计信息失败"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "城市/邮编",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "上传",
|
||||
"down": "下载"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "用户",
|
||||
"enableGravatar": {
|
||||
"label": "启用Gravatar",
|
||||
"description": "未设置自定义头像时,若用户有邮箱,则自动采用其 Libravatar/Gravatar 头像"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "搜索",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "crwdns610:0crwdne610:0",
|
||||
"noResults": "crwdns612:0crwdne612:0",
|
||||
"unsavedChanges": "crwdns2784:0crwdne2784:0",
|
||||
"servers": "crwdns3846:0crwdne3846:0",
|
||||
"applications": "crwdns3848:0crwdne3848:0",
|
||||
"services": "crwdns3850:0crwdne3850:0",
|
||||
"preview": {
|
||||
"show": "crwdns614:0crwdne614:0",
|
||||
"hide": "crwdns616:0crwdne616:0"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "crwdns3428:0crwdne3428:0"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "crwdns3852:0crwdne3852:0",
|
||||
"description": "crwdns3854:0crwdne3854:0",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": "crwdns3856:0crwdne3856:0"
|
||||
},
|
||||
"showApplications": {
|
||||
"label": "crwdns3858:0crwdne3858:0"
|
||||
},
|
||||
"showServices": {
|
||||
"label": "crwdns3860:0crwdne3860:0"
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "crwdns3862:0crwdne3862:0",
|
||||
"applications": "crwdns3864:0crwdne3864:0",
|
||||
"services": "crwdns3866:0crwdne3866:0"
|
||||
},
|
||||
"footer": {
|
||||
"updated": "crwdns3868:0{when}crwdne3868:0"
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "crwdns3870:0crwdne3870:0",
|
||||
"internalServerError": "crwdns3872:0crwdne3872:0"
|
||||
},
|
||||
"server": {
|
||||
"buildServer": "crwdns3874:0crwdne3874:0"
|
||||
},
|
||||
"instances": {
|
||||
"title": "crwdns3876:0crwdne3876:0"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "crwdns1110:0crwdne1110:0",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "crwdns3610:0crwdne3610:0",
|
||||
"down": "crwdns3612:0crwdne3612:0"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "crwdns3828:0crwdne3828:0",
|
||||
"description": "crwdns3830:0crwdne3830:0",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": "crwdns3832:0crwdne3832:0"
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "crwdns3834:0crwdne3834:0",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": "crwdns3836:0crwdne3836:0"
|
||||
},
|
||||
"absolute": {
|
||||
"label": "crwdns3838:0crwdne3838:0"
|
||||
},
|
||||
"free": {
|
||||
"label": "crwdns3840:0crwdne3840:0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": "crwdns3842:0crwdne3842:0"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": "crwdns3844:0crwdne3844:0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3240,7 +3305,7 @@
|
||||
"title": "crwdns1764:0crwdne1764:0",
|
||||
"general": {
|
||||
"title": "crwdns1766:0crwdne1766:0",
|
||||
"text": "crwdns1768:0crwdne1768:0"
|
||||
"text": "crwdns3878:0crwdne3878:0"
|
||||
},
|
||||
"widgetData": {
|
||||
"title": "crwdns1770:0crwdne1770:0",
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "crwdns3822:0crwdne3822:0",
|
||||
"enableGravatar": {
|
||||
"label": "crwdns3824:0crwdne3824:0",
|
||||
"description": "crwdns3826:0crwdne3826:0"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "crwdns2550:0crwdne2550:0",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Nebezpečná zóna",
|
||||
"noResults": "Nebyly nalezeny žádné výsledky",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "Zobrazit náhled",
|
||||
"hide": "Skrýt náhled"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Farezone",
|
||||
"noResults": "Ingen resultater fundet",
|
||||
"unsavedChanges": "Du har ikke gemte ændringer!",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "Vis forhåndsvisning",
|
||||
"hide": "Skjul forhåndsvisning"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "Kunne ikke hente containerstatistik"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "By / Postnummer",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "OP",
|
||||
"down": "NED"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Søg",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Gefahrenbereich",
|
||||
"noResults": "Die Suche ergab keine Treffer",
|
||||
"unsavedChanges": "Sie haben ungespeicherte Änderungen!",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "Vorschau ansehen",
|
||||
"hide": "Vorschau ausblenden"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "Stadt / Postleitzahl",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Suche",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Gefahrenbereich",
|
||||
"noResults": "Die Suche ergab keine Treffer",
|
||||
"unsavedChanges": "Sie haben ungespeicherte Änderungen!",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "Vorschau ansehen",
|
||||
"hide": "Vorschau ausblenden"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "Fehler beim Abrufen der Container Statistiken"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "Stadt / Postleitzahl",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "OBEN",
|
||||
"down": "UNTEN"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Suche",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Επικίνδυνη Περιοχή",
|
||||
"noResults": "Δεν βρέθηκαν αποτελέσματα",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -836,19 +836,19 @@
|
||||
"title": "Request error",
|
||||
"description": {
|
||||
"connection": {
|
||||
"hostUnreachable": "",
|
||||
"networkUnreachable": "",
|
||||
"refused": "",
|
||||
"reset": ""
|
||||
"hostUnreachable": "The server could not be reached. This usually means the host is offline or unreachable from your network.",
|
||||
"networkUnreachable": "The network is unreachable. Please check your Internet connection or network configuration.",
|
||||
"refused": "The server refused the connection. It may not be running or is rejecting requests on the specified port.",
|
||||
"reset": "The connection was unexpectedly closed by the server. This can happen if the server is unstable or restarted."
|
||||
},
|
||||
"dns": {
|
||||
"notFound": "",
|
||||
"timeout": "",
|
||||
"noAnswer": ""
|
||||
"notFound": "The server address could not be found. Please check the URL for typos or invalid domain names.",
|
||||
"timeout": "DNS lookup timed out. This may be a temporary issue—please try again in a few moments.",
|
||||
"noAnswer": "The DNS server didn't return a valid response. The domain may exist but has no valid records."
|
||||
},
|
||||
"timeout": {
|
||||
"aborted": "",
|
||||
"timeout": ""
|
||||
"aborted": "The request was aborted before it could complete. This might be due to a user action or system timeout.",
|
||||
"timeout": "The request took too long to complete and was timed out. Check your network or try again later."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -920,8 +920,8 @@
|
||||
"message": "The path is incorrect. Please double-check the URL"
|
||||
},
|
||||
"tooManyRequests": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Too many requests in a given time",
|
||||
"message": "There were too many requests. You are likely being rate-limited or rejected by the target system"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -963,36 +963,36 @@
|
||||
"newLabel": "New realm"
|
||||
},
|
||||
"personalAccessToken": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "Personal Access Token",
|
||||
"newLabel": "New Personal Access Token"
|
||||
},
|
||||
"topic": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "Topic",
|
||||
"newLabel": "New topic"
|
||||
},
|
||||
"url": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "URL",
|
||||
"newLabel": "New URL"
|
||||
},
|
||||
"opnsenseApiKey": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "API Key (Key)",
|
||||
"newLabel": "New API Key (Key)"
|
||||
},
|
||||
"opnsenseApiSecret": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "API Key (Secret)",
|
||||
"newLabel": "New API Key (Secret)"
|
||||
},
|
||||
"githubAppId": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "App Id",
|
||||
"newLabel": "New App Id"
|
||||
},
|
||||
"githubInstallationId": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "Installation Id",
|
||||
"newLabel": "New Installation Id"
|
||||
},
|
||||
"privateKey": {
|
||||
"label": "",
|
||||
"newLabel": ""
|
||||
"label": "Private key",
|
||||
"newLabel": "New private key"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1044,14 +1044,14 @@
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"success": "",
|
||||
"success": "Success",
|
||||
"beta": "Beta",
|
||||
"error": "Alpha",
|
||||
"action": {
|
||||
"add": "Add",
|
||||
"apply": "Apply",
|
||||
"backToOverview": "Back to overview",
|
||||
"change": "",
|
||||
"change": "Change",
|
||||
"create": "Create",
|
||||
"createAnother": "Create and start over",
|
||||
"edit": "Edit",
|
||||
@@ -1063,7 +1063,7 @@
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"discard": "Discard",
|
||||
"close": "",
|
||||
"close": "Close",
|
||||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"previous": "Previous",
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Danger zone",
|
||||
"noResults": "No results have been found",
|
||||
"unsavedChanges": "You have unsaved changes!",
|
||||
"servers": "Servers",
|
||||
"applications": "Apps",
|
||||
"services": "Services",
|
||||
"preview": {
|
||||
"show": "Show a preview",
|
||||
"hide": "Hide the preview"
|
||||
@@ -1174,8 +1177,8 @@
|
||||
},
|
||||
"unit": {
|
||||
"speed": {
|
||||
"kilometersPerHour": "",
|
||||
"milesPerHour": ""
|
||||
"kilometersPerHour": "km/h",
|
||||
"milesPerHour": "mph"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1190,7 +1193,7 @@
|
||||
"label": "Title"
|
||||
},
|
||||
"customCssClasses": {
|
||||
"label": ""
|
||||
"label": "Custom CSS classes"
|
||||
},
|
||||
"borderColor": {
|
||||
"label": "Border colour"
|
||||
@@ -1223,69 +1226,69 @@
|
||||
},
|
||||
"remove": {
|
||||
"title": "Remove category",
|
||||
"message": ""
|
||||
"message": "Are you sure you want to remove the category {name}?"
|
||||
},
|
||||
"edit": {
|
||||
"title": "",
|
||||
"submit": ""
|
||||
"title": "Rename category",
|
||||
"submit": "Rename category"
|
||||
},
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": ""
|
||||
"create": "New category",
|
||||
"changePosition": "Change position"
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
"title": "",
|
||||
"text": ""
|
||||
"title": "Open all in tabs",
|
||||
"text": "Some browsers may block the bulk-opening of tabs for security reasons. Homarr was unable to open all windows, because your browser blocked this action. Please allow \"Open pop-up windows\" and re-try."
|
||||
}
|
||||
}
|
||||
},
|
||||
"item": {
|
||||
"action": {
|
||||
"create": "",
|
||||
"import": "",
|
||||
"edit": "",
|
||||
"moveResize": "",
|
||||
"duplicate": "",
|
||||
"remove": ""
|
||||
"create": "New item",
|
||||
"import": "Import item",
|
||||
"edit": "Edit item",
|
||||
"moveResize": "Move / resize item",
|
||||
"duplicate": "Duplicate item",
|
||||
"remove": "Remove item"
|
||||
},
|
||||
"menu": {
|
||||
"label": {
|
||||
"settings": ""
|
||||
"settings": "Settings"
|
||||
}
|
||||
},
|
||||
"create": {
|
||||
"title": "",
|
||||
"search": "",
|
||||
"addToBoard": ""
|
||||
"title": "Choose item to add",
|
||||
"search": "Filter items",
|
||||
"addToBoard": "Add to board"
|
||||
},
|
||||
"moveResize": {
|
||||
"title": "",
|
||||
"title": "Move / resize item",
|
||||
"field": {
|
||||
"width": {
|
||||
"label": ""
|
||||
"label": "Width"
|
||||
},
|
||||
"height": {
|
||||
"label": ""
|
||||
"label": "Height"
|
||||
},
|
||||
"xOffset": {
|
||||
"label": ""
|
||||
"label": "X offset"
|
||||
},
|
||||
"yOffset": {
|
||||
"label": ""
|
||||
"label": "Y offset"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"title": "",
|
||||
"title": "Edit item",
|
||||
"advancedOptions": {
|
||||
"label": "",
|
||||
"title": ""
|
||||
"label": "Advanced options",
|
||||
"title": "Advanced item options"
|
||||
},
|
||||
"field": {
|
||||
"integrations": {
|
||||
"label": ""
|
||||
"label": "Integrations"
|
||||
},
|
||||
"title": {
|
||||
"label": ""
|
||||
@@ -1424,46 +1427,46 @@
|
||||
},
|
||||
"dnsHoleControls": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"description": "Control PiHole or AdGuard from your dashboard",
|
||||
"option": {
|
||||
"layout": {
|
||||
"label": "",
|
||||
"label": "Layout",
|
||||
"option": {
|
||||
"row": {
|
||||
"label": ""
|
||||
"label": "Horizontal"
|
||||
},
|
||||
"column": {
|
||||
"label": ""
|
||||
"label": "Vertical"
|
||||
},
|
||||
"grid": {
|
||||
"label": ""
|
||||
"label": "Grid"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showToggleAllButtons": {
|
||||
"label": ""
|
||||
"label": "Show Toggle All Buttons"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"internalServerError": ""
|
||||
"internalServerError": "Failed to control DNS Hole"
|
||||
},
|
||||
"controls": {
|
||||
"enableAll": "",
|
||||
"disableAll": "",
|
||||
"setTimer": "",
|
||||
"set": "",
|
||||
"enabled": "",
|
||||
"disabled": "",
|
||||
"processing": "",
|
||||
"disconnected": "",
|
||||
"hours": "",
|
||||
"minutes": "",
|
||||
"unlimited": ""
|
||||
"enableAll": "Enable All",
|
||||
"disableAll": "Disable All",
|
||||
"setTimer": "Set Timer",
|
||||
"set": "Set",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"processing": "Processing",
|
||||
"disconnected": "Disconnected",
|
||||
"hours": "Hours",
|
||||
"minutes": "Minutes",
|
||||
"unlimited": "Leave blank to unlimited"
|
||||
}
|
||||
},
|
||||
"clock": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"name": "Date and time",
|
||||
"description": "Displays the current date and time.",
|
||||
"option": {
|
||||
"customTitleToggle": {
|
||||
"label": "",
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
@@ -4403,12 +4475,12 @@
|
||||
"message": "",
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Search engine deleted",
|
||||
"message": "The search engine was deleted successfully"
|
||||
},
|
||||
"error": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Search engine not deleted",
|
||||
"message": "The search engine could not be deleted"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4416,15 +4488,15 @@
|
||||
"media": {
|
||||
"request": {
|
||||
"modal": {
|
||||
"title": "",
|
||||
"title": "Request \"{name}\"",
|
||||
"table": {
|
||||
"header": {
|
||||
"season": "",
|
||||
"episodes": ""
|
||||
"season": "Season",
|
||||
"episodes": "Episodes"
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
"send": ""
|
||||
"send": "Send request"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4434,44 +4506,44 @@
|
||||
"certificate": {
|
||||
"field": {
|
||||
"hostname": {
|
||||
"label": ""
|
||||
"label": "Hostname"
|
||||
},
|
||||
"subject": {
|
||||
"label": ""
|
||||
"label": "Subject"
|
||||
},
|
||||
"issuer": {
|
||||
"label": ""
|
||||
"label": "Issuer"
|
||||
},
|
||||
"validFrom": {
|
||||
"label": ""
|
||||
"label": "Valid from"
|
||||
},
|
||||
"validTo": {
|
||||
"label": ""
|
||||
"label": "Valid to"
|
||||
},
|
||||
"serialNumber": {
|
||||
"label": ""
|
||||
"label": "Serial number"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": ""
|
||||
"label": "Fingerprint"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"list": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "Trusted certificates",
|
||||
"description": "Used by Homarr to request data from integrations.",
|
||||
"noResults": {
|
||||
"title": ""
|
||||
"title": "There are no certificates yet"
|
||||
},
|
||||
"invalid": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
"title": "Invalid certificate",
|
||||
"description": "Failed to parse certificate"
|
||||
},
|
||||
"expires": "",
|
||||
"toHostnames": ""
|
||||
"expires": "Expires {when}",
|
||||
"toHostnames": "Trusted hostnames"
|
||||
},
|
||||
"hostnames": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "Trusted certificate hostnames",
|
||||
"description": "Some certificates do not allow the specific domain Homarr uses to request them, because of this all trusted hostnames with their certificate thumbprints are used to bypass these restrictions.",
|
||||
"noResults": {
|
||||
"title": "There are no hostnames yet"
|
||||
},
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Danger zone",
|
||||
"noResults": "No results found",
|
||||
"unsavedChanges": "You have unsaved changes!",
|
||||
"servers": "Servers",
|
||||
"applications": "Apps",
|
||||
"services": "Services",
|
||||
"preview": {
|
||||
"show": "Show preview",
|
||||
"hide": "Hide preview"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "Failed to fetch containers stats"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "Coolify",
|
||||
"description": "Overview of your Coolify instance with servers and applications",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": "Show servers"
|
||||
},
|
||||
"showApplications": {
|
||||
"label": "Show applications"
|
||||
},
|
||||
"showServices": {
|
||||
"label": "Show services"
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "No servers found",
|
||||
"applications": "No applications found",
|
||||
"services": "No services found"
|
||||
},
|
||||
"footer": {
|
||||
"updated": "Updated {when}"
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "No Coolify integration configured",
|
||||
"internalServerError": "Failed to fetch Coolify data"
|
||||
},
|
||||
"server": {
|
||||
"buildServer": "Build"
|
||||
},
|
||||
"instances": {
|
||||
"title": "Instances"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "City / Postal code",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "UP",
|
||||
"down": "DOWN"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "System disks",
|
||||
"description": "Disk usage of your system",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": "Show temperature if available"
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "Display mode",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": "Percentage (e.g., 76%)"
|
||||
},
|
||||
"absolute": {
|
||||
"label": "Absolute values (e.g., 800GB / 1TB)"
|
||||
},
|
||||
"free": {
|
||||
"label": "Free space percentage (e.g., 24% free)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": "Show background usage bar"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": "Unhealthy"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3240,7 +3305,7 @@
|
||||
"title": "Analytics",
|
||||
"general": {
|
||||
"title": "Send anonymous analytics",
|
||||
"text": "Homarr will send anonymized analytics using the open source software Umami. It never collects any personal information and is therefore fully GDPR & CCPA compliant. We encourage you to enable analytics because it helps our open source team to identify issues and prioritize our backlog."
|
||||
"text": "Homarr will send anonymized analytics using the open source software Umami. When enabled, we collect: Homarr version, database type (MySQL/PostgreSQL/SQLite), Docker/Kubernetes status, authentication methods enabled, and the number of each feature. All data is anonymous and aggregated - no personal information, URLs, names, or identifiable data is ever collected. This helps our open source team understand usage patterns and prioritize development."
|
||||
},
|
||||
"widgetData": {
|
||||
"title": "Widget data",
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Zona de riesgo",
|
||||
"noResults": "No se han encontrado resultados",
|
||||
"unsavedChanges": "¡Tienes cambios sin guardar!",
|
||||
"servers": "Servidores",
|
||||
"applications": "Aplicaciones",
|
||||
"services": "Servicios",
|
||||
"preview": {
|
||||
"show": "Mostrar vista previa",
|
||||
"hide": "Ocultar vista previa"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "No se pudo obtener las estadísticas de los contenedores"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "Coolify",
|
||||
"description": "Resumen de su instancia de Coolify con servidores y aplicaciones",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": "Mostrar servidores"
|
||||
},
|
||||
"showApplications": {
|
||||
"label": "Mostrar aplicaciones"
|
||||
},
|
||||
"showServices": {
|
||||
"label": "Mostrar servicios"
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "No se encontraron servidores",
|
||||
"applications": "No se encontraron aplicaciones",
|
||||
"services": "No se encontraron servicios"
|
||||
},
|
||||
"footer": {
|
||||
"updated": "Actualizado {when}"
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "No hay integración de Coolify configurada",
|
||||
"internalServerError": "No se pudieron obtener los datos de Coolify"
|
||||
},
|
||||
"server": {
|
||||
"buildServer": "Construir"
|
||||
},
|
||||
"instances": {
|
||||
"title": "Instancias"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "Ciudad / Código postal",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "ENCENDIDO",
|
||||
"down": "APAGADO"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "Discos del sistema",
|
||||
"description": "Uso del disco de su sistema",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": "Mostrar la temperatura si está disponible"
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "Modo de visualización",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": "Porcentaje (e.g., 76 %)"
|
||||
},
|
||||
"absolute": {
|
||||
"label": "Valores absolutos (e.g., 800 GB / 1 TB)"
|
||||
},
|
||||
"free": {
|
||||
"label": "Porcentaje de espacio libre (e.g., 24 % libre)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": "Mostrar la barra de uso en segundo plano"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": "Poco sano"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Usuarios",
|
||||
"enableGravatar": {
|
||||
"label": "Habilitar Gravatar",
|
||||
"description": "Recupera los avatares de usuario de Libravatar/Gravatar cuando no hay ningún avatar personalizado y se configura un correo electrónico"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Buscar",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "",
|
||||
"noResults": "",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"init": {
|
||||
"step": {
|
||||
"start": {
|
||||
"title": "Bienvenue dans Homarr",
|
||||
"title": "Bienvenue sur Homarr",
|
||||
"subtitle": "Commençons par configurer votre instance Homarr.",
|
||||
"description": "Pour commencer, veuillez selectionner comment vous souhaitez configurer votre instance Homarr.",
|
||||
"action": {
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Zone de danger",
|
||||
"noResults": "Aucun résultat trouvé",
|
||||
"unsavedChanges": "Vous avez des modifications non enregistrées !",
|
||||
"servers": "Serveurs",
|
||||
"applications": "Applications",
|
||||
"services": "Services",
|
||||
"preview": {
|
||||
"show": "Afficher l’aperçu",
|
||||
"hide": "Masquer l'aperçu"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "Impossible de récupérer les statistiques des conteneurs"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "Coolify",
|
||||
"description": "Aperçu de votre instance Coolify avec les serveurs et les applications",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": "Afficher les serveurs"
|
||||
},
|
||||
"showApplications": {
|
||||
"label": "Afficher les applications"
|
||||
},
|
||||
"showServices": {
|
||||
"label": "Afficher les services"
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "Aucun serveur trouvé",
|
||||
"applications": "Aucune application trouvée",
|
||||
"services": "Aucun service trouvé"
|
||||
},
|
||||
"footer": {
|
||||
"updated": "Mis à jour {when}"
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "Aucune intégration de Coolify configurée",
|
||||
"internalServerError": "Impossible de récupérer les données de Coolify"
|
||||
},
|
||||
"server": {
|
||||
"buildServer": "Version"
|
||||
},
|
||||
"instances": {
|
||||
"title": "Instances"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "Ville / Code Postal",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "UP",
|
||||
"down": "HORS SERVICE"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "Disques système",
|
||||
"description": "Utilisation du disque de votre système",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": "Afficher la température si disponible"
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "Mode d'affichage",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": "Pourcentage (ex : 70%)"
|
||||
},
|
||||
"absolute": {
|
||||
"label": "Valeurs absolues (ex. : 800 Go / 1 To)"
|
||||
},
|
||||
"free": {
|
||||
"label": "Pourcentage d'espace libre (ex. : 24 % libre)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": "Afficher la barre d'utilisation en arrière-plan"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": "Dysfonctionnement"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Utilisateurs",
|
||||
"enableGravatar": {
|
||||
"label": "Activer Gravatar",
|
||||
"description": "Revient aux avatars des utilisateurs de Libravatar/Gravatar quand aucun avatar personnalisé n'est défini et qu'un e-mail est configuré"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Rechercher",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "אזור מסוכן",
|
||||
"noResults": "לא נמצאו תוצאות",
|
||||
"unsavedChanges": "ישנם שינויים שלא נשמרו!",
|
||||
"servers": "שרתים",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "הצג תצוגה מקדימה",
|
||||
"hide": "הסתר תצוגה המקדימה"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "נכשל באחזור סטטיסטיקות המכולות"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "עיר / מיקוד",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "העלאה",
|
||||
"down": "הורדה"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "משתמשים",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "חיפוש",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Opasna zona",
|
||||
"noResults": "Nije pronađen nijedan rezultat",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Veszélyzóna",
|
||||
"noResults": "Nincs eredmény",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Zona pericolosa",
|
||||
"noResults": "Nessun risultato trovato",
|
||||
"unsavedChanges": "Ci sono modifiche non salvate!",
|
||||
"servers": "Server",
|
||||
"applications": "App",
|
||||
"services": "Servizi",
|
||||
"preview": {
|
||||
"show": "Mostra anteprima",
|
||||
"hide": "Nascondi anteprima"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "Recupero statistiche container non riuscito"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "Coolify",
|
||||
"description": "Panoramica della tua istanza Coolify con server e applicazioni",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": "Mostra server"
|
||||
},
|
||||
"showApplications": {
|
||||
"label": "Mostra applicazioni"
|
||||
},
|
||||
"showServices": {
|
||||
"label": "Mostra servizi"
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "Nessun server trovato",
|
||||
"applications": "Nessuna applicazione trovata",
|
||||
"services": "Nessun servizio trovato"
|
||||
},
|
||||
"footer": {
|
||||
"updated": "Aggiornato {when}"
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "Nessuna integrazione di Coolify configurata",
|
||||
"internalServerError": "Impossibile recuperare i dati Coolify"
|
||||
},
|
||||
"server": {
|
||||
"buildServer": "Build"
|
||||
},
|
||||
"instances": {
|
||||
"title": "Istanze"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "Città / Codice postale",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "SU",
|
||||
"down": "DOWN"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "Dischi di sistema",
|
||||
"description": "Uso del disco del sistema",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": "Mostra temperatura se disponibile"
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "Modalità di visualizzazione",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": "Percentuale (es. 76%)"
|
||||
},
|
||||
"absolute": {
|
||||
"label": "Valori assoluti (ad es. 800GB / 1TB)"
|
||||
},
|
||||
"free": {
|
||||
"label": "Percentuale di spazio libero (ad es. 24% libero)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": "Mostra barra di utilizzo in background"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": "Compromesso"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Utenti",
|
||||
"enableGravatar": {
|
||||
"label": "Abilita Gravatar",
|
||||
"description": "Torna agli avatar degli utenti da Libravatar/Gravatar quando non è impostato alcun avatar personalizzato e viene configurata un'email"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Cerca",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "危険な操作",
|
||||
"noResults": "結果が見つかりません",
|
||||
"unsavedChanges": "保存していない変更があります!",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "プレビューを表示",
|
||||
"hide": "プレビューを閉じる"
|
||||
@@ -1941,25 +1944,58 @@
|
||||
"description": "あなたのコンテナの統計(このウィジェットは管理者権限でのみ追加できます)",
|
||||
"option": {
|
||||
"enableRowSorting": {
|
||||
"label": ""
|
||||
"label": "アイテムの並び替えを有効にする"
|
||||
},
|
||||
"defaultSort": {
|
||||
"label": "",
|
||||
"label": "デフォルトで並び替えに使用される列",
|
||||
"option": {
|
||||
"name": "",
|
||||
"state": "",
|
||||
"cpuUsage": "",
|
||||
"memoryUsage": ""
|
||||
"name": "名称",
|
||||
"state": "状態",
|
||||
"cpuUsage": "CPU 使用率",
|
||||
"memoryUsage": "メモリ使用量"
|
||||
}
|
||||
},
|
||||
"descendingDefaultSort": {
|
||||
"label": ""
|
||||
"label": "並び替えを反転"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"internalServerError": "コンテナステータスの取得に失敗しました"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "都市/郵便番号",
|
||||
@@ -2248,7 +2284,7 @@
|
||||
"unknown": "不明",
|
||||
"pending": "保留中",
|
||||
"processing": "処理中",
|
||||
"requested": "",
|
||||
"requested": "要求済み",
|
||||
"partiallyAvailable": "一部",
|
||||
"available": "利用可能",
|
||||
"blacklisted": "ブラックリスト",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "UP",
|
||||
"down": "DOWN"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "ユーザー",
|
||||
"enableGravatar": {
|
||||
"label": "Gravatar を有効化",
|
||||
"description": "カスタムアバターが設定されておらず、メールが設定されている場合は、Libavatar/Gravatarからユーザーのアバターにフォールバックします"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "検索",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "위험한 설정",
|
||||
"noResults": "결과를 찾을 수 없습니다.",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "",
|
||||
"noResults": "Rezultatų nerasta",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Bīstamā zona",
|
||||
"noResults": "Nav atrasts neviens rezultāts",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Gevarenzone",
|
||||
"noResults": "Geen resultaten gevonden",
|
||||
"unsavedChanges": "Er zijn niet-opgeslagen wijzigingen!",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "Voorbeeld weergeven",
|
||||
"hide": "Voorbeeld verbergen"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "Stad / postcode",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "OMHOOG",
|
||||
"down": "OMLAAG"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Zoeken",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Faresonen",
|
||||
"noResults": "Ingen resultater funnet",
|
||||
"unsavedChanges": "Du har ulagrede endringer!",
|
||||
"servers": "Servere",
|
||||
"applications": "Apper",
|
||||
"services": "Tjenester",
|
||||
"preview": {
|
||||
"show": "Vis forhåndsvisning",
|
||||
"hide": "Skjul forhåndsvisning"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "Kunne ikke hente containerstatistikk"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "Coolify",
|
||||
"description": "Oversikt over din Coolify-instans med servere og apper",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": "Vis servere"
|
||||
},
|
||||
"showApplications": {
|
||||
"label": "Vis applikasjoner"
|
||||
},
|
||||
"showServices": {
|
||||
"label": "Vis tjenester"
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "Ingen server funnet",
|
||||
"applications": "Ingen applikasjoner funnet",
|
||||
"services": "Ingen tjenester funnet"
|
||||
},
|
||||
"footer": {
|
||||
"updated": "Oppdatert {when}"
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "Ingen Coolify integrasjon konfigurert",
|
||||
"internalServerError": "Kunne ikke hente Coolify data"
|
||||
},
|
||||
"server": {
|
||||
"buildServer": "Bygg"
|
||||
},
|
||||
"instances": {
|
||||
"title": "Instanser"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "By / Postnummer",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "OPP",
|
||||
"down": "NED"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "Systemdisker",
|
||||
"description": "Disk bruk av systemet",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": "Vis temperatur hvis tilgjengelig"
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "Visningsmodus",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": "Prosentandel (f.eks. 76%)"
|
||||
},
|
||||
"absolute": {
|
||||
"label": "Absolutte verdier (f.eks. 800GB / 1TB)"
|
||||
},
|
||||
"free": {
|
||||
"label": "Prosentandel ledig plass (f.eks. 24% ledig)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": "Vis bakgrunnsbrukslinje"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": "Usunn"
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3240,7 +3305,7 @@
|
||||
"title": "Statistikk",
|
||||
"general": {
|
||||
"title": "Send anonym brukerdata",
|
||||
"text": "Homarr vil sende anonymt brukerdata med åpen kildekode programvaren Umami. Det vil aldri samle inn personlig informasjon og følger derfor GDPR & CCPA fullstendig. Vi oppfordrer deg til å aktivere sending av brukerdata da det hjelper teamet vårt å identifisere problemer og sette prioritering."
|
||||
"text": "Homarr vil sende anonymiserte analyser ved bruk av den åpne programvaren Umami. Når dette er aktivert, samler vi inn: Homarr-versjon, databastype (MySQL/PostgreSQL/SQLite), Docker/Kubernetes-status, aktiverte autentiseringsmetoder, og antallet av hver funksjon. Alle data er anonyme og aggregerte – ingen personlig informasjon, URL-er, navn eller identifiserbare data samles inn. Dette hjelper vårt open source-team med å forstå bruks‑mønstre og prioritere utvikling."
|
||||
},
|
||||
"widgetData": {
|
||||
"title": "Widget data",
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Brukere",
|
||||
"enableGravatar": {
|
||||
"label": "Aktiver Gravatar",
|
||||
"description": "Bruker avatarer fra Libravatar/Gravatar som reserve når ingen egendefinert avatar er angitt og en e-post er konfigurert"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Søk",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Strefa zagrożenia",
|
||||
"noResults": "Nie znaleziono żadnych wyników",
|
||||
"unsavedChanges": "Masz niezapisane zmiany!",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "Pokaż podgląd",
|
||||
"hide": "Ukryj podgląd"
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": "Nie udało się pobrać statystyk kontenerów"
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "Miasto / kod pocztowy",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "Aktywny",
|
||||
"down": "Nieaktywny"
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "Szukaj",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
+1475
-1403
File diff suppressed because it is too large.
Load diff
@@ -1138,6 +1138,9 @@
|
||||
"dangerZone": "Zonă periculoasă",
|
||||
"noResults": "Nici un rezultat găsit",
|
||||
"unsavedChanges": "",
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": "",
|
||||
"preview": {
|
||||
"show": "",
|
||||
"hide": ""
|
||||
@@ -1960,6 +1963,39 @@
|
||||
"internalServerError": ""
|
||||
}
|
||||
},
|
||||
"coolify": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showServers": {
|
||||
"label": ""
|
||||
},
|
||||
"showApplications": {
|
||||
"label": ""
|
||||
},
|
||||
"showServices": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"servers": "",
|
||||
"applications": "",
|
||||
"services": ""
|
||||
},
|
||||
"footer": {
|
||||
"updated": ""
|
||||
},
|
||||
"error": {
|
||||
"noIntegration": "",
|
||||
"internalServerError": ""
|
||||
},
|
||||
"server": {
|
||||
"buildServer": ""
|
||||
},
|
||||
"instances": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"location": {
|
||||
"query": "",
|
||||
@@ -2608,6 +2644,35 @@
|
||||
"up": "",
|
||||
"down": ""
|
||||
}
|
||||
},
|
||||
"systemDisks": {
|
||||
"name": "",
|
||||
"description": "",
|
||||
"option": {
|
||||
"showTemperatureIfAvailable": {
|
||||
"label": ""
|
||||
},
|
||||
"displayMode": {
|
||||
"label": "",
|
||||
"option": {
|
||||
"percentage": {
|
||||
"label": ""
|
||||
},
|
||||
"absolute": {
|
||||
"label": ""
|
||||
},
|
||||
"free": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"showBackgroundBar": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"unhealthy": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"widgetPreview": {
|
||||
@@ -3294,6 +3359,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "",
|
||||
"enableGravatar": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"defaultSearchEngine": {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user