Compare commits

...
Author SHA1 Message Date
Releases Homarr a5b6c120dd chore(release): 1.58.1 [skip ci] 2026-04-04 09:53:34 +00:00
homarr-releases[bot] 9e7d5ad629 chore(release): automatic release v1.58.1 2026-04-04 09:52:41 +00:00
Meier Lukas a86cdb9fb9 fix: arabic text in english translations 2026-04-04 11:50:19 +02:00
homarr-update-contributors[bot] 270fce6a0e chore: update integrations list readme (#5404)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-04 09:46:50 +00:00
homarr-crowdin[bot] 2680255f44 chore(lang): updated translations from crowdin (#5401)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-04-04 11:45:23 +02:00
Meier Lukas ddf87d278e chore: remove temporary ts-expect-error for search-ch documentation link 2026-04-04 11:41:25 +02:00
homarr-releases[bot] 822d2c194b chore: update bug report template (#5399)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-04-03 21:50:53 +02:00
Releases Homarr f5f7f3e433 chore(release): 1.58.0 [skip ci] 2026-04-03 19:38:14 +00:00
homarr-releases[bot] b1b3b79cd9 chore(release): automatic release v1.58.0 2026-04-03 19:37:17 +00:00
homarr-update-contributors[bot] 35d40d0dea chore: update integrations list readme (#5397)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-03 18:28:41 +00:00
Meier Lukas abb2dfc92a feat(integrations): add search.ch integration with timetable widget (#5180) 2026-04-03 20:23:15 +02:00
homarr-renovate[bot] 279d830b79 chore(deps): update dependency conventional-changelog-conventionalcommits to ^9.3.1 (#5396)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-03 17:25:35 +00:00
homarr-renovate[bot] 72a93e681f fix(deps): update trpc monorepo to ^11.16.0 (#5392)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-03 16:48:45 +00:00
Meier Lukas c977b84e2d fix: node-ical prevents many trpc queries from loading (#5395) 2026-04-03 18:26:52 +02:00
homarr-update-contributors[bot] e92c407beb chore: update contributors (#5394)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-03 13:24:06 +00:00
homarr-crowdin[bot]andMeier Lukas 05af82bc02 chore(lang): updated translations from crowdin
* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

---------

Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-04-03 01:17:29 +00:00
homarr-crowdin[bot]andMeier Lukas 531bfd6fcf chore(lang): updated translations from crowdin
* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

---------

Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-04-02 18:53:29 +00:00
homarr-renovate[bot] fa40b6b647 fix(deps): update turbo monorepo to ^2.8.21 (#5391)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-02 18:50:09 +00:00
homarr-renovate[bot] c363cf8457 chore(deps): update dependency axios@&gt;&#x3d;1.0.0 &lt;1.8.2 to >=1.14.0 (#5389)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-01 19:09:32 +00:00
homarr-renovate[bot] 0a78e3f12a fix(deps): update dependency drizzle-orm to ^0.45.2 (#5388)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-01 19:02:54 +00:00
homarr-crowdin[bot]andMeier Lukas f380b383a3 chore(lang): updated translations from crowdin
* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

---------

Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-04-01 18:13:33 +02:00
homarr-renovate[bot] b30dbafbbb fix(deps): update tiptap monorepo to v3.21.0 (#5386)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-01 15:13:38 +00:00
homarr-renovate[bot] a9bcc9d04b fix(deps): update dependency @tabler/icons-react to ^3.41.0 (#5384)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-31 23:42:53 +00:00
homarr-renovate[bot] 451e68ab57 fix(deps): update trpc monorepo to ^11.15.1 (#5381)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-31 20:49:58 +00:00
Meier Lukas a7ed7a4592 ci(renovate): update prebuilt-debian lockfile automatically (#5379) 2026-03-31 22:11:08 +02:00
homarr-renovate[bot] 2dd3c906c4 fix(deps): update dependency @immich/sdk to ^2.6.3 (#5380)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-31 19:30:29 +00:00
Meier Lukas 6a200c39af fix(boards): unable to click on links at settings page (#5230) 2026-03-31 19:08:02 +02:00
homarr-renovate[bot] e88e3d17c0 fix(deps): update dependency @ctrl/deluge to v8 (#5367)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-31 18:52:38 +02:00
homarr-renovate[bot]andMeier Lukas 46db8b6ab3 fix(deps): update dependency better-sqlite3 to ^12.8.0 (#5266)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-03-31 18:43:59 +02:00
homarr-renovate[bot] 545dba0c8f chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=8.0.3 (#5377)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-31 16:07:34 +00:00
homarr-renovate[bot] cbb85159d6 chore(deps): update vitest monorepo to ^4.1.2 (#5378)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-31 16:06:32 +00:00
homarr-renovate[bot] ff07658212 chore(deps): update node.js (#5364)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-31 11:57:00 +00:00
homarr-renovate[bot] a9582f04ed fix(deps): update dependency zod to ^4.3.6 (#5376)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 21:50:50 +00:00
homarr-renovate[bot]andMeier Lukas 2b4c83dcca fix(deps): update dependency zod to ^4.3.5 - abandoned (#4803)
* fix(deps): update dependency zod to ^4.3.5

* fix: omit can not be used with refine

* fix: convert intersection to object due to trpc-openapi issue

* fix: remove omit after refine

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-03-30 23:11:14 +02:00
homarr-renovate[bot] 336c55b3e9 chore(deps): update docker/setup-buildx-action action to v4 (#5200)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 22:58:47 +02:00
homarr-renovate[bot] eb5dd3628a chore(deps): update docker/metadata-action action to v6 (#5207)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 22:58:37 +02:00
homarr-renovate[bot] c19766e53f chore(deps): update docker/build-push-action action to v7 (#5208)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 22:58:28 +02:00
homarr-renovate[bot] cc153fff10 chore(deps): update dependency jsdom to v29 (#5274)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 22:53:20 +02:00
homarr-renovate[bot] b71718be09 chore(deps): update dependency proxmox-api&gt;undici to v7.24.6 (#5373)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 20:52:12 +00:00
homarr-renovate[bot]andMeier Lukas b07c545f17 fix(deps): update dependency node-ical to ^0.23.1 - abandoned (#4856)
* fix(deps): update dependency node-ical to ^0.23.1

* fix: date from node-ical represents utc value

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-03-30 22:43:58 +02:00
homarr-renovate[bot] 4aff9819e5 chore(deps): update actions/create-github-app-token action to v3 (#5267)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 22:24:09 +02:00
homarr-renovate[bot] 9e0c3c0117 chore(deps): update pnpm to v10.33.0 (#5372)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 22:23:46 +02:00
homarr-crowdin[bot] 107ec7737a chore(lang): update translations from crowdin (#5350)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-03-30 22:23:03 +02:00
homarr-renovate[bot] d4177afc18 chore(deps): update pnpm/action-setup action to v5 (#5294)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 22:22:44 +02:00
Sascha BuehrleandMeier Lukas 7679553f16 fix: honor trusted certificates in Immich integration requests (#5275)
* fix: use trusted-cert fetch for immich sdk requests (fixes #5242)

* fix: use testing fetch for Immich connection check

* fix: typecheck issues

---------

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-03-30 22:21:11 +02:00
homarr-renovate[bot] 4f2cc9af2a fix(deps): update dependency undici to v7.24.6 (#5374)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-30 17:06:11 +00:00
homarr-renovate[bot] 6820f8afea fix(deps): update tiptap monorepo to v3.20.5 (#5371)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 16:43:28 +00:00
homarr-renovate[bot] 4a5bafa790 fix(deps): update dependency @immich/sdk to ^2.6.2 (#5370)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 14:44:39 +00:00
homarr-renovate[bot] 8d1ff4065d fix(deps): update dependency isomorphic-dompurify to ^3.7.1 (#5369)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 10:41:25 +00:00
homarr-renovate[bot] 14d87178b5 fix(deps): update dependency jotai to ^2.19.0 (#5368)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 09:45:23 +00:00
homarr-renovate[bot] c2b54ddfaa fix(deps): update dependency isomorphic-dompurify to ^3.7.0 (#5366)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 09:40:05 +00:00
homarr-renovate[bot] cefbf0e271 fix(deps): update dependency typescript-eslint to ^8.57.2 (#5360)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 07:13:39 +00:00
homarr-renovate[bot] 189806b5a2 fix(deps): update dependency fast-xml-parser to ^5.5.9 (#5359)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 07:12:11 +00:00
homarr-renovate[bot] 9c134c5ceb fix(deps): update dependency @ctrl/deluge to ^7.5.3 (#5365)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-29 05:17:45 +00:00
homarr-renovate[bot] 682099189b fix(deps): update tanstack-query monorepo to ^5.95.2 (#5357)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 16:41:43 +00:00
homarr-renovate[bot] 32218beb7e chore(deps): update vitest monorepo to ^4.1.1 (#5356)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 15:40:16 +00:00
homarr-renovate[bot] 332b6b4f42 fix(deps): update tanstack-query monorepo to ^5.95.1 (#5355)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 14:39:28 +00:00
homarr-renovate[bot] aca893254c chore(deps): update dependency brace-expansion@&gt;&#x3d;2.0.0 &lt;&#x3d;2.0.1 to >=5.0.5 [security] (#5341)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 14:34:27 +00:00
homarr-renovate[bot] 2fb2981d2f fix(deps): update trpc monorepo to ^11.15.0 (#5354)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 13:06:24 +00:00
homarr-renovate[bot] 5d84888a33 chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=8.0.2 (#5353)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 13:04:53 +00:00
homarr-renovate[bot] 728f7e489a fix(deps): update dependency fastify to ^5.8.4 (#5351)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 13:04:35 +00:00
homarr-renovate[bot] 0e3ac069d9 chore(deps): update dependency hono@&lt;4.6.5 to >=4.12.9 (#5352)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-28 11:35:24 +00:00
homarr-renovate[bot] 505a6b3a89 chore(deps): update dependency brace-expansion@&gt;&#x3d;1.0.0 &lt;&#x3d;1.1.11 to >=5.0.5 [security] (#5340)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 23:35:56 +00:00
homarr-releases[bot] 7c8adab061 chore: update bug report template (#5349)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-03-27 23:06:11 +01:00
homarr-renovate[bot] b11bf367d0 fix(deps): update tanstack-query monorepo to ^5.95.0 (#5325)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 21:43:25 +00:00
Releases Homarr 15c2037230 chore(release): 1.57.1 [skip ci] 2026-03-27 21:34:11 +00:00
homarr-releases[bot] 3735c998e4 chore(release): automatic release v1.57.1 2026-03-27 21:32:58 +00:00
Manuel 192a43a031 fix: dashdot gpu (#5347) 2026-03-27 22:32:13 +01:00
homarr-releases[bot] e9c908b317 chore: update bug report template (#5346)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-03-27 21:45:48 +01:00
homarr-renovate[bot] 49d9394cd7 fix(deps): update dependency @t3-oss/env-nextjs to ^0.13.11 (#5345)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 19:53:16 +00:00
homarr-renovate[bot] 798293813c fix(deps): update dependency fast-xml-parser to ^5.5.8 (#5324)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 19:52:59 +00:00
homarr-renovate[bot] 42a3791e6b fix(deps): update dependency dockerode to ^4.0.10 (#5327)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 19:52:19 +00:00
Releases Homarr 3d8e22574e chore(release): 1.57.0 [skip ci] 2026-03-27 19:41:40 +00:00
homarr-releases[bot] 531d87a83b chore(release): automatic release v1.57.0 2026-03-27 19:40:44 +00:00
homarr-crowdin[bot] 929a803965 chore(lang): update translations from crowdin (#5322)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-03-27 20:23:28 +01:00
Daniel 003dac4744 feat: add GPU monitoring support via Glances integration (#5255)
* feat: add GPU monitoring support via Glances integration

- Add GPU fields to SystemHealthMonitoring interface (gpuId, name,
  memoryUtilization, processorUtilization, temperature, fanSpeed)
- Extend Glances integration to parse GPU data from /api/4/all endpoint
  (supports NVIDIA, AMD, and Intel GPUs via Glances)
- Add GPU utilization ring to Health Monitoring widget with tooltip
  showing VRAM usage, temperature, and fan speed
- Add GPU time-series chart to System Resources widget (grape color)
- Add GPU toggle option to both widgets
- Add English translations for GPU labels
- Add gpu: [] to all other integrations (dashdot, truenas, unraid,
  openmediavault) for backward compatibility
- Add GPU mock data for development/testing

Closes community request from PR #5102 for GPU monitoring support.

* fix: implement GPU monitoring for dashdot integration and clarify OMV comment

- Add GPU load fetching via dashdot /load/gpu endpoint
- Add GPU info parsing from dashdot /info endpoint
- Add gpuLoadApi and gpu field to internalServerInfoApi schemas
- Add getCurrentGpuLoadAsync method to DashDotIntegration
- Map GPU load data to SystemHealthMonitoring gpu array
- Add explanatory comment for OMV's empty gpu array

Addresses review comments from manuel-rw on PR #5255

* Delete manifest.json
2026-03-27 20:22:41 +01:00
homarr-renovate[bot] d7f0596db8 fix(deps): update trpc monorepo to ^11.14.1 (#5328)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 19:13:22 +00:00
homarr-renovate[bot] dec490365a chore(deps): update dependency proxmox-api&gt;undici to v7.24.5 (#5321)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 19:12:08 +00:00
Matt Van HornandMatt Van Horn 1639da5779 fix: handle null ports in Docker container table (#5313)
When Docker API returns `null` for container ports (instead of `[]`),
the Docker management page crashes. Added optional chaining on
`ports?.length` to handle this case.

Fixes #5311

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
2026-03-27 20:04:03 +01:00
homarr-renovate[bot] d53c922688 fix(deps): update dependency ws to ^8.20.0 (#5337)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 18:09:40 +00:00
homarr-renovate[bot] 22f5d3b959 fix(deps): update dependency trpc-to-openapi to ^3.2.0 (#5320)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 18:08:15 +00:00
homarr-renovate[bot] 8d64cb8a5e fix(deps): update dependency isomorphic-dompurify to ^3.6.0 (#5334)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 18:07:40 +00:00
homarr-renovate[bot] bbd42600aa chore(deps): update dependency @types/pg to ^8.20.0 (#5331)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 18:05:32 +00:00
homarr-renovate[bot] 8859923bb7 chore(deps): update dependency rollup@&gt;&#x3d;4.0.0 &lt;4.22.4 to >=4.60.0 (#5332)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 18:04:25 +00:00
homarr-renovate[bot] e23a397a80 fix(deps): update nextjs monorepo to v16.2.1 (#5330)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 18:03:06 +00:00
f88bf9582e feat: add nextcloud notification provider (#5343)
* feat: add Nextcloud notification provider
fix: resolve hydration error in add-board modal

* Update packages/integrations/src/nextcloud/nextcloud.integration.ts

Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>

* Update nextcloud.integration.ts

* defined notificationSchema outside of class

---------

Co-authored-by: Lorinc936 <lorinc1231@gmail.com>
Co-authored-by: Lorinc936 <116025544+Lorinc936@users.noreply.github.com>
2026-03-27 18:51:43 +01:00
homarr-renovate[bot] 22bbef9d8e fix(deps): update dependency undici to v7.24.5 (#5338)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-27 18:22:06 +01:00
Manuel 4f2b8217f8 fix: warnings in code 2026-03-27 18:12:27 +01:00
Kelian Baert 2fefd9da01 feat: add "allowModals" option for iframe widget (#5339) 2026-03-27 18:05:15 +01:00
Manuel b3e9b3351b Merge commit from fork 2026-03-27 17:50:26 +01:00
141 changed files with 3841 additions and 1844 deletions

No files matched your search

+3
View File
@@ -33,6 +33,9 @@ body:
options:
# The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION#
- 1.58.0
- 1.57.1
- 1.57.0
- 1.56.1
- 1.56.0
- 1.55.0
+2 -2
View File
@@ -15,11 +15,11 @@ runs:
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Build docker image for ${{ inputs.architecture }}
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
push: false
load: true
+6
View File
@@ -31,6 +31,12 @@
automerge: false,
},
],
// Updates lockfile for prebuilt-debian after if package.json is updated, otherwise only root lockfile is updated
postUpgradeTasks: {
commands: ["cd deployments/prebuilt-debian && pnpm install --ignore-workspace --lockfile-only"],
fileFilters: ["deployments/prebuilt-debian/pnpm-lock.yaml"],
executionMode: "update",
},
updateInternalDeps: true,
rangeStrategy: "bump",
automerge: false,
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v6
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
+1 -1
View File
@@ -89,7 +89,7 @@ jobs:
uses: ./tooling/github/setup
- name: Build docker image
id: build-docker-image
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
platforms: linux/amd64
context: .
@@ -19,7 +19,7 @@ jobs:
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.CROWDIN_APP_PRIVATE_KEY }}
app-id: ${{ secrets.CROWDIN_APP_ID }}
+10 -10
View File
@@ -50,7 +50,7 @@ jobs:
- name: Obtain token
if: env.SKIP_RELEASE == 'false'
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
@@ -60,12 +60,12 @@ jobs:
if: env.SKIP_RELEASE == 'false'
with:
persist-credentials: false
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v5
if: env.SKIP_RELEASE == 'false'
- uses: actions/setup-node@v6
if: env.SKIP_RELEASE == 'false'
with:
node-version: 24.14.0
node-version: 24.14.1
cache: "pnpm"
- run: npm i -g pnpm
if: env.SKIP_RELEASE == 'false'
@@ -123,7 +123,7 @@ jobs:
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: "${{ env.GHCR_REPO }}"
@@ -135,11 +135,11 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
network: host
@@ -162,7 +162,7 @@ jobs:
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: "${{ env.GHCR_REPO }}"
@@ -174,11 +174,11 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
network: host
@@ -231,7 +231,7 @@ jobs:
- name: Obtain token
if: needs.release.outputs.skipped == 'false'
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
@@ -50,7 +50,7 @@ jobs:
args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
- name: Obtain token
id: obtainApprovalToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
app-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
@@ -62,7 +62,7 @@ jobs:
gh pr review --approve --body "Automatically approved by GitHub Action"
- name: Obtain token
id: obtainMergeToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
steps:
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_DOCS_RELEASE_APP_PRIVATE_KEY }}
app-id: ${{ vars.HOMARR_DOCS_RELEASE_APP_ID }}
@@ -45,7 +45,7 @@ jobs:
steps:
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.RENOVATE_MERGE_PRIVATE_KEY }}
app-id: ${{ secrets.RENOVATE_MERGE_APP_ID }}
+2 -2
View File
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
@@ -33,7 +33,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v5
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Obtain token
id: obtainToken
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
private-key: ${{ secrets.HOMARR_UPDATE_CONTRIBUTORS_PRIVATE_KEY }}
app-id: ${{ vars.HOMARR_UPDATE_CONTRIBUTORS_APP_ID }}
+1 -1
View File
@@ -1 +1 @@
24.14.0
24.14.1
+94
View File
@@ -1,3 +1,97 @@
## [1.58.1](https://github.com/homarr-labs/homarr/compare/v1.58.0...v1.58.1) (2026-04-04)
### Bug Fixes
* arabic text in english translations ([a86cdb9](https://github.com/homarr-labs/homarr/commit/a86cdb9fb91a10c8250f849e70c17777c18ada77))
## [1.58.0](https://github.com/homarr-labs/homarr/compare/v1.57.1...v1.58.0) (2026-04-03)
### Features
* **integrations:** add search.ch integration with timetable widget ([#5180](https://github.com/homarr-labs/homarr/issues/5180)) ([abb2dfc](https://github.com/homarr-labs/homarr/commit/abb2dfc92ab51b16e7172374cfee8b9c0c752454))
### Bug Fixes
* **boards:** unable to click on links at settings page ([#5230](https://github.com/homarr-labs/homarr/issues/5230)) ([6a200c3](https://github.com/homarr-labs/homarr/commit/6a200c39afaa1a9a1057643612065a40ad1cdd82))
* **deps:** update dependency @ctrl/deluge to ^7.5.3 ([#5365](https://github.com/homarr-labs/homarr/issues/5365)) ([9c134c5](https://github.com/homarr-labs/homarr/commit/9c134c5ceb742b21b021ef3b49de8cc0d526d460))
* **deps:** update dependency @ctrl/deluge to v8 ([#5367](https://github.com/homarr-labs/homarr/issues/5367)) ([e88e3d1](https://github.com/homarr-labs/homarr/commit/e88e3d17c01dab8836bec11d2ac01195f06cdc2e))
* **deps:** update dependency @immich/sdk to ^2.6.2 ([#5370](https://github.com/homarr-labs/homarr/issues/5370)) ([4a5bafa](https://github.com/homarr-labs/homarr/commit/4a5bafa79092b209e94a56f72362011f416bf115))
* **deps:** update dependency @immich/sdk to ^2.6.3 ([#5380](https://github.com/homarr-labs/homarr/issues/5380)) ([2dd3c90](https://github.com/homarr-labs/homarr/commit/2dd3c906c4215b55e23f4b15ba4b423e23be8376))
* **deps:** update dependency @tabler/icons-react to ^3.41.0 ([#5384](https://github.com/homarr-labs/homarr/issues/5384)) ([a9bcc9d](https://github.com/homarr-labs/homarr/commit/a9bcc9d04bdfcf28ea561cd590ca2a4bd775593c))
* **deps:** update dependency better-sqlite3 to ^12.8.0 ([#5266](https://github.com/homarr-labs/homarr/issues/5266)) ([46db8b6](https://github.com/homarr-labs/homarr/commit/46db8b6ab32406fb4ff7a7a9938692954778a070))
* **deps:** update dependency drizzle-orm to ^0.45.2 ([#5388](https://github.com/homarr-labs/homarr/issues/5388)) ([0a78e3f](https://github.com/homarr-labs/homarr/commit/0a78e3f12aa99509f42d7af647a478c5abaf5c96))
* **deps:** update dependency fast-xml-parser to ^5.5.9 ([#5359](https://github.com/homarr-labs/homarr/issues/5359)) ([189806b](https://github.com/homarr-labs/homarr/commit/189806b5a2b8e67f925cc8def6bc8ddefdc5bfc4))
* **deps:** update dependency fastify to ^5.8.4 ([#5351](https://github.com/homarr-labs/homarr/issues/5351)) ([728f7e4](https://github.com/homarr-labs/homarr/commit/728f7e489aa499bcd7e97c7ec8571e29e7e2cab3))
* **deps:** update dependency isomorphic-dompurify to ^3.7.0 ([#5366](https://github.com/homarr-labs/homarr/issues/5366)) ([c2b54dd](https://github.com/homarr-labs/homarr/commit/c2b54ddfaab93daa4e65c0c38bf5c27e4402a492))
* **deps:** update dependency isomorphic-dompurify to ^3.7.1 ([#5369](https://github.com/homarr-labs/homarr/issues/5369)) ([8d1ff40](https://github.com/homarr-labs/homarr/commit/8d1ff4065d669268f0e6a03f2b68c1ba728a756a))
* **deps:** update dependency jotai to ^2.19.0 ([#5368](https://github.com/homarr-labs/homarr/issues/5368)) ([14d8717](https://github.com/homarr-labs/homarr/commit/14d87178b5a93bdc6fb74793feed95d014814d0b))
* **deps:** update dependency node-ical to ^0.23.1 - abandoned ([#4856](https://github.com/homarr-labs/homarr/issues/4856)) ([b07c545](https://github.com/homarr-labs/homarr/commit/b07c545f17f4dafa62167047baa9eed4b7ee4395))
* **deps:** update dependency typescript-eslint to ^8.57.2 ([#5360](https://github.com/homarr-labs/homarr/issues/5360)) ([cefbf0e](https://github.com/homarr-labs/homarr/commit/cefbf0e2715e9adbc72cdb0b83d84c309e3257d6))
* **deps:** update dependency undici to v7.24.6 ([#5374](https://github.com/homarr-labs/homarr/issues/5374)) ([4f2cc9a](https://github.com/homarr-labs/homarr/commit/4f2cc9af2a42e19624def3bdea60fcb00334c483))
* **deps:** update dependency zod to ^4.3.5 - abandoned ([#4803](https://github.com/homarr-labs/homarr/issues/4803)) ([2b4c83d](https://github.com/homarr-labs/homarr/commit/2b4c83dccac168ffa6f9b6e184ed004659373cf7))
* **deps:** update dependency zod to ^4.3.6 ([#5376](https://github.com/homarr-labs/homarr/issues/5376)) ([a9582f0](https://github.com/homarr-labs/homarr/commit/a9582f04edb0e161d1fab6e64b13d0ce463ef341))
* **deps:** update tanstack-query monorepo to ^5.95.0 ([#5325](https://github.com/homarr-labs/homarr/issues/5325)) ([b11bf36](https://github.com/homarr-labs/homarr/commit/b11bf367d0567e4c8b9068804442ebd200dab01c))
* **deps:** update tanstack-query monorepo to ^5.95.1 ([#5355](https://github.com/homarr-labs/homarr/issues/5355)) ([332b6b4](https://github.com/homarr-labs/homarr/commit/332b6b4f42e7c90570cd4c25a6e6a331a76c660f))
* **deps:** update tanstack-query monorepo to ^5.95.2 ([#5357](https://github.com/homarr-labs/homarr/issues/5357)) ([6820991](https://github.com/homarr-labs/homarr/commit/682099189b689ea17e16af41fd280a72ed68016f))
* **deps:** update tiptap monorepo to v3.20.5 ([#5371](https://github.com/homarr-labs/homarr/issues/5371)) ([6820f8a](https://github.com/homarr-labs/homarr/commit/6820f8afea622587c1ee18b13d13a041ca0572ee))
* **deps:** update tiptap monorepo to v3.21.0 ([#5386](https://github.com/homarr-labs/homarr/issues/5386)) ([b30dbaf](https://github.com/homarr-labs/homarr/commit/b30dbafbbbad468ff37ff11237a64ec29df62345))
* **deps:** update trpc monorepo to ^11.15.0 ([#5354](https://github.com/homarr-labs/homarr/issues/5354)) ([2fb2981](https://github.com/homarr-labs/homarr/commit/2fb2981d2fb79ca2ca6a9bc5c08d91c8fa5710e7))
* **deps:** update trpc monorepo to ^11.15.1 ([#5381](https://github.com/homarr-labs/homarr/issues/5381)) ([451e68a](https://github.com/homarr-labs/homarr/commit/451e68ab57551e23dd7ae772b7483cc56d57ebbb))
* **deps:** update trpc monorepo to ^11.16.0 ([#5392](https://github.com/homarr-labs/homarr/issues/5392)) ([72a93e6](https://github.com/homarr-labs/homarr/commit/72a93e681f3701459176692857220730c279f80b))
* **deps:** update turbo monorepo to ^2.8.21 ([#5391](https://github.com/homarr-labs/homarr/issues/5391)) ([fa40b6b](https://github.com/homarr-labs/homarr/commit/fa40b6b647b884a7f10c268a22330e0e68b50d4d))
* honor trusted certificates in Immich integration requests ([#5275](https://github.com/homarr-labs/homarr/issues/5275)) ([7679553](https://github.com/homarr-labs/homarr/commit/7679553f16ff2945a3eb6cb668ca3a701d13d5ce)), closes [#5242](https://github.com/homarr-labs/homarr/issues/5242)
* node-ical prevents many trpc queries from loading ([#5395](https://github.com/homarr-labs/homarr/issues/5395)) ([c977b84](https://github.com/homarr-labs/homarr/commit/c977b84e2d84d5724219f5ff5f99bc550438e168))
## [1.57.1](https://github.com/homarr-labs/homarr/compare/v1.57.0...v1.57.1) (2026-03-27)
### Bug Fixes
* dashdot gpu ([#5347](https://github.com/homarr-labs/homarr/issues/5347)) ([192a43a](https://github.com/homarr-labs/homarr/commit/192a43a031eb9431da2c11395eda69a6c7236b9b))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.11 ([#5345](https://github.com/homarr-labs/homarr/issues/5345)) ([49d9394](https://github.com/homarr-labs/homarr/commit/49d9394cd78b2a1146c8dc00dd982215a2f9a580))
* **deps:** update dependency dockerode to ^4.0.10 ([#5327](https://github.com/homarr-labs/homarr/issues/5327)) ([42a3791](https://github.com/homarr-labs/homarr/commit/42a3791e6b5664a0f28d3ee4d29d5257ef3765f6))
* **deps:** update dependency fast-xml-parser to ^5.5.8 ([#5324](https://github.com/homarr-labs/homarr/issues/5324)) ([7982938](https://github.com/homarr-labs/homarr/commit/798293813cf82e38d7059069823f927b7cdfb3b8))
## [1.57.0](https://github.com/homarr-labs/homarr/compare/v1.56.1...v1.57.0) (2026-03-27)
### Features
* add "allowModals" option for iframe widget ([#5339](https://github.com/homarr-labs/homarr/issues/5339)) ([2fefd9d](https://github.com/homarr-labs/homarr/commit/2fefd9da01c827160fa5fd9b63a38615ea9969e2))
* add GPU monitoring support via Glances integration ([#5255](https://github.com/homarr-labs/homarr/issues/5255)) ([003dac4](https://github.com/homarr-labs/homarr/commit/003dac4744f2e23535f1cbb7494187201af4b164)), closes [#5102](https://github.com/homarr-labs/homarr/issues/5102)
* add nextcloud notification provider ([#5343](https://github.com/homarr-labs/homarr/issues/5343)) ([f88bf95](https://github.com/homarr-labs/homarr/commit/f88bf9582e1cf45a6b7fd898e12d227869a3849f))
### Bug Fixes
* **deps:** update dependency @immich/sdk to ^2.6.0 ([#5318](https://github.com/homarr-labs/homarr/issues/5318)) ([20a9b3d](https://github.com/homarr-labs/homarr/commit/20a9b3d556a15e6fdf7d1dbd78b677b5ce219c75))
* **deps:** update dependency @immich/sdk to ^2.6.1 ([#5319](https://github.com/homarr-labs/homarr/issues/5319)) ([feaf022](https://github.com/homarr-labs/homarr/commit/feaf022965cf4a27112c7e3bcb9ef3ad0dd5c281))
* **deps:** update dependency @next/eslint-plugin-next to v16.1.7 ([#5289](https://github.com/homarr-labs/homarr/issues/5289)) ([237d11d](https://github.com/homarr-labs/homarr/commit/237d11d2c021e28d58b0d627d2802d509d4c1b2b))
* **deps:** update dependency @tanstack/react-query to ^5.91.0 ([#5305](https://github.com/homarr-labs/homarr/issues/5305)) ([13b99d0](https://github.com/homarr-labs/homarr/commit/13b99d021b46fe0541ce9b42ba66756fafd26c30))
* **deps:** update dependency @tanstack/react-query to ^5.91.2 ([#5314](https://github.com/homarr-labs/homarr/issues/5314)) ([6fa6ba3](https://github.com/homarr-labs/homarr/commit/6fa6ba385e6691fefbb9f045a0ff976bcd85de16))
* **deps:** update dependency drizzle-kit to ^0.31.10 ([#5291](https://github.com/homarr-labs/homarr/issues/5291)) ([09227a3](https://github.com/homarr-labs/homarr/commit/09227a3884d291032f1012b59ce06babc3c87103))
* **deps:** update dependency fast-xml-parser to ^5.5.6 ([#5297](https://github.com/homarr-labs/homarr/issues/5297)) ([991044e](https://github.com/homarr-labs/homarr/commit/991044ebd78288bea7b441d3c14b159b5e66915b))
* **deps:** update dependency fast-xml-parser to ^5.5.7 ([#5315](https://github.com/homarr-labs/homarr/issues/5315)) ([2336f9e](https://github.com/homarr-labs/homarr/commit/2336f9ee5e35f7b610743dee55b91caf8434c1e4))
* **deps:** update dependency ioredis to v5.10.1 ([#5316](https://github.com/homarr-labs/homarr/issues/5316)) ([47b2d36](https://github.com/homarr-labs/homarr/commit/47b2d36f7a0933b6a6bbb61a449888be9304e1c5))
* **deps:** update dependency isomorphic-dompurify to ^3.4.0 ([#5292](https://github.com/homarr-labs/homarr/issues/5292)) ([6276449](https://github.com/homarr-labs/homarr/commit/62764499e7bbc9995ecf3de7351c9224d6aee4b6))
* **deps:** update dependency isomorphic-dompurify to ^3.5.0 ([#5299](https://github.com/homarr-labs/homarr/issues/5299)) ([b6b11c3](https://github.com/homarr-labs/homarr/commit/b6b11c39d2b4a33c5732ae239d7c1aaba6003cc6))
* **deps:** update dependency isomorphic-dompurify to ^3.5.1 ([#5300](https://github.com/homarr-labs/homarr/issues/5300)) ([94dfddf](https://github.com/homarr-labs/homarr/commit/94dfddf8bde2966b46bcd2d30e5e4f886ebe4605))
* **deps:** update dependency isomorphic-dompurify to ^3.6.0 ([#5334](https://github.com/homarr-labs/homarr/issues/5334)) ([8d64cb8](https://github.com/homarr-labs/homarr/commit/8d64cb8a5e276677b033774152c921aedc1e6c8a))
* **deps:** update dependency mysql2 to v3.20.0 ([#5283](https://github.com/homarr-labs/homarr/issues/5283)) ([4fb48c2](https://github.com/homarr-labs/homarr/commit/4fb48c23a2ccb1c047bab0d354933561e81cc3ed))
* **deps:** update dependency swagger-ui-react to ^5.32.1 ([#5296](https://github.com/homarr-labs/homarr/issues/5296)) ([63c55ce](https://github.com/homarr-labs/homarr/commit/63c55cec5711c41544c65ab83f24d09e63b10354))
* **deps:** update dependency trpc-to-openapi to ^3.2.0 ([#5320](https://github.com/homarr-labs/homarr/issues/5320)) ([22f5d3b](https://github.com/homarr-labs/homarr/commit/22f5d3b95959cf0a835c9750b41106eaafc4432e))
* **deps:** update dependency typescript-eslint to ^8.57.1 ([#5286](https://github.com/homarr-labs/homarr/issues/5286)) ([d4e4c96](https://github.com/homarr-labs/homarr/commit/d4e4c9669d648632a055b5ec0515821b50821142))
* **deps:** update dependency undici to v7.24.5 ([#5338](https://github.com/homarr-labs/homarr/issues/5338)) ([22bbef9](https://github.com/homarr-labs/homarr/commit/22bbef9d8e03215abddf6c6e26a5df45f252626d))
* **deps:** update dependency ws to ^8.20.0 ([#5337](https://github.com/homarr-labs/homarr/issues/5337)) ([d53c922](https://github.com/homarr-labs/homarr/commit/d53c922688606bd7d9494089e1ec43b930ad9923))
* **deps:** update mantine monorepo to ^8.3.18 ([#5298](https://github.com/homarr-labs/homarr/issues/5298)) ([6f8e629](https://github.com/homarr-labs/homarr/commit/6f8e62963a4eb47cb3095dff46ee0e759e1cbd3c))
* **deps:** update nextjs monorepo to v16.2.0 ([#5309](https://github.com/homarr-labs/homarr/issues/5309)) ([8831613](https://github.com/homarr-labs/homarr/commit/883161357dab4d94ff794f9444040d88074c464c))
* **deps:** update nextjs monorepo to v16.2.1 ([#5330](https://github.com/homarr-labs/homarr/issues/5330)) ([e23a397](https://github.com/homarr-labs/homarr/commit/e23a397a80e4409f5da50c737aeb26b295ce79fc))
* **deps:** update testcontainers-node monorepo to ^11.13.0 ([#5293](https://github.com/homarr-labs/homarr/issues/5293)) ([c009dd7](https://github.com/homarr-labs/homarr/commit/c009dd7de649bf24ab9bcf5ea25c65ebd18540bb))
* **deps:** update tiptap monorepo to v3.20.4 ([#5287](https://github.com/homarr-labs/homarr/issues/5287)) ([369b4d4](https://github.com/homarr-labs/homarr/commit/369b4d486327d0eaa04b70dd692c426f9a39afd7))
* **deps:** update trpc monorepo to ^11.14.1 ([#5328](https://github.com/homarr-labs/homarr/issues/5328)) ([d7f0596](https://github.com/homarr-labs/homarr/commit/d7f0596db8e44e2e79bb26095abd142b50675cc3))
* **deps:** update turbo monorepo to ^2.8.18 ([#5308](https://github.com/homarr-labs/homarr/issues/5308)) ([24694b9](https://github.com/homarr-labs/homarr/commit/24694b92411aae818b97a3a5c9add97d707a2f38))
* **deps:** update turbo monorepo to ^2.8.19 ([#5310](https://github.com/homarr-labs/homarr/issues/5310)) ([7537184](https://github.com/homarr-labs/homarr/commit/7537184a8168a4837d0a83a925a70c938555f199))
* **deps:** update turbo monorepo to ^2.8.20 ([#5317](https://github.com/homarr-labs/homarr/issues/5317)) ([5b4e6fc](https://github.com/homarr-labs/homarr/commit/5b4e6fcc11c877815c4730607de84ab70d630fb8))
* handle null ports in Docker container table ([#5313](https://github.com/homarr-labs/homarr/issues/5313)) ([1639da5](https://github.com/homarr-labs/homarr/commit/1639da57794d2555426e56030b4b34e2cc201aa9)), closes [#5311](https://github.com/homarr-labs/homarr/issues/5311)
* warnings in code ([4f2b821](https://github.com/homarr-labs/homarr/commit/4f2b8217f819b91ec4b05e32aebd6bc5473db155))
## [1.56.1](https://github.com/homarr-labs/homarr/compare/v1.56.0...v1.56.1) (2026-03-20)
### Bug Fixes
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:24.14.0-alpine AS base
FROM node:24.14.1-alpine AS base
FROM base AS builder
RUN apk add --no-cache libc6-compat
+12 -12
View File
@@ -55,14 +55,14 @@
"@mantine/modals": "^8.3.18",
"@mantine/tiptap": "^8.3.18",
"@million/lint": "1.0.14",
"@tabler/icons-react": "^3.40.0",
"@tanstack/react-query": "^5.91.2",
"@tanstack/react-query-devtools": "^5.91.3",
"@tanstack/react-query-next-experimental": "^5.91.0",
"@trpc/client": "^11.13.4",
"@trpc/next": "^11.13.4",
"@trpc/react-query": "^11.13.4",
"@trpc/server": "^11.13.4",
"@tabler/icons-react": "^3.41.0",
"@tanstack/react-query": "^5.95.2",
"@tanstack/react-query-devtools": "^5.95.2",
"@tanstack/react-query-next-experimental": "^5.95.2",
"@trpc/client": "^11.16.0",
"@trpc/next": "^11.16.0",
"@trpc/react-query": "^11.16.0",
"@trpc/server": "^11.16.0",
"@xterm/addon-canvas": "^0.7.0",
"@xterm/addon-fit": "0.11.0",
"@xterm/xterm": "^6.0.0",
@@ -73,10 +73,10 @@
"dotenv": "^17.3.1",
"flag-icons": "^7.5.0",
"glob": "^13.0.6",
"isomorphic-dompurify": "^3.5.1",
"jotai": "^2.18.1",
"isomorphic-dompurify": "^3.7.1",
"jotai": "^2.19.0",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.2.0",
"next": "16.2.1",
"postcss-preset-mantine": "^1.18.0",
"prismjs": "^1.30.0",
"react": "19.2.4",
@@ -87,7 +87,7 @@
"superjson": "2.2.6",
"swagger-ui-react": "^5.32.1",
"use-deep-compare-effect": "^1.8.1",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -13,6 +13,7 @@ import type { useForm } from "@homarr/form";
import { useZodForm } from "@homarr/form";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import { sanitizeRedirectionUrl } from "@homarr/validation/redirection-url";
import { userSignInSchema } from "@homarr/validation/user";
type Provider = "credentials" | "ldap" | "oidc";
@@ -70,7 +71,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c
// Redirect to the callback URL if the response is defined and comes from a credentials provider (ldap or credentials). oidc is redirected automatically.
await revalidatePathActionAsync("/");
router.push(callbackUrl);
router.push(sanitizeRedirectionUrl(callbackUrl));
},
[t, router, callbackUrl],
);
@@ -4,6 +4,7 @@ import { Card, Center, Stack, Text, Title } from "@mantine/core";
import { env } from "@homarr/auth/env";
import { auth } from "@homarr/auth/next";
import { getScopedI18n } from "@homarr/translation/server";
import { sanitizeRedirectionUrl } from "@homarr/validation/redirection-url";
import { HomarrLogoWithTitle } from "~/components/layout/logo/homarr-logo";
import { LoginForm } from "./_login-form";
@@ -19,7 +20,7 @@ export default async function Login(props: LoginProps) {
const session = await auth();
if (session) {
redirect(searchParams.callbackUrl ?? "/");
redirect(sanitizeRedirectionUrl(searchParams.callbackUrl));
}
const t = await getScopedI18n("user.page.login");
@@ -264,6 +264,5 @@ const usePreventLeaveWithDirty = (isDirty: boolean) => {
window.removeEventListener("popstate", handlePopState);
window.removeEventListener("beforeunload", handleBeforeUnload);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDirty]);
};
@@ -27,7 +27,6 @@ export const BoardReadyProvider = ({ children }: PropsWithChildren) => {
useEffect(() => {
setReadySections((previous) => previous.filter((id) => board.sections.some((section) => section.id === id)));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [board.sections.length, setReadySections]);
const markAsReady = useCallback((id: string) => {
@@ -2,7 +2,7 @@
import { useEffect, useRef } from "react";
import { Button, Grid, Group, Loader, Stack, TextInput } from "@mantine/core";
import { useDebouncedValue, useDocumentTitle, useFavicon } from "@mantine/hooks";
import { useDebouncedValue, useDocumentTitle } from "@mantine/hooks";
import { useUpdateBoard } from "@homarr/boards/updater";
import { useZodForm } from "@homarr/form";
@@ -10,6 +10,7 @@ import { IconPicker } from "@homarr/forms-collection";
import { useI18n } from "@homarr/translation/client";
import { boardSavePartialSettingsSchema } from "@homarr/validation/board";
import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
import { createMetaTitle } from "~/metadata";
import type { Board } from "../../_types";
import { useSavePartialSettingsMutation } from "./_shared";
@@ -53,7 +54,6 @@ export const GeneralSettingsContent = ({ board }: Props) => {
);
useLogoPreview(form.values.logoImageUrl);
useFaviconPreview(form.values.faviconImageUrl);
const metaTitleStatus = useMetaTitlePreview(form.values.metaTitle);
// Cleanup for not applied changes of the page title and logo image URL
@@ -75,6 +75,7 @@ export const GeneralSettingsContent = ({ board }: Props) => {
pageTitle: values.pageTitle,
logoImageUrl: values.logoImageUrl,
};
updateFavicon(values.faviconImageUrl ?? homarrLogoPath);
savePartialSettings({
id: board.id,
...values,
@@ -146,7 +147,13 @@ const useMetaTitlePreview = (title: string | null) => {
};
};
const useFaviconPreview = (url: string | null) => {
const [faviconDebounced] = useDebouncedValue(url ?? "", 500);
useFavicon(faviconDebounced);
// Previously we used the useFavicon hook from Mantine
// However if the user tried to click on a link to a different page
// it caused an error "Cannot read properties of null (reading 'removeChild')"
// Probably because the head element was null (https://github.com/mantinedev/mantine/blob/b90d9b81031f45f5d15e75f25138ed6477f65bce/packages/%40mantine/hooks/src/use-favicon/use-favicon.ts#L21)
// See https://github.com/homarr-labs/homarr/issues/4905
const updateFavicon = (url: string) => {
const link: HTMLLinkElement | null = document.querySelector('link[rel="icon"]');
if (!link) return;
link.href = url;
};
@@ -14,7 +14,7 @@ import { ApiKeysManagement } from "./components/api-keys";
export async function generateMetadata() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
if (!session?.user.permissions.includes("admin")) {
return {};
}
@@ -27,7 +27,7 @@ export async function generateMetadata() {
export default async function ApiPage() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
if (!session?.user.permissions.includes("admin")) {
notFound();
}
const document = openApiDocument(extractBaseUrlFromHeaders(await headers()));
@@ -76,6 +76,7 @@ const createColumns = (
accessorKey: "ports",
header: t("docker.field.ports.label"),
Cell({ cell }) {
if (!cell.row.original.ports) return null;
if (!cell.row.original.ports.length) return null;
return (
<OverflowBadge overflowCount={1} data={cell.row.original.ports.map((port) => port.PrivatePort.toString())} />
@@ -18,7 +18,7 @@ import { LogContextProvider } from "./log-context";
export async function generateMetadata() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("admin")) {
if (!session?.user.permissions.includes("admin")) {
return {};
}
const t = await getScopedI18n("management");
@@ -30,7 +30,7 @@ export async function generateMetadata() {
export default async function LogsManagementPage() {
const session = await auth();
if (!session?.user || !session.user.permissions.includes("other-view-logs")) {
if (!session?.user.permissions.includes("other-view-logs")) {
notFound();
}
@@ -75,7 +75,6 @@ const executeHealthCheckSafelyAsync = async (
},
};
} catch (error) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
logger.error(new ErrorWithMetadata("Healthcheck failed", { name }, { cause: error }));
return {
status: "unhealthy",
@@ -117,7 +117,6 @@ const InnerContent = ({ item, ...dimensions }: InnerContentProps) => {
when={
widgetSupportsIntegrations &&
item.integrationIds.length === 0 &&
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(!("integrationsRequired" in definition) || definition.integrationsRequired !== false)
}
/>
@@ -206,7 +206,6 @@ export const useGridstack = (section: Omit<Section, "items">, itemIds: string[])
}
// Only run this effect when the section items change
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [itemIds.length, columnCount]);
/**
+2 -2
View File
@@ -39,9 +39,9 @@
"@homarr/widgets": "workspace:^0.1.0",
"dayjs": "^1.11.20",
"dotenv": "^17.3.1",
"fastify": "^5.8.2",
"fastify": "^5.8.4",
"superjson": "2.2.6",
"undici": "7.18.2"
"undici": "7.24.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -27,7 +27,7 @@
"@homarr/validation": "workspace:^0.1.0",
"dotenv": "^17.3.1",
"tsx": "4.20.4",
"ws": "^8.19.0"
"ws": "^8.20.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:24.14.0-trixie AS base
FROM node:24.14.1-trixie AS base
WORKDIR /app
COPY package.json .
COPY pnpm-lock.yaml .
+4 -4
View File
@@ -2,12 +2,12 @@
"name": "homarr-prebuilt-debian",
"private": true,
"dependencies": {
"better-sqlite3": "^12.6.2"
"better-sqlite3": "^12.8.0"
},
"packageManager": "pnpm@10.32.1",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">=24.14.0",
"pnpm": ">=10.32.1"
"node": ">=24.14.1",
"pnpm": ">=10.33.0"
},
"pnpm": {
"onlyBuiltDependencies": [
+6 -5
View File
@@ -9,16 +9,16 @@ importers:
.:
dependencies:
better-sqlite3:
specifier: ^12.6.2
version: 12.6.2
specifier: ^12.8.0
version: 12.8.0
packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
better-sqlite3@12.6.2:
resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==}
better-sqlite3@12.8.0:
resolution: {integrity: sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==}
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
bindings@1.5.0:
@@ -93,6 +93,7 @@ packages:
prebuild-install@7.1.3:
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
engines: {node: '>=10'}
deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
hasBin: true
pump@3.0.3:
@@ -147,7 +148,7 @@ snapshots:
base64-js@1.5.1: {}
better-sqlite3@12.6.2:
better-sqlite3@12.8.0:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.3
+11 -4
View File
@@ -302,6 +302,13 @@
</a>
</td></tr>
<tr><td align="center">
<a href="https://homarr.dev/docs/integrations/search-ch" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/search-ch.png" alt="Search.ch" width="90" height="90" />
<br/>
<p align="center">Search.ch</p>
</a>
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/seerr" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/seerr.svg" alt="Seerr" width="90" height="90" />
<br/>
@@ -342,15 +349,15 @@
<br/>
<p align="center">Transmission</p>
</a>
</td>
<td align="center">
</td></tr>
<tr><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/>
<p align="center">TrueNAS</p>
</a>
</td></tr>
<tr><td align="center">
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/unifi-controller" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unifi.png" alt="Unifi Controller" width="90" height="90" />
<br/>
+18 -18
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.56.1",
"version": "1.58.1",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -43,26 +43,26 @@
"@semantic-release/npm": "^13.1.5",
"@semantic-release/release-notes-generator": "^14.1.0",
"@testcontainers/redis": "^11.13.0",
"@turbo/gen": "^2.8.20",
"@turbo/gen": "^2.8.21",
"@vitejs/plugin-react": "^5.2.0",
"@vitest/coverage-v8": "^4.1.0",
"@vitest/ui": "^4.1.0",
"conventional-changelog-conventionalcommits": "^9.3.0",
"@vitest/coverage-v8": "^4.1.2",
"@vitest/ui": "^4.1.2",
"conventional-changelog-conventionalcommits": "^9.3.1",
"cross-env": "^10.1.0",
"jsdom": "^28.1.0",
"jsdom": "^29.0.1",
"json5": "^2.2.3",
"prettier": "^3.8.1",
"semantic-release": "^25.0.3",
"testcontainers": "^11.13.0",
"turbo": "^2.8.20",
"turbo": "^2.8.21",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.1.0"
"vitest": "^4.1.2"
},
"packageManager": "pnpm@10.32.1",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">=24.14.0",
"pnpm": ">=10.32.1"
"node": ">=24.14.1",
"pnpm": ">=10.33.0"
},
"pnpm": {
"onlyBuiltDependencies": [
@@ -79,23 +79,23 @@
"overrides": {
"@babel/helpers@<7.26.10": ">=7.29.2",
"@babel/runtime@<7.26.10": ">=7.29.2",
"axios@>=1.0.0 <1.8.2": ">=1.13.6",
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.4",
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.4",
"axios@>=1.0.0 <1.8.2": ">=1.14.0",
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.5",
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.5",
"esbuild@<=0.24.2": ">=0.27.4",
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
"hono@<4.6.5": ">=4.12.8",
"hono@<4.6.5": ">=4.12.9",
"linkifyjs@<4.3.2": ">=4.3.2",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.7",
"prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.24.0",
"proxmox-api>undici": "7.24.6",
"react-is": "^19.2.4",
"rollup@>=4.0.0 <4.22.4": ">=4.59.0",
"rollup@>=4.0.0 <4.22.4": ">=4.60.0",
"sha.js@<=2.4.11": ">=2.4.12",
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
"tmp@<=0.2.3": ">=0.2.5",
"vite@>=5.0.0 <=5.4.18": ">=8.0.1"
"vite@>=5.0.0 <=5.4.18": ">=8.0.3"
},
"patchedDependencies": {
"@types/node-unifi": "patches/@types__node-unifi.patch",
+8 -8
View File
@@ -41,18 +41,18 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@kubernetes/client-node": "^1.4.0",
"@tanstack/react-query": "^5.91.2",
"@trpc/client": "^11.13.4",
"@trpc/react-query": "^11.13.4",
"@trpc/server": "^11.13.4",
"@trpc/tanstack-react-query": "^11.13.4",
"@tanstack/react-query": "^5.95.2",
"@trpc/client": "^11.16.0",
"@trpc/react-query": "^11.16.0",
"@trpc/server": "^11.16.0",
"@trpc/tanstack-react-query": "^11.16.0",
"lodash.clonedeep": "^4.5.0",
"next": "16.2.0",
"next": "16.2.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"superjson": "2.2.6",
"trpc-to-openapi": "^3.1.0",
"zod": "^4.2.1"
"trpc-to-openapi": "^3.2.0",
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -140,7 +140,7 @@ interface DockerContainer {
id: string;
state: ContainerState;
image: string;
ports: Port[];
ports: Port[] | undefined;
iconUrl: string | null;
cpuUsage: number;
memoryUsage: number;
+2 -2
View File
@@ -145,7 +145,7 @@ export const userRouter = createTRPCRouter({
create: permissionRequiredProcedure
.requiresPermission("admin")
.meta({ openapi: { method: "POST", path: "/api/users", tags: ["users"], protect: true } })
.input(userCreateSchema)
.input(convertIntersectionToZodObject(userCreateSchema))
.output(z.void())
.mutation(async ({ ctx, input }) => {
throwIfCredentialsDisabled();
@@ -376,7 +376,7 @@ export const userRouter = createTRPCRouter({
await ctx.db.delete(users).where(eq(users.id, input.userId));
}),
changePassword: protectedProcedure
.input(userChangePasswordApiSchema)
.input(convertIntersectionToZodObject(userChangePasswordApiSchema))
.output(z.void())
.meta({ openapi: { method: "PATCH", path: "/api/users/{userId}/changePassword", tags: ["users"], protect: true } })
.mutation(async ({ ctx, input }) => {
+1
View File
@@ -26,5 +26,6 @@ export const widgetRouter = createTRPCRouter({
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)),
timetable: lazy(() => import("./timetable").then((mod) => mod.timetableRouter)),
tracearr: lazy(() => import("./tracearr").then((mod) => mod.tracearrRouter)),
});
@@ -0,0 +1,43 @@
import z from "zod";
import { getIntegrationKindsByCategory } from "@homarr/definitions";
import { timetableOptionsSchema } from "@homarr/integrations/types";
import {
timetableGetTimetableRequestHandler,
timetableSearchStationsRequestHandler,
} from "@homarr/request-handler/timetable";
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
import { createTRPCRouter, publicProcedure } from "../../trpc";
export const timetableRouter = createTRPCRouter({
getTimetable: publicProcedure
.concat(createOneIntegrationMiddleware("query", ...getIntegrationKindsByCategory("timetable")))
.input(timetableOptionsSchema)
.query(async ({ input, ctx }) => {
const innerHandler = timetableGetTimetableRequestHandler.handler(ctx.integration, {
stationId: input.stationId,
limit: input.limit,
});
const { data } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
return data;
}),
searchStations: publicProcedure
.concat(createOneIntegrationMiddleware("query", ...getIntegrationKindsByCategory("timetable")))
.input(
z.object({
query: z.string(),
}),
)
.query(async ({ input, ctx }) => {
const innerHandler = timetableSearchStationsRequestHandler.handler(ctx.integration, {
query: input.query,
});
const { data } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
return data;
}),
});
+2 -2
View File
@@ -34,11 +34,11 @@
"bcrypt": "^6.0.0",
"cookies": "^0.9.1",
"ldapts": "8.1.7",
"next": "16.2.0",
"next": "16.2.1",
"next-auth": "5.0.0-beta.30",
"react": "19.2.4",
"react-dom": "19.2.4",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+3 -3
View File
@@ -30,12 +30,12 @@
"@paralleldrive/cuid2": "^3.3.0",
"cookie": "1.1.1",
"dayjs": "^1.11.20",
"next": "16.2.0",
"next": "16.2.1",
"octokit": "^5.0.5",
"react": "19.2.4",
"react-dom": "19.2.4",
"undici": "7.18.2",
"zod": "^4.2.1",
"undici": "7.24.6",
"zod": "^4.3.6",
"zod-validation-error": "^5.0.0"
},
"devDependencies": {
+5 -5
View File
@@ -37,23 +37,23 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@t3-oss/env-nextjs": "^0.13.10",
"better-sqlite3": "^12.6.2",
"@t3-oss/env-nextjs": "^0.13.11",
"better-sqlite3": "^12.8.0",
"dns-caching": "^0.2.9",
"drizzle-orm": "^0.45.1",
"drizzle-orm": "^0.45.2",
"ioredis": "5.10.1",
"mysql2": "3.20.0",
"pg": "^8.20.0",
"superjson": "2.2.6",
"winston": "3.19.0",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/better-sqlite3": "7.6.13",
"@types/pg": "^8.18.0",
"@types/pg": "^8.20.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
}
+5 -5
View File
@@ -28,13 +28,13 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/cron-jobs": "workspace:^0.1.0",
"@tanstack/react-query": "^5.91.2",
"@trpc/client": "^11.13.4",
"@trpc/server": "^11.13.4",
"@trpc/tanstack-react-query": "^11.13.4",
"@tanstack/react-query": "^5.95.2",
"@trpc/client": "^11.16.0",
"@trpc/server": "^11.16.0",
"@trpc/tanstack-react-query": "^11.16.0",
"node-cron": "^4.2.1",
"react": "19.2.4",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2
View File
@@ -18,6 +18,7 @@ import { mediaServerJob } from "./jobs/integrations/media-server";
import { mediaTranscodingJob } from "./jobs/integrations/media-transcoding";
import { networkControllerJob } from "./jobs/integrations/network-controller";
import { refreshNotificationsJob } from "./jobs/integrations/notifications";
import { timetableJob } from "./jobs/integrations/timetable";
import { tracearrJob } from "./jobs/integrations/tracearr";
import { minecraftServerStatusJob } from "./jobs/minecraft-server-status";
import { pingJob } from "./jobs/ping";
@@ -51,6 +52,7 @@ export const jobGroup = createCronJobGroup({
firewallInterfaces: firewallInterfacesJob,
refreshNotifications: refreshNotificationsJob,
weather: weatherJob,
timetable: timetableJob,
tracearr: tracearrJob,
});
@@ -0,0 +1,20 @@
import { EVERY_5_MINUTES } from "@homarr/cron-jobs-core/expressions";
import { createRequestIntegrationJobHandler } from "@homarr/request-handler/lib/cached-request-integration-job-handler";
import { timetableGetTimetableRequestHandler } from "@homarr/request-handler/timetable";
import { createCronJob } from "../../lib";
export const timetableJob = createCronJob("timetable", EVERY_5_MINUTES).withCallback(
createRequestIntegrationJobHandler(timetableGetTimetableRequestHandler.handler, {
widgetKinds: ["timetable"],
getInput: {
timetable: (options) =>
options.station
? {
stationId: options.station.value,
limit: 10,
}
: [],
},
}),
);
+3 -3
View File
@@ -51,10 +51,10 @@
"@paralleldrive/cuid2": "^3.3.0",
"@testcontainers/mysql": "^11.13.0",
"@testcontainers/postgresql": "^11.13.0",
"better-sqlite3": "^12.6.2",
"better-sqlite3": "^12.8.0",
"dotenv": "^17.3.1",
"drizzle-kit": "^0.31.10",
"drizzle-orm": "^0.45.1",
"drizzle-orm": "^0.45.2",
"drizzle-zod": "^0.8.3",
"mysql2": "3.20.0",
"pg": "^8.20.0",
@@ -65,7 +65,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/better-sqlite3": "7.6.13",
"@types/pg": "^8.18.0",
"@types/pg": "^8.20.0",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
+2 -2
View File
@@ -24,8 +24,8 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"fast-xml-parser": "^5.5.7",
"zod": "^4.2.1"
"fast-xml-parser": "^5.5.9",
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -188,6 +188,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/radarr"
| "/docs/integrations/readarr"
| "/docs/integrations/sabnzbd"
| "/docs/integrations/search-ch"
| "/docs/integrations/seerr"
| "/docs/integrations/slskd"
| "/docs/integrations/sonarr"
@@ -237,6 +238,7 @@ export type HomarrDocumentationPath =
| "/docs/widgets/smart-home-execute-automation"
| "/docs/widgets/stock-price"
| "/docs/widgets/system-resources"
| "/docs/widgets/timetable"
| "/docs/widgets/tracearr"
| "/docs/widgets/video"
| "/docs/widgets/weather"
+10 -1
View File
@@ -217,7 +217,7 @@ export const integrationDefs = {
name: "Nextcloud",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg",
category: ["calendar"],
category: ["calendar", "notifications"],
documentationUrl: createDocumentationLink("/docs/integrations/nextcloud"),
},
unifiController: {
@@ -334,6 +334,14 @@ export const integrationDefs = {
// @ts-expect-error - docs page will be created when integration is merged
documentationUrl: createDocumentationLink("/docs/integrations/coolify"),
},
searchCh: {
name: "Search.ch",
secretKinds: [[]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/search-ch.png",
defaultUrl: "https://search.ch",
category: ["timetable"],
documentationUrl: createDocumentationLink("/docs/integrations/search-ch"),
},
immich: {
name: "Immich",
secretKinds: [["apiKey"]],
@@ -434,6 +442,7 @@ export const integrationCategories = [
"releasesProvider",
"notifications",
"firewall",
"timetable",
"photoService",
"mediaMonitoring",
] as const;
+1
View File
@@ -31,6 +31,7 @@ export const widgetKinds = [
"systemResources",
"coolify",
"systemDisks",
"timetable",
"immich-serverStats",
"immich-albumCarousel",
"tracearr",
+1 -1
View File
@@ -26,7 +26,7 @@
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"dockerode": "^4.0.9"
"dockerode": "^4.0.10"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -28,7 +28,7 @@
"@homarr/validation": "workspace:^0.1.0",
"@mantine/form": "^8.3.18",
"mantine-form-zod-resolver": "^1.3.0",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+1 -1
View File
@@ -32,7 +32,7 @@
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.18",
"react": "19.2.4",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+5 -5
View File
@@ -25,7 +25,7 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@ctrl/deluge": "^7.5.2",
"@ctrl/deluge": "^8.0.0",
"@ctrl/qbittorrent": "^9.11.2",
"@ctrl/transmission": "^7.4.0",
"@gitbeaker/rest": "^43.8.0",
@@ -38,18 +38,18 @@
"@homarr/redis": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@immich/sdk": "^2.6.1",
"@immich/sdk": "^2.6.3",
"@jellyfin/sdk": "^0.13.0",
"@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",
"timezones-ical-library": "^2.1.3",
"tsdav": "^2.1.8",
"undici": "7.18.2",
"undici": "7.24.6",
"xml2js": "^0.6.2",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -40,6 +40,7 @@ import { PlexIntegration } from "../plex/plex-integration";
import { ProwlarrIntegration } from "../prowlarr/prowlarr-integration";
import { ProxmoxIntegration } from "../proxmox/proxmox-integration";
import { QuayIntegration } from "../quay/quay-integration";
import { SearchChIntegration } from "../search-ch/search-ch-integration";
import { SeerrIntegration } from "../seerr/seerr-integration";
import { TracearrIntegration } from "../tracearr/tracearr-integration";
import { TrueNasIntegration } from "../truenas/truenas-integration";
@@ -114,6 +115,7 @@ export const integrationCreators = {
coolify: CoolifyIntegration,
tracearr: TracearrIntegration,
glances: GlancesIntegration,
searchCh: SearchChIntegration,
immich: ImmichIntegration,
} satisfies Record<IntegrationKind, IntegrationInstance | [(input: IntegrationInput) => Promise<Integration>]>;
@@ -33,6 +33,7 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
const memoryLoad = await this.getCurrentMemoryLoadAsync();
const storageLoad = await this.getCurrentStorageLoadAsync();
const networkLoad = await this.getCurrentNetworkLoadAsync();
const gpuLoad = await this.getCurrentGpuLoadAsync();
const channel = this.getChannel();
const history = await channel.getSliceUntilTimeAsync(dayjs().subtract(15, "minutes").toDate());
@@ -56,6 +57,14 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
availablePkgUpdates: 0,
rebootRequired: false,
smart: [], // API endpoint does not provide S.M.A.R.T data.
gpu: gpuLoad.map((gpu, index) => ({
gpuId: `gpu-${index}`,
name: info.gpuNames[index] ?? `GPU ${index}`,
memoryUtilization: gpu.memory ?? 0,
processorUtilization: gpu.load ?? 0,
temperature: null,
fanSpeed: null,
})),
uptime: info.uptime,
version: `${info.operatingSystemVersion}`,
loadAverage: {
@@ -76,6 +85,7 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
cpuModel: serverInfo.cpu.model,
operatingSystemVersion: `${serverInfo.os.distro} ${serverInfo.os.release} (${serverInfo.os.kernel})`,
uptime: serverInfo.os.uptime,
gpuNames: serverInfo.gpu.layout.map((gpu) => gpu.brand),
};
}
@@ -150,6 +160,17 @@ export class DashDotIntegration extends Integration implements ISystemHealthMoni
return await networkLoadApi.parseAsync(JSON.parse(result));
}
private async getCurrentGpuLoadAsync() {
try {
const response = await fetchWithTrustedCertificatesAsync(this.url("/load/gpu"));
const result = await response.json();
const data = await gpuLoadApi.parseAsync(result);
return data.layout;
} catch {
return [];
}
}
private getChannel() {
return createChannelEventHistoryOld<z.infer<typeof cpuLoadPerCoreApiList>>(
`integration:${this.integration.id}:history:cpu`,
@@ -198,6 +219,25 @@ const internalServerInfoApi = z.object({
),
}),
),
gpu: z.object({
layout: z
.array(
z.object({
brand: z.string(),
model: z.string().optional(),
}),
)
.default([]),
}),
});
const cpuLoadPerCoreApiList = z.array(cpuLoadPerCoreApi);
const gpuLoadApi = z.object({
layout: z.array(
z.object({
load: z.number().min(0).optional(),
memory: z.number().min(0).optional(),
}),
),
});
@@ -59,6 +59,14 @@ export class GlancesIntegration extends Integration implements ISystemHealthMoni
loadAverage: null,
smart: [],
cpuTemp: undefined,
gpu: stats.gpu.map((gpu) => ({
gpuId: gpu.gpu_id,
name: gpu.name,
memoryUtilization: gpu.mem ?? 0,
processorUtilization: gpu.proc ?? 0,
temperature: gpu.temperature ?? null,
fanSpeed: gpu.fan_speed ?? null,
})),
};
}
@@ -150,4 +158,16 @@ const allSchema = z.object({
quicklook: z.object({
cpu_name: z.string(),
}),
gpu: z
.array(
z.object({
gpu_id: z.string(),
name: z.string(),
mem: z.number().nullable().optional(),
proc: z.number().nullable().optional(),
temperature: z.number().nullable().optional(),
fan_speed: z.number().nullable().optional(),
}),
)
.default([]),
});
@@ -1,5 +1,6 @@
import { getAlbumInfo, getAllAlbums, getMyUser, getServerStatistics, init, searchUsers } from "@immich/sdk";
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ImageProxy } from "@homarr/image-proxy";
@@ -41,7 +42,10 @@ export class ImmichIntegration extends Integration {
public async getServerStatsAsync(): Promise<ImmichServerStats> {
this.initClient();
const [statistics, users] = await Promise.all([getServerStatistics(), searchUsers()]);
const [statistics, users] = await Promise.all([
getServerStatistics(this.getRequestOptions()),
searchUsers(this.getRequestOptions()),
]);
return {
photoCount: statistics.photos,
userCount: users.length,
@@ -52,7 +56,7 @@ export class ImmichIntegration extends Integration {
public async getAlbumAsync(albumId: string): Promise<ImmichAlbum> {
this.initClient();
const album = await getAlbumInfo({ id: albumId });
const album = await getAlbumInfo({ id: albumId }, this.getRequestOptions());
const imageProxy = new ImageProxy();
return {
albumName: album.albumName,
@@ -89,17 +93,24 @@ export class ImmichIntegration extends Integration {
}[]
> {
this.initClient();
const albums = await getAllAlbums({});
const albums = await getAllAlbums({}, this.getRequestOptions());
return albums.map((album) => ({ id: album.id, albumName: album.albumName, assetCount: album.assetCount }));
}
protected async testingAsync(_: IntegrationTestingInput): Promise<TestingResult> {
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
this.initClient();
const user = await getMyUser();
const user = await getMyUser(this.getRequestOptions(input.fetchAsync));
logger.debug(`Logged in as ${user.name} (${user.id})`);
return { success: true };
}
private getRequestOptions(fetchAsync = fetchWithTrustedCertificatesAsync) {
return {
// Undici and node types are not the same
fetch: fetchAsync as unknown as typeof fetch,
};
}
/**
* Sets the credentials and prepares the client for calls.
* Must be called before any other functions.
@@ -31,6 +31,14 @@ export interface SystemHealthMonitoring {
overallStatus: string;
healthy: boolean;
}[];
gpu: {
gpuId: string;
name: string;
memoryUtilization: number;
processorUtilization: number;
temperature: number | null;
fanSpeed: number | null;
}[];
}
// TODO: in the future decouple this from the Proxmox integration
@@ -0,0 +1,6 @@
import type { Station, Timetable, TimetableOptions } from "./timetable-types";
export interface TimetableIntegration {
searchStationsAsync(query: string): Promise<Station[]>;
getTimetableAsync(options: TimetableOptions): Promise<Timetable>;
}
@@ -0,0 +1,33 @@
import z from "zod";
export interface Station {
id: string;
name: string;
}
export const timetableOptionsSchema = z.object({
stationId: z.string(),
limit: z.number().default(10),
});
export type TimetableOptions = z.infer<typeof timetableOptionsSchema>;
export interface TimetableEntry {
timestamp: Date;
delay: number;
line: {
name: string;
color: string | null;
} | null;
location: string;
platform: {
name: string;
hasChanged: boolean;
} | null;
}
export interface Timetable {
stationId: string;
timestamp: Date;
entries: TimetableEntry[];
}
@@ -36,6 +36,16 @@ export class SystemHealthMonitoringMockService implements ISystemHealthMonitorin
healthy: true,
},
],
gpu: [
{
gpuId: "mock0",
name: "Mock GPU RTX 4090",
memoryUtilization: Math.floor(Math.random() * 100),
processorUtilization: Math.floor(Math.random() * 100),
temperature: Math.floor(Math.random() * 90),
fanSpeed: Math.floor(Math.random() * 2000),
},
],
});
}
}
@@ -2,11 +2,14 @@ import type { Agent } from "https";
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import ICAL from "ical.js";
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
import * as ical from "node-ical";
import { tzlib_get_ical_block, tzlib_get_timezones } from "timezones-ical-library";
import { DAVClient } from "tsdav";
import { z } from "zod";
import { createHttpsAgentAsync } from "@homarr/core/infrastructure/http";
import { ResponseError } from "@homarr/common/server";
import { createHttpsAgentAsync, fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { HandleIntegrationErrors } from "../base/errors/decorator";
@@ -16,6 +19,35 @@ import { Integration } from "../base/integration";
import type { TestingResult } from "../base/test-connection/test-connection-service";
import type { ICalendarIntegration } from "../interfaces/calendar/calendar-integration";
import type { CalendarEvent } from "../interfaces/calendar/calendar-types";
import type { Notification } from "../interfaces/notifications/notification-types";
import type { INotificationsIntegration } from "../interfaces/notifications/notifications-integration";
// Register all existing timezones
if (ICAL.TimezoneService.count === 0) {
const timezones = tzlib_get_timezones() as string[];
for (const tzid of timezones) {
ICAL.TimezoneService.register(
new ICAL.Timezone({
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-non-null-assertion
component: new ICAL.Component(ICAL.parse((tzlib_get_ical_block(tzid) as string[])[0]!)),
tzid,
}),
);
}
}
const notificationSchema = z.object({
ocs: z.object({
data: z.array(
z.object({
notification_id: z.number(),
datetime: z.string(),
subject: z.string(),
message: z.string(),
}),
),
}),
});
const logger = createLogger({ module: "nextcloudIntegration" });
@@ -23,7 +55,7 @@ dayjs.extend(utc);
dayjs.extend(timezone);
@HandleIntegrationErrors([integrationTsdavHttpErrorHandler])
export class NextcloudIntegration extends Integration implements ICalendarIntegration {
export class NextcloudIntegration extends Integration implements ICalendarIntegration, INotificationsIntegration {
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
const client = await this.createCalendarClientAsync(await createHttpsAgentAsync(input.options));
await client.login();
@@ -51,42 +83,64 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr
return calendarEvents
.map((event) => {
// @ts-expect-error the typescript definitions for this package are wrong
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
const icalData = ical.default.parseICS(event.data) as ical.CalendarResponse;
const veventObject = Object.values(icalData).find((data) => data.type === "VEVENT");
if (!veventObject) {
throw new Error(`Invalid event data object: ${JSON.stringify(event.data)}. Unable to process the calendar.`);
if (typeof event.data !== "string") {
return [];
}
const comp = ICAL.Component.fromString(event.data);
const veventComponent = comp.getFirstSubcomponent("vevent");
if (!veventComponent) {
return [];
}
const veventObject = new ICAL.Event(veventComponent);
logger.debug(`Converting VEVENT event to ${event.etag} from Nextcloud: ${JSON.stringify(veventObject)}`);
const eventUrlWithoutHost = new URL(event.url).pathname;
const eventSlug = Buffer.from(eventUrlWithoutHost).toString("base64url");
const startDates = veventObject.rrule ? veventObject.rrule.between(start, end) : [veventObject.start];
const durationMs = veventObject.duration.toSeconds() * 1000;
const durationMs = veventObject.end.getTime() - veventObject.start.getTime();
// Handle recurring events
let startDates: Date[];
if (veventObject.isRecurring()) {
const iterator = veventObject.iterator();
startDates = [];
// Iterate through occurrences within the time range
let next: ICAL.Time | undefined;
// next actually returns undefined when there are no more occurrences
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
while ((next = iterator.next())) {
const nextStartDate = next.convertToZone(ICAL.Timezone.utcTimezone).toJSDate();
if (nextStartDate > end) break;
const nextEndDate = new Date(nextStartDate.getTime() + durationMs);
if (nextEndDate < start) continue;
startDates.push(nextStartDate);
}
} else {
startDates = [veventObject.startDate.convertToZone(ICAL.Timezone.utcTimezone).toJSDate()];
}
return startDates.map((startDate) => {
const timezoneOffsetMinutes = veventObject.rrule?.origOptions.tzid
? dayjs(startDate).tz(veventObject.rrule.origOptions.tzid).utcOffset()
: 0;
const utcStartDate = new Date(startDate.getTime() - timezoneOffsetMinutes * 60 * 1000);
const endDate = new Date(utcStartDate.getTime() + durationMs);
const dateInMillis = utcStartDate.valueOf();
const endDate = new Date(startDate.getTime() + durationMs);
const dateInMillis = startDate.valueOf();
// Get color property from the component
const colorProperty = veventComponent.getFirstProperty("color");
const color = colorProperty?.getFirstValue() as string | undefined;
return {
title: veventObject.summary,
subTitle: null,
description: veventObject.description,
startDate: utcStartDate,
description: veventObject.description || null,
startDate,
endDate,
image: null,
location: veventObject.location || null,
indicatorColor:
"color" in veventObject && typeof veventObject.color === "string" ? veventObject.color : "#ff8600",
indicatorColor: color ?? "#ff8600",
links: [
{
href: this.externalUrl(
@@ -104,6 +158,29 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr
.flat();
}
public async getNotificationsAsync(): Promise<Notification[]> {
const url = this.url("/ocs/v2.php/apps/notifications/api/v2/notifications", { format: "json" });
const response = await fetchWithTrustedCertificatesAsync(url, {
headers: {
Authorization: `Basic ${Buffer.from(
`${this.getSecretValue("username")}:${this.getSecretValue("password")}`,
).toString("base64")}`,
"OCS-APIRequest": "true",
},
});
if (!response.ok) throw new ResponseError(response);
const json = notificationSchema.parse(await response.json());
return json.ocs.data.map((notification) => ({
id: String(notification.notification_id),
time: new Date(notification.datetime),
title: notification.subject,
body: notification.message,
}));
}
private async createCalendarClientAsync(agent?: Agent) {
return new DAVClient({
serverUrl: this.integration.url,
@@ -86,6 +86,7 @@ export class OpenMediaVaultIntegration extends Integration implements ISystemHea
cpuTemp: cpuTempResult.success ? cpuTempResult.data.response.cputemp : undefined,
fileSystem,
smart,
gpu: [], // OpenMediaVault does not expose GPU monitoring via its RPC API.
};
}
@@ -0,0 +1,141 @@
import z from "zod";
import { ResponseError } from "@homarr/common/server";
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
import type { IntegrationTestingInput } from "../base/integration";
import { Integration } from "../base/integration";
import { TestConnectionError } from "../base/test-connection/test-connection-error";
import type { TestingResult } from "../base/test-connection/test-connection-service";
import type { TimetableIntegration } from "../interfaces/timetable/timetable-integration";
import type { Station, Timetable, TimetableOptions } from "../interfaces/timetable/timetable-types";
const supportedStationTypes = ["bus", "tram", "train", "ship", "cablecar", "funicular", "chairlift"];
export class SearchChIntegration extends Integration implements TimetableIntegration {
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
const response = await input.fetchAsync(this.url("/timetable/api/completion.json", { term: "Zürich" }));
if (!response.ok) {
return TestConnectionError.StatusResult(response);
}
return {
success: true,
};
}
/**
* Searches for stations matching the given query.
* @param query The search query for station names.
* @returns A list of stations matching the search query.
* @docs https://search.ch/fahrplan/api/help
*/
public async searchStationsAsync(query: string): Promise<Station[]> {
const response = await fetchWithTrustedCertificatesAsync(
this.url("/timetable/api/completion.json", {
term: query,
show_ids: 1,
nofavorites: 1,
}),
);
if (!response.ok) {
throw new ResponseError(response);
}
const data = await searchSchema.parseAsync(await response.json());
return (
data
// Remove completions like addresses, we only want stations.
.filter((item) => supportedStationTypes.some((type) => item.iconclass.endsWith(type)))
.map((item) => (item.id !== undefined ? { id: item.id, name: item.label } : null))
.filter((item) => item !== null)
);
}
/**
* Gets the departure timetable for a given station.
* @param options Timetable options including station ID and limit.
* @returns timetable for the given station.
* @docs https://search.ch/fahrplan/api/help
*/
public async getTimetableAsync(options: TimetableOptions): Promise<Timetable> {
const now = new Date();
// The following format is used [01.01.2000, 23:59:00]. The API allows usage of DD.MM.YYYY format and HH:mm format
const [date, time] = now.toLocaleString("en-CH", { timeZone: "Europe/Zurich" }).split(", ") as [string, string];
const response = await fetchWithTrustedCertificatesAsync(
this.url("/timetable/api/stationboard.json", {
stop: options.stationId,
limit: options.limit,
show_delays: 1,
show_tracks: 1,
date,
time: time.substring(0, 5), // Only keep the HH:mm part of the time, as that's what the API expects
}),
);
if (!response.ok) {
throw new ResponseError(response);
}
const data = await timetableSchema.parseAsync(await response.json());
return {
stationId: options.stationId,
timestamp: now,
entries: data.connections.map((connection) => {
// Color is in format A35~F10~ for foreground and background color. Some only have ~~ and therefore no color
const color = connection.color.split("~")[0];
return {
timestamp: connection.time,
line: connection.line
? {
name: connection.line,
color: color && color.length >= 1 ? `#${color}` : null,
}
: null,
location: connection.terminal.name,
delay: connection.dep_delay,
platform: connection.track
? {
// If the track name contains "!", it indicates a change in the track
// so we remove the "!" from the name and set hasChanged to true
name: connection.track.replace("!", ""),
hasChanged: connection.track.includes("!"),
}
: null,
};
}),
};
}
}
const timetableSchema = z.object({
connections: z.array(
z.object({
time: z.string().transform((str) => new Date(str)),
line: z.string().optional(),
color: z.string(),
terminal: z.object({
name: z.string(),
}),
track: z.string().optional(),
dep_delay: z
.string()
.optional()
.transform((str) => (str ? parseInt(str) : 0)),
}),
),
});
const searchSchema = z.array(
z.object({
id: z.string().optional(),
label: z.string(),
iconclass: z.string(), // This is in format sl-icon-type-{type}
}),
);
@@ -297,6 +297,7 @@ export class TrueNasIntegration extends Integration implements ISystemHealthMoni
version: systemInformation.version,
cpuModelName: systemInformation.model,
rebootRequired: false,
gpu: [],
};
}
+1
View File
@@ -12,4 +12,5 @@ export * from "./unifi-controller/unifi-controller-types";
export * from "./opnsense/opnsense-types";
export * from "./interfaces/media-releases";
export * from "./coolify/coolify-types";
export * from "./interfaces/timetable/timetable-types";
export * from "./tracearr/tracearr-types";
@@ -70,6 +70,7 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit
// See ArrayDiskStatus from https://studio.apollographql.com/public/Unraid-API/variant/current/explorer
healthy: disk.status === "DISK_OK",
})),
gpu: [],
};
}
@@ -0,0 +1,454 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { StartedTestContainer } from "testcontainers";
import { GenericContainer, getContainerRuntimeClient, ImageName, Wait } from "testcontainers";
import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
import { createId } from "@homarr/common";
import { createDb } from "@homarr/db/test";
import { NextcloudIntegration } from "../src";
// Mock the database
vi.mock("@homarr/db", async (importActual) => {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const actual = await importActual<typeof import("@homarr/db")>();
return {
...actual,
db: createDb(),
};
});
// Mock certificate verification
vi.mock("@homarr/core/infrastructure/certificates", async (importActual) => {
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
const actual = await importActual<typeof import("@homarr/core/infrastructure/certificates")>();
return {
...actual,
getTrustedCertificateHostnamesAsync: vi.fn().mockImplementation(() => {
return Promise.resolve([]);
}),
};
});
const IMAGE_NAME = "nextcloud:latest";
const DEFAULT_USERNAME = "admin";
const DEFAULT_PASSWORD = "admin123";
interface CalendarTestEvent {
summary: string;
description?: string;
location?: string;
color?: string;
dtstart: string;
dtend: string;
rrule?: string;
timezone?: string;
}
describe("Nextcloud integration", () => {
let startedContainer: StartedTestContainer | null = null;
let nextcloudIntegration: NextcloudIntegration | null = null;
beforeAll(async () => {
const containerRuntimeClient = await getContainerRuntimeClient();
await containerRuntimeClient.image.pull(ImageName.fromString(IMAGE_NAME));
startedContainer = await prepareNextcloudContainerAsync();
nextcloudIntegration = createNextcloudIntegration(startedContainer);
}, 100_000);
afterAll(async () => {
if (startedContainer) {
await startedContainer.stop();
startedContainer = null;
}
});
describe("Test connection", () => {
test("Test connection > Should work with valid credentials", async () => {
// Act
const result = await nextcloudIntegration!.testConnectionAsync();
// Assert
expect(result.success).toBe(true);
}, 20_000);
test("Test connection > Should fail with invalid credentials", async () => {
// Arrange
const invalidIntegration = createNextcloudIntegration(startedContainer!, "wrong-user", "wrong-pass");
// Act
const result = await invalidIntegration.testConnectionAsync();
// Assert
expect(result.success).toBe(false);
}, 20_000);
});
describe("Retrieve calendar events", () => {
test("Retrieve calendar events > Should retrieve all fields correctly", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Test Meeting",
description: "This is a test meeting",
location: "Conference Room A",
color: "#FF5733",
dtstart: "20260415T140000Z",
dtend: "20260415T150000Z",
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(1);
const retrievedEvent = relevantEvents[0];
expect(retrievedEvent).toMatchObject({
title: testEvent.summary,
description: testEvent.description,
location: testEvent.location,
indicatorColor: testEvent.color,
});
expect(retrievedEvent!.startDate.toISOString()).toBe("2026-04-15T14:00:00.000Z");
expect(retrievedEvent!.endDate?.toISOString()).toBe("2026-04-15T15:00:00.000Z");
}, 20_000);
test("Retrieve calendar events > Should work with only required fields", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Simple Event",
dtstart: "20260415T140000Z",
dtend: "20260415T150000Z",
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(1);
const retrievedEvent = relevantEvents[0];
expect(retrievedEvent?.location).toBeNull();
expect(retrievedEvent?.description).toBeNull();
expect(retrievedEvent?.indicatorColor).toBe("#ff8600"); // Default color
}, 20_000);
test("Retrieve calendar events > Should work with timezone specified", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Timezone Event",
dtstart: "20260415T140000",
dtend: "20260415T150000",
timezone: "Asia/Tokyo",
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(1);
const retrievedEvent = relevantEvents[0];
expect(retrievedEvent!.startDate.toISOString()).toBe("2026-04-15T05:00:00.000Z"); // 14:00 in Tokyo is 05:00 UTC
expect(retrievedEvent!.endDate?.toISOString()).toBe("2026-04-15T06:00:00.000Z"); // 15:00 in Tokyo is 06:00 UTC
}, 120_000);
test("Retrieve calendar events > Should work with event that starts before the range but ends within the range", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Early Meeting",
dtstart: "20260401T230000Z", // Starts before the range
dtend: "20260402T010000Z", // Ends within the range
};
const start = new Date("2026-04-02T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(1);
}, 20_000);
test("Retrieve calendar events > Should work with event that starts within the range but ends after the range", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Late Meeting",
dtstart: "20260430T230000Z", // Starts within the range
dtend: "20260501T010000Z", // Ends after the range
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(1);
}, 20_000);
test("Retrieve calendar events > Should not retrieve events outside the range", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Out of Range Meeting",
dtstart: "20260301T140000Z", // Starts before the range
dtend: "20260301T150000Z", // Ends before the range
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(0);
}, 20_000);
test("Retrieve calendar events > Should work with events that begin before the range and end after the range", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Spanning Meeting",
dtstart: "20260330T230000Z", // Starts before the range
dtend: "20260501T010000Z", // Ends after the range
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(1);
}, 20_000);
test("Retrieve calendar events > Should work with recurring events first occurrence before the range", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Recurring Meeting",
dtstart: "20260330T140000Z", // First occurrence starts before the range
dtend: "20260330T150000Z",
rrule: "FREQ=DAILY;COUNT=5", // Daily for 5 occurrences
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(3);
}, 20_000);
test("Retrieve calendar events > Should work with recurring events that go beyond the range", async () => {
// Arrange
const calendarName = createId();
const testEvent: CalendarTestEvent = {
summary: "Working Week",
dtstart: "20260302T090000Z",
dtend: "20260306T170000Z",
rrule: "FREQ=WEEKLY;COUNT=10",
};
const start = new Date("2026-04-01T00:00:00Z");
const end = new Date("2026-04-30T23:59:59Z");
await createCalendarAsync(startedContainer!, calendarName);
await createCalendarEventAsync(startedContainer!, calendarName, testEvent);
// Act
const events = await nextcloudIntegration!.getCalendarEventsAsync(start, end);
// Assert
const relevantEvents = events.filter((event) => event.title === testEvent.summary);
expect(relevantEvents).toHaveLength(5);
});
});
});
/**
* Prepares a Nextcloud container with proper configuration
*/
async function prepareNextcloudContainerAsync(): Promise<StartedTestContainer> {
const container = createNextcloudContainer();
const startedContainer = await container.start();
return startedContainer;
}
/**
* Creates a Nextcloud container with proper configuration
*/
function createNextcloudContainer() {
return new GenericContainer(IMAGE_NAME)
.withEnvironment({
NEXTCLOUD_ADMIN_USER: DEFAULT_USERNAME,
NEXTCLOUD_ADMIN_PASSWORD: DEFAULT_PASSWORD,
NEXTCLOUD_TRUSTED_DOMAINS: "*",
SQLITE_DATABASE: "nextcloud", // Use SQLite for faster startup
})
.withExposedPorts(80)
.withWaitStrategy(Wait.forHttp("/status.php", 80).forStatusCode(200))
.withStartupTimeout(120_000);
}
/**
* Creates a Nextcloud integration instance
*/
function createNextcloudIntegration(
container: StartedTestContainer,
username?: string,
password?: string,
): NextcloudIntegration {
return new NextcloudIntegration({
id: "1",
decryptedSecrets: [
{
kind: "username",
value: username ?? DEFAULT_USERNAME,
},
{
kind: "password",
value: password ?? DEFAULT_PASSWORD,
},
],
name: "Nextcloud",
url: `http://${container.getHost()}:${container.getMappedPort(80)}`,
externalUrl: null,
});
}
/**
* Creates a calendar in Nextcloud via OCS API
*/
async function createCalendarAsync(container: StartedTestContainer, calendarName: string): Promise<void> {
const host = container.getHost();
const port = container.getMappedPort(80);
const url = `http://${host}:${port}/remote.php/dav/calendars/${DEFAULT_USERNAME}/${calendarName}`;
const auth = Buffer.from(`${DEFAULT_USERNAME}:${DEFAULT_PASSWORD}`).toString("base64");
const response = await fetch(url, {
method: "MKCALENDAR",
headers: {
Authorization: `Basic ${auth}`,
"Content-Type": "application/xml",
},
body: `<?xml version="1.0" encoding="UTF-8"?>
<c:mkcalendar xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
<d:set>
<d:prop>
<d:displayname>${calendarName}</d:displayname>
</d:prop>
</d:set>
</c:mkcalendar>`,
});
if (!response.ok && response.status !== 201) {
throw new Error(`Failed to create calendar: ${response.status} ${response.statusText}`);
}
}
/**
* Creates a calendar event in Nextcloud via CalDAV
*/
async function createCalendarEventAsync(
container: StartedTestContainer,
calendarName: string,
event: CalendarTestEvent,
): Promise<void> {
const host = container.getHost();
const port = container.getMappedPort(80);
const id = createId();
const url = `http://${host}:${port}/remote.php/dav/calendars/${DEFAULT_USERNAME}/${calendarName}/${id}.ics`;
const auth = Buffer.from(`${DEFAULT_USERNAME}:${DEFAULT_PASSWORD}`).toString("base64");
// Build iCalendar format
const icalData = buildICalEvent(id, event);
const response = await fetch(url, {
method: "PUT",
headers: {
Authorization: `Basic ${auth}`,
"Content-Type": "text/calendar",
},
body: icalData,
});
if (!response.ok && response.status !== 201) {
throw new Error(`Failed to create event: ${response.status} ${response.statusText}`);
}
}
/**
* Builds an iCalendar format string from event data
*/
function buildICalEvent(uid: string, event: CalendarTestEvent): string {
const lines = [
"BEGIN:VCALENDAR",
"VERSION:2.0",
"PRODID:-//Homarr Tests//EN",
"BEGIN:VEVENT",
`UID:${uid}`,
`DTSTAMP:${new Date().toISOString().replace(/[-:]/g, "").split(".")[0]}Z`,
`DTSTART${event.timezone ? `;TZID=${event.timezone}` : ""}:${event.dtstart}`,
`DTEND${event.timezone ? `;TZID=${event.timezone}` : ""}:${event.dtend}`,
`SUMMARY:${event.summary}`,
];
if (event.description) {
lines.push(`DESCRIPTION:${event.description}`);
}
if (event.location) {
lines.push(`LOCATION:${event.location}`);
}
if (event.color) {
lines.push(`COLOR:${event.color}`);
}
if (event.rrule) {
lines.push(`RRULE:${event.rrule}`);
}
lines.push("END:VEVENT", "END:VCALENDAR");
return lines.join("\r\n");
}
+3 -3
View File
@@ -34,12 +34,12 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.18",
"@tabler/icons-react": "^3.40.0",
"@tabler/icons-react": "^3.41.0",
"dayjs": "^1.11.20",
"next": "16.2.0",
"next": "16.2.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -57,10 +57,12 @@ export const AddBoardModal = createModal(({ actions }) => {
{...form.getInputProps("name")}
description={
boardNameStatus.description ? (
<Group c={boardNameStatus.description.color} gap="xs" align="center">
{boardNameStatus.description.icon ? <boardNameStatus.description.icon size={16} /> : null}
<span>{boardNameStatus.description.label}</span>
</Group>
<span>
<Group c={boardNameStatus.description.color} gap="xs" align="center">
{boardNameStatus.description.icon ? <boardNameStatus.description.icon size={16} /> : null}
<span>{boardNameStatus.description.label}</span>
</Group>
</span>
) : null
}
/>
@@ -21,7 +21,7 @@ export const AddDockerAppToHomarrModal = createModal<AddDockerAppToHomarrProps>(
{
initialValues: {
containerUrls: innerProps.selectedContainers.map((container) => {
if (container.ports[0]) {
if (Array.isArray(container.ports) && container.ports[0]) {
return `http://${container.ports[0].IP}:${container.ports[0].PublicPort}`;
}
+1 -1
View File
@@ -25,7 +25,7 @@
"dependencies": {
"@homarr/ui": "workspace:^0.1.0",
"@mantine/notifications": "^8.3.18",
"@tabler/icons-react": "^3.40.0"
"@tabler/icons-react": "^3.41.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -2
View File
@@ -40,11 +40,11 @@
"@mantine/core": "^8.3.18",
"@mantine/hooks": "^8.3.18",
"adm-zip": "0.5.16",
"next": "16.2.0",
"next": "16.2.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"superjson": "2.2.6",
"zod": "^4.2.1",
"zod": "^4.3.6",
"zod-form-data": "^3.0.1"
},
"devDependencies": {
@@ -101,6 +101,7 @@ const optionMapping: OptionMapping = {
allowMicrophone: (oldOptions) => oldOptions.allowMicrophone,
allowGeolocation: (oldOptions) => oldOptions.allowGeolocation,
allowScrolling: (oldOptions) => oldOptions.allowScrolling,
allowModals: () => undefined,
},
video: {
feedUrl: (oldOptions) => oldOptions.FeedUrl,
@@ -161,6 +162,7 @@ const optionMapping: OptionMapping = {
sectionIndicatorRequirement: (oldOptions) =>
"sectionIndicatorColor" in oldOptions ? oldOptions.sectionIndicatorColor : undefined,
showUptime: () => undefined,
gpu: () => undefined,
visibleClusterSections: (oldOptions) => {
if (!("showNode" in oldOptions)) return undefined;
+1 -1
View File
@@ -23,7 +23,7 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"zod": "^4.2.1"
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -2
View File
@@ -33,8 +33,8 @@
"dayjs": "^1.11.20",
"octokit": "^5.0.5",
"superjson": "2.2.6",
"undici": "7.18.2",
"zod": "^4.2.1"
"undici": "7.24.6",
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -1
View File
@@ -1,5 +1,6 @@
import dayjs from "dayjs";
import type { ContainerInfo, ContainerStats } from "dockerode";
import type Dockerode from "dockerode";
import { db, like, or } from "@homarr/db";
import { icons } from "@homarr/db/schema";
@@ -72,7 +73,7 @@ async function getContainersWithStatsAsync() {
cpuUsage,
memoryUsage,
image: container.Image,
ports: container.Ports,
ports: container.Ports as Dockerode.Port[] | undefined,
};
});
+36
View File
@@ -0,0 +1,36 @@
import dayjs from "dayjs";
import type { IntegrationKindByCategory } from "@homarr/definitions";
import { createIntegrationAsync } from "@homarr/integrations";
import type { Station, Timetable } from "@homarr/integrations/types";
import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-request-handler";
export const timetableSearchStationsRequestHandler = createCachedIntegrationRequestHandler<
Station[],
IntegrationKindByCategory<"timetable">,
{ query: string }
>({
async requestAsync(integration, input) {
const integrationInstance = await createIntegrationAsync(integration);
return await integrationInstance.searchStationsAsync(input.query);
},
queryKey: "timetableSearchStations",
cacheDuration: dayjs.duration(1, "day"),
});
export const timetableGetTimetableRequestHandler = createCachedIntegrationRequestHandler<
Timetable,
IntegrationKindByCategory<"timetable">,
{ stationId: string; limit: number }
>({
async requestAsync(integration, input) {
const integrationInstance = await createIntegrationAsync(integration);
return await integrationInstance.getTimetableAsync({
stationId: input.stationId,
limit: input.limit,
});
},
queryKey: "timetableGetTimetable",
cacheDuration: dayjs.duration(1, "minute"),
});
+1 -1
View File
@@ -26,7 +26,7 @@
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/dates": "^8.3.18",
"next": "16.2.0",
"next": "16.2.1",
"react": "19.2.4",
"react-dom": "19.2.4"
},
+3 -3
View File
@@ -36,9 +36,9 @@
"@mantine/core": "^8.3.18",
"@mantine/hooks": "^8.3.18",
"@mantine/spotlight": "^8.3.18",
"@tabler/icons-react": "^3.40.0",
"jotai": "^2.18.1",
"next": "16.2.0",
"@tabler/icons-react": "^3.41.0",
"jotai": "^2.19.0",
"next": "16.2.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"use-deep-compare-effect": "^1.8.1"
@@ -94,7 +94,6 @@ const createSpotlightContext = (displayName: string) => {
context.unregisterItems(key);
};
// We ignore the results
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...dependencyArray, key]);
};
+1 -1
View File
@@ -32,7 +32,7 @@
"dayjs": "^1.11.20",
"deepmerge": "4.3.1",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.2.0",
"next": "16.2.1",
"next-intl": "4.8.3",
"react": "19.2.4",
"react-dom": "19.2.4"
+22
View File
@@ -1525,6 +1525,17 @@
"offline": ""
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "",
"description": "",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": ""
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": ""
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": ""
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": ""
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": "离线"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "笔记",
"description": "支持 Markdown 的简单笔记本小部件",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "允许地理位置"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "显示内存信息"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "显示运行时间"
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "内存",
"gpu": "",
"network": "网络"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "CPU",
"memory": "内存",
"gpu": "",
"network": "网络",
"up": "上传",
"down": "下载"
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Minecraft 服务状态"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "图标更新"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": "crwdns2494:0crwdne2494:0"
}
},
"timetable": {
"name": "crwdns4000:0crwdne4000:0",
"description": "crwdns4002:0crwdne4002:0",
"title": "crwdns4004:0{station}crwdne4004:0",
"noStation": "crwdns4006:0crwdne4006:0",
"option": {
"station": {
"label": "crwdns4008:0crwdne4008:0"
}
}
},
"notebook": {
"name": "crwdns860:0crwdne860:0",
"description": "crwdns862:0crwdne862:0",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "crwdns970:0crwdne970:0"
},
"allowModals": {
"label": "crwdns3992:0crwdne3992:0"
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "crwdns1080:0crwdne1080:0"
},
"gpu": {
"label": "crwdns3994:0crwdne3994:0"
},
"showUptime": {
"label": "crwdns3446:0crwdne3446:0"
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "crwdns3700:0crwdne3700:0",
"memory": "crwdns3702:0crwdne3702:0",
"gpu": "crwdns3996:0crwdne3996:0",
"network": "crwdns3704:0crwdne3704:0"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "crwdns3604:0crwdne3604:0",
"memory": "crwdns3606:0crwdne3606:0",
"gpu": "crwdns3998:0crwdne3998:0",
"network": "crwdns3608:0crwdne3608:0",
"up": "crwdns3610:0crwdne3610:0",
"down": "crwdns3612:0crwdne3612:0"
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "crwdns2496:0crwdne2496:0"
},
"timetable": {
"label": "crwdns4010:0crwdne4010:0"
},
"iconsUpdater": {
"label": "crwdns1828:0crwdne1828:0"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": ""
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Zápisník",
"description": "",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Povolit polohu"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Zobrazit informace o paměti"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": ""
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": ""
},
+113 -91
View File
@@ -1138,9 +1138,9 @@
"dangerZone": "Farezone",
"noResults": "Ingen resultater fundet",
"unsavedChanges": "Du har ikke gemte ændringer!",
"servers": "",
"applications": "",
"services": "",
"servers": "Servere",
"applications": "Apps",
"services": "Services",
"preview": {
"show": "Vis forhåndsvisning",
"hide": "Skjul forhåndsvisning"
@@ -1525,6 +1525,17 @@
"offline": "Offline"
}
},
"timetable": {
"name": "Tidsskema",
"description": "Viser afgangstider for en station",
"title": "Afgange {station}",
"noStation": "Ingen station valgt",
"option": {
"station": {
"label": "Station"
}
}
},
"notebook": {
"name": "Notesbog",
"description": "En simpel notebook widget, der understøtter markdown",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Tillad geolokalisering"
},
"allowModals": {
"label": "Tillad modaler"
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Vis hukommelsesoplysninger"
},
"gpu": {
"label": "Vis GPU info"
},
"showUptime": {
"label": "Vis Oppetid"
},
@@ -1944,19 +1961,19 @@
"description": "Statistik for dine containere (Denne widget kan kun tilføjes med administratorrettigheder)",
"option": {
"enableRowSorting": {
"label": ""
"label": "Aktiver sortering"
},
"defaultSort": {
"label": "",
"label": "Kolonne brugt til sortering som standard",
"option": {
"name": "",
"state": "",
"cpuUsage": "",
"memoryUsage": ""
"name": "Navn",
"state": "Tilstand",
"cpuUsage": "CPU-forbrug",
"memoryUsage": "Hukommelsesforbrug"
}
},
"descendingDefaultSort": {
"label": ""
"label": "Inverter sortering"
}
},
"error": {
@@ -1964,91 +1981,91 @@
}
},
"coolify": {
"name": "",
"description": "",
"name": "Coolify",
"description": "Oversigt over din Coolify instans med servere og applikationer",
"option": {
"showServers": {
"label": ""
"label": "Vis servere"
},
"showApplications": {
"label": ""
"label": "Vis applikationer"
},
"showServices": {
"label": ""
"label": "Vis services"
}
},
"empty": {
"servers": "",
"applications": "",
"services": ""
"servers": "Ingen servere fundet",
"applications": "Ingen applikationer fundet",
"services": "Ingen services fundet"
},
"footer": {
"updated": ""
"updated": "Opdateret {when}"
},
"error": {
"noIntegration": "",
"internalServerError": ""
"noIntegration": "Ingen Coolify integration konfigureret",
"internalServerError": "Kunne ikke hente Coolify data"
},
"server": {
"buildServer": ""
"buildServer": "Byg"
},
"instances": {
"title": ""
"title": "Instanser"
}
},
"tracearr": {
"name": "",
"description": "",
"name": "Tracearr",
"description": "Forbedret overvågning af medieserver med detaljerede stream og omkodningsdata",
"option": {
"showStreams": {
"label": ""
"label": "Vis aktive streams"
},
"showStats": {
"label": ""
"label": "Vis statistikoversigt"
},
"showRecentActivity": {
"label": ""
"label": "Vis seneste aktivitet"
},
"showViolations": {
"label": ""
"label": "Vis overtrædelser"
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
"activeStreams": "Aktive streams",
"totalUsers": "Antal brugere i alt",
"transcodes": "Transkodninger",
"bandwidth": "Båndbredde"
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"empty": "Ingen aktive streams",
"user": "Bruger",
"media": "Medie",
"state": "Tilstand",
"quality": "Kvalitet",
"device": "Enhed",
"progress": "Fremskridt",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
"directPlay": "Direkte afspilning",
"transcode": "Transkod",
"directStream": "Direkte stream"
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
"title": "Seneste aktivitet",
"empty": "Ingen nylig aktivitet",
"watched": "Sete",
"partial": "Delvis"
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
"title": "Overtrædelser",
"empty": "Ingen nylige overtrædelser",
"rule": "Regel",
"user": "Bruger"
},
"noSectionsEnabled": "",
"noSectionsEnabled": "Ingen sektioner aktiveret",
"error": {
"noIntegration": "",
"internalServerError": ""
"noIntegration": "Ingen Tracearr integration konfigureret",
"internalServerError": "Kunne ikke hente Tracearr data"
}
},
"common": {
@@ -2147,50 +2164,50 @@
}
},
"immich-serverStats": {
"name": "",
"description": "",
"name": "Immich Server Statistik",
"description": "Information om din Immich instans",
"option": {
"showUsers": {
"label": "",
"description": ""
"label": "Vis brugere",
"description": "Vis antal brugere på din instans"
},
"showPhotos": {
"label": "",
"description": ""
"label": "Vis billeder",
"description": "Vis antal billeder på din instans"
},
"showVideos": {
"label": "",
"description": ""
"label": "Vis videoer",
"description": "Vis antal videoer på din instans"
},
"showStorage": {
"label": "",
"description": ""
"label": "Vis lagerplads",
"description": "Vis den samlede mængde brugte lagerplads"
}
},
"users": "",
"photos": "",
"videos": "",
"storage": ""
"users": "Brugere",
"photos": "Billeder",
"videos": "Videoer",
"storage": "Brugt lagerplads"
},
"immich-albumCarousel": {
"name": "",
"name": "Immich album",
"option": {
"albumId": {
"label": "",
"description": ""
"label": "Album ID",
"description": "Dette er et unikt ID som refererer til dit Immich album. Du kan finde dette id ved at åbne et album og kopiere den unikke streng fra URL, for eksempel 'a21aca09-9f05-47dc-b1a1-7b53c547226e'"
},
"rotationIntervalSeconds": {
"label": "",
"description": ""
"label": "Interval i sekunder",
"description": "Skift mellem billeder i albummet hvert X sekunder. Skal være mindst 1. Skal være under 3600."
},
"showPhotoInfo": {
"label": "",
"description": ""
"label": "Vis billedinfo",
"description": "Vis dato for billedet og det aktuelle indeks i bunden af diasshowet"
}
},
"description": "",
"noAlbumSelected": "",
"noPhotos": ""
"description": "Diasshow af billeder i dit Immich album",
"noAlbumSelected": "Intet album valgt, rediger widget og vælg et album",
"noPhotos": "Ingen fotos i dette album"
},
"downloads": {
"name": "Download Klient",
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "Hukommelse",
"gpu": "GPU",
"network": "Netværk"
}
},
@@ -2741,38 +2759,39 @@
"card": {
"cpu": "CPU",
"memory": "MEM",
"gpu": "GPU",
"network": "NET",
"up": "OP",
"down": "NED"
}
},
"systemDisks": {
"name": "",
"description": "",
"name": "System diske",
"description": "Diskforbrug af dit system",
"option": {
"showTemperatureIfAvailable": {
"label": ""
"label": "Vis temperatur, hvis muligt"
},
"displayMode": {
"label": "",
"label": "Visningstilstand",
"option": {
"percentage": {
"label": ""
"label": "Procentdel (f.eks. 76 %)"
},
"absolute": {
"label": ""
"label": "Absolutte værdier (f.eks. 800GB / 1TB)"
},
"free": {
"label": ""
"label": "Fri pladsprocent (fx, 24% fri)"
}
}
},
"showBackgroundBar": {
"label": ""
"label": "Vis forbrugslinje i baggrunden"
}
},
"status": {
"unhealthy": ""
"unhealthy": "Usund"
}
}
},
@@ -3461,10 +3480,10 @@
}
},
"user": {
"title": "",
"title": "Brugere",
"enableGravatar": {
"label": "",
"description": ""
"label": "Aktiver Gravatar",
"description": "Falder tilbage til brugernes avatarer fra Libravatar/Gravatar, når ingen brugerdefineret avatar er valgt og en e-mail er konfigureret"
}
},
"search": {
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Status for Minecraft server"
},
"timetable": {
"label": "Tidsskema"
},
"iconsUpdater": {
"label": "Opdatering Af Ikoner"
},
@@ -3575,7 +3597,7 @@
"label": "Vejr"
},
"tracearr": {
"label": ""
"label": "Tracearr"
}
},
"interval": {
+22
View File
@@ -1525,6 +1525,17 @@
"offline": ""
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Notizbuch",
"description": "Ein einfaches Notizbuch Widget, das Markdown unterstützt",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Geolokalisierung zulassen"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Speicher-Info anzeigen"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Status des Minecraft Servers"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "Symbol Updater"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": "Offline"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Notizbuch",
"description": "Ein einfaches Notizbuch Widget, das Markdown unterstützt",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Geolokalisierung zulassen"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Speicher-Info anzeigen"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "Laufzeit anzeigen"
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "Speicher",
"gpu": "",
"network": "Netzwerk"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "CPU",
"memory": "Arbeitsspeicher",
"gpu": "",
"network": "Netzwerk",
"up": "OBEN",
"down": "UNTEN"
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Status des Minecraft Servers"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "Symbol Updater"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": ""
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Σημειωματάριο",
"description": "",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Επιτρέπεται ο γεωεντοπισμός"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Εμφάνιση Πληροφοριών Μνήμης"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": ""
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": ""
},
+129 -107
View File
@@ -1525,6 +1525,17 @@
"offline": "Offline"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Notebook",
"description": "A simple notebook widget that supports markdown",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Allow geolocation"
},
"allowModals": {
"label": "Allow modals"
}
},
"error": {
@@ -1746,217 +1760,220 @@
"name": "Calendar",
"description": "Display events from your integrations in a calendar view within a certain relative time period",
"duration": {
"allDay": ""
"allDay": "All day"
},
"option": {
"releaseType": {
"label": "",
"label": "Radarr release type",
"options": {
"inCinemas": "",
"digitalRelease": "",
"physicalRelease": ""
"inCinemas": "In cinemas",
"digitalRelease": "Digital release",
"physicalRelease": "Physical release"
}
},
"filterPastMonths": {
"label": ""
"label": "Start from"
},
"filterFutureMonths": {
"label": ""
"label": "End at"
},
"showUnmonitored": {
"label": ""
"label": "Show unmonitored"
}
}
},
"weather": {
"name": "",
"description": "",
"name": "Weather",
"description": "Displays the current weather information of a set location.",
"option": {
"isFormatFahrenheit": {
"label": ""
"label": "Temperature in Fahrenheit"
},
"disableTemperatureDecimals": {
"label": ""
"label": "Disable temperature decimals"
},
"showCurrentWindSpeed": {
"label": "",
"description": ""
"label": "Show current wind speed",
"description": "Only on current weather"
},
"useImperialSpeed": {
"label": ""
"label": "Use mph for windspeed"
},
"location": {
"label": ""
"label": "Weather location"
},
"showCity": {
"label": ""
"label": "Show city"
},
"hasForecast": {
"label": ""
"label": "Show forecast"
},
"forecastDayCount": {
"label": "",
"description": ""
"label": "Amount of forecast days",
"description": "When the widget is not wide enough, less days are shown"
},
"dateFormat": {
"label": "",
"description": ""
"label": "Date Format",
"description": "How the date should look like"
}
},
"currentWindSpeed": "",
"currentWindSpeed": "{currentWindSpeed} {unit}",
"dailyForecast": {
"sunrise": "",
"sunset": "",
"maxWindSpeed": "",
"maxWindGusts": ""
"sunrise": "Sunrise",
"sunset": "Sunset",
"maxWindSpeed": "Max wind speed: {maxWindSpeed} {unit}",
"maxWindGusts": "Max wind gusts: {maxWindGusts} {unit}"
},
"kind": {
"clear": "",
"mainlyClear": "",
"fog": "",
"drizzle": "",
"freezingDrizzle": "",
"clear": "Clear",
"mainlyClear": "Mainly clear",
"fog": "Fog",
"drizzle": "Drizzle",
"freezingDrizzle": "Freezing drizzle",
"rain": "Rain",
"freezingRain": "Freezing rain",
"snowFall": "Snow fall",
"snowGrains": "",
"rainShowers": "",
"snowShowers": "",
"thunderstorm": "",
"thunderstormWithHail": "",
"unknown": ""
"snowGrains": "Snow grains",
"rainShowers": "Rain showers",
"snowShowers": "Snow showers",
"thunderstorm": "Thunderstorm",
"thunderstormWithHail": "Thunderstorm with hail",
"unknown": "Unknown"
}
},
"indexerManager": {
"name": "",
"description": "",
"name": "Indexer manager status",
"description": "Status of your indexers",
"option": {
"openIndexerSiteInNewTab": {
"label": ""
"label": "Open indexer site in new tab"
}
},
"title": "",
"testAll": "",
"title": "Indexer manager",
"testAll": "Test all",
"error": {
"internalServerError": ""
"internalServerError": "Failed to fetch indexers status"
}
},
"healthMonitoring": {
"name": "",
"description": "",
"name": "System Health Monitoring",
"description": "Displays information showing the health and status of your system(s).",
"tab": {
"system": "",
"cluster": ""
"system": "System",
"cluster": "Cluster"
},
"option": {
"fahrenheit": {
"label": ""
"label": "CPU Temp in Fahrenheit"
},
"cpu": {
"label": ""
"label": "Show CPU Info"
},
"memory": {
"label": ""
"label": "Show Memory Info"
},
"gpu": {
"label": "Show GPU Info"
},
"showUptime": {
"label": ""
"label": "Show Uptime"
},
"fileSystem": {
"label": ""
"label": "Show Filesystem Info"
},
"defaultTab": {
"label": ""
"label": "Default tab"
},
"visibleClusterSections": {
"label": ""
"label": "Visible cluster sections"
},
"sectionIndicatorRequirement": {
"label": ""
"label": "Section indicator requirement"
}
},
"popover": {
"information": "",
"processor": "",
"memory": "",
"memoryAvailable": "",
"version": "",
"uptime": "",
"loadAverage": "",
"minute": "",
"minutes": "",
"used": "",
"available": "",
"lastSeen": ""
"information": "Information",
"processor": "Processor: {cpuModelName}",
"memory": "Memory: {memory}GiB",
"memoryAvailable": "Available: {memoryAvailable}GiB ({percent}%)",
"version": "Version: {version}",
"uptime": "Uptime: {months} Months, {days} Days, {hours} Hours, {minutes} Minutes",
"loadAverage": "Load average:",
"minute": "1 minute",
"minutes": "{count} minutes",
"used": "Used",
"available": "Available",
"lastSeen": "Last status update: {lastSeen}"
},
"memory": {},
"error": {
"internalServerError": ""
"internalServerError": "Failed to fetch health status"
},
"cluster": {
"summary": {
"cpu": "",
"memory": ""
"cpu": "CPU",
"memory": "RAM"
},
"resource": {
"node": {
"name": ""
"name": "Nodes"
},
"qemu": {
"name": ""
"name": "VMs"
},
"lxc": {
"name": ""
"name": "LXCs"
},
"storage": {
"name": ""
"name": "Storage"
}
},
"popover": {
"rightSection": {
"node": "",
"vmId": "",
"plugin": ""
"node": "Node",
"vmId": "VM ID",
"plugin": "Plugin"
},
"detail": {
"cpu": "",
"memory": "",
"storage": "",
"uptime": "",
"haState": "",
"cpu": "Cores",
"memory": "Memory",
"storage": "Storage",
"uptime": "Uptime",
"haState": "HA State",
"storageType": {
"local": "",
"shared": ""
"local": "Local storage",
"shared": "Shared storage"
}
}
},
"table": {
"header": {
"name": "",
"cpu": "",
"memory": "",
"node": ""
"name": "Name",
"cpu": "CPU",
"memory": "RAM",
"node": "Node"
}
}
}
},
"dockerContainers": {
"name": "",
"description": "",
"name": "Docker stats",
"description": "Stats of your containers (This widget can only be added with administrator privileges)",
"option": {
"enableRowSorting": {
"label": ""
"label": "Enable items sorting"
},
"defaultSort": {
"label": "",
"label": "Column used for sorting by default",
"option": {
"name": "",
"state": "",
"cpuUsage": "",
"memoryUsage": ""
"name": "Name",
"state": "State",
"cpuUsage": "CPU usage",
"memoryUsage": "Memory usage"
}
},
"descendingDefaultSort": {
"label": ""
"label": "Invert sorting"
}
},
"error": {
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": ""
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": ""
},
@@ -4382,13 +4404,13 @@
"title": "",
"option": {
"documentation": {
"label": ""
"label": "Documentation"
},
"submitIssue": {
"label": ""
"label": "Submit an issue"
},
"discord": {
"label": ""
"label": "Community Discord"
}
}
}
@@ -4397,20 +4419,20 @@
"home": {
"group": {
"search": {
"title": "",
"title": "Search",
"option": {
"other": {
"label": ""
"label": "Search with another search engine"
},
"no-default": {
"label": "",
"description": ""
"label": "No default search engine",
"description": "Set a default search engine in preferences"
},
"search": {
"label": ""
"label": "Search for \"{query}\" with {name}"
},
"from-integration": {
"description": ""
"description": "Start typing to search"
}
}
},
+22 -1
View File
@@ -1525,6 +1525,17 @@
"offline": "Offline"
}
},
"timetable": {
"name": "Timetable",
"description": "Displays departure times for a station",
"title": "Departures {station}",
"noStation": "No station selected",
"option": {
"station": {
"label": "Station"
}
}
},
"notebook": {
"name": "Notebook",
"description": "A simple notebook widget that supports markdown",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Allow geolocation"
},
"allowModals": {
"label": "Allow modals"
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Show Memory Info"
},
"gpu": {
"label": "Show GPU Info"
},
"showUptime": {
"label": "Show Uptime"
},
@@ -2033,7 +2050,6 @@
"directStream": "Direct Stream"
}
},
"recentActivity": {
"title": "Recent Activity",
"empty": "No recent activity",
@@ -2726,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "Memory",
"gpu": "GPU",
"network": "Network"
}
},
@@ -2742,6 +2759,7 @@
"card": {
"cpu": "CPU",
"memory": "MEM",
"gpu": "GPU",
"network": "NET",
"up": "UP",
"down": "DOWN"
@@ -3506,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Minecraft server status"
},
"timetable": {
"label": "Timetable"
},
"iconsUpdater": {
"label": "Icons Updater"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": "Desconectado"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Bloc de notas",
"description": "Un widget de bloc de notas sencillo que soporta Markdown",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Permitir geolocalización"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Mostrar información de la memoria"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "Mostrar tiempo de actividad"
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "Memoria",
"gpu": "",
"network": "Red"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "CPU",
"memory": "MEM",
"gpu": "",
"network": "RED",
"up": "ENCENDIDO",
"down": "APAGADO"
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Estado del Servidor de Minecraft"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "Actualizador de iconos"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": ""
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "",
"description": "",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": ""
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": ""
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": ""
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": ""
},
+61 -39
View File
@@ -27,7 +27,7 @@
"description": "Configurer le comportement d'importation"
},
"boardSelection": {
"title": "Tableaux {count} trouvés",
"title": "{count} tableaux trouvés",
"description": "Choisissez tous les tableaux avec la taille que vous souhaitez importer",
"action": {
"selectAll": "Tout sélectionner",
@@ -114,7 +114,7 @@
},
"invite": {
"title": "Rejoignez Homarr",
"subtitle": "Bienvenue sur Homarr! Veuillez créer votre compte",
"subtitle": "Bienvenue sur Homarr ! Veuillez créer votre compte",
"description": "Vous avez été invité par {username}"
},
"init": {
@@ -177,7 +177,7 @@
}
},
"forgotPassword": {
"label": "Mot de passe oublié?",
"label": "Mot de passe oublié ?",
"description": "Un administrateur peut utiliser la commande suivante pour réinitialiser votre mot de passe :"
}
},
@@ -578,7 +578,7 @@
},
"delete": {
"title": "Supprimer l'app",
"message": "Êtes-vous sûr de vouloir supprimer l'app {name}?",
"message": "Êtes-vous sûr de vouloir supprimer l'app {name} ?",
"notification": {
"success": {
"title": "Suppression effectuée avec succès",
@@ -684,7 +684,7 @@
},
"delete": {
"title": "Supprimer l'intégration",
"message": "Êtes-vous sûr(e) de vouloir supprimer l'intégration {name}?",
"message": "Êtes-vous sûr(e) de vouloir supprimer l'intégration {name} ?",
"notification": {
"success": {
"title": "Suppression réussie",
@@ -742,7 +742,7 @@
"description": "La réponse n'a pas pu être analysée. Veuillez vérifier que l'URL pointe vers l'URL de base du service."
},
"authorization": {
"title": "",
"title": "Erreur d'autorisation",
"description": "La requête n'a pas été autorisée. Veuillez vérifier que les informations d'identification sont correctes et que vous les avez configurées avec suffisamment d'autorisations."
},
"statusCode": {
@@ -794,7 +794,7 @@
"hostnameMismatch": {
"confirm": {
"title": "Faire confiant à l'incohérence du nom d'hôte",
"message": ""
"message": "Êtes-vous sûr de vouloir faire confiance ce certificat avec un nom d'hôte incohérent ?"
},
"notification": {
"success": {
@@ -1164,8 +1164,8 @@
"number": "Ce champ doit être inférieur ou égal à {maximum}"
},
"custom": {
"passwordsDoNotMatch": "Les mots de passe ne correspondent pas.",
"passwordRequirements": "Le mot de passe ne respecte pas les exigences requises.",
"passwordsDoNotMatch": "Les mots de passe ne correspondent pas",
"passwordRequirements": "Le mot de passe ne respecte pas les exigences requises",
"boardAlreadyExists": "Un tableau avec ce nom existe déjà",
"invalidFileType": "Type de fichier invalide, {expected} attendu",
"invalidFileName": "Nom de fichier invalide",
@@ -1248,7 +1248,7 @@
"action": {
"create": "Nouvel élément",
"import": "Importer un élément",
"edit": "Editer l'élément",
"edit": "Éditer l'élément",
"moveResize": "Déplacer / redimensionner l'élément",
"duplicate": "Dupliquer l'élément",
"remove": "Supprimer l'élément"
@@ -1281,7 +1281,7 @@
}
},
"edit": {
"title": "Editer l'élément",
"title": "Éditer l'élément",
"advancedOptions": {
"label": "Options avancées",
"title": "Options avancées de l'élément"
@@ -1309,7 +1309,7 @@
"widget": {
"app": {
"name": "App",
"description": "Intégre une application dans le tableau.",
"description": "Intègre une application dans le tableau.",
"option": {
"appId": {
"label": "Choisir une application"
@@ -1525,6 +1525,17 @@
"offline": "Hors ligne"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Bloc-notes",
"description": "Un simple widget bloc-notes qui supporte le markdown",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Autoriser la géolocalisation"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1634,7 +1648,7 @@
}
},
"smartHome-entityState": {
"name": "Etat de l'entité",
"name": "État de l'entité",
"description": "Affichez l'état d'une entité et optionnellement activez la",
"option": {
"entityId": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Afficher les infos de la mémoire"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "Afficher le temps de disponibilité"
},
@@ -1877,16 +1894,16 @@
"popover": {
"information": "Information",
"processor": "Processeur : {cpuModelName}",
"memory": "Mémoire: {memory}GiB",
"memoryAvailable": "Disponible: {memoryAvailable}GiB ({percent}%)",
"memory": "Mémoire : {memory}GiB",
"memoryAvailable": "Disponible : {memoryAvailable}GiB ({percent}%)",
"version": "Version : {version}",
"uptime": "Disponibilité: {months} Mois, {days} Jours, {hours} Heures, {minutes} Minutes",
"loadAverage": "Charge moyenne:",
"uptime": "Disponibilité : {months} Mois, {days} Jours, {hours} Heures, {minutes} Minutes",
"loadAverage": "Charge moyenne :",
"minute": "1 minute",
"minutes": "{count} minutes",
"used": "Utilisé ",
"available": "Disponible",
"lastSeen": "Dernière mise à jour de statut: {lastSeen}"
"lastSeen": "Dernière mise à jour de statut : {lastSeen}"
},
"memory": {},
"error": {
@@ -1922,7 +1939,7 @@
"memory": "Mémoire",
"storage": "Stockage",
"uptime": "Disponibilité",
"haState": "Etat HA",
"haState": "État HA",
"storageType": {
"local": "Stockage local",
"shared": "Stockage partagé"
@@ -1997,20 +2014,20 @@
}
},
"tracearr": {
"name": "",
"description": "",
"name": "Tracearr",
"description": "Surveillance avancée du serveur multimédia avec des données détaillées de flux et de transcodage",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
"label": "Afficher le résumé des statistiques"
},
"showRecentActivity": {
"label": "Afficher l'activité récente"
},
"showViolations": {
"label": ""
"label": "Afficher les violations"
}
},
"stats": {
@@ -2022,7 +2039,7 @@
"streams": {
"empty": "",
"user": "Utilisateur",
"media": "",
"media": "Média",
"state": "",
"quality": "Qualité",
"device": "Appareil",
@@ -2040,15 +2057,15 @@
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"title": "Violations",
"empty": "Aucune violation récente",
"rule": "Règle",
"user": "Utilisateur"
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
"noIntegration": "Aucune intégration Tracearr configurée",
"internalServerError": "Impossible de récupérer les données de Tracearr"
}
},
"common": {
@@ -2056,7 +2073,7 @@
"query": "Ville / Code Postal",
"latitude": "Latitude",
"longitude": "Longitude",
"disabledTooltip": "Veuillez saisir une ville ou un code postal.",
"disabledTooltip": "Veuillez saisir une ville ou un code postal",
"unknownLocation": "Lieu inconnu",
"search": "Rechercher",
"table": {
@@ -2075,7 +2092,7 @@
}
},
"integration": {
"noData": "Aucune intégration trouvée.",
"noData": "Aucune intégration trouvée",
"description": "Cliquez sur <here></here> pour créer une nouvelle intégration"
},
"app": {
@@ -2091,7 +2108,7 @@
},
"video": {
"name": "Flux vidéo",
"description": "Intégre un flux vidéo ou une vidéo provenant d'une caméra ou d'un site web",
"description": "Intègre un flux vidéo ou une vidéo provenant d'une caméra ou d'un site web",
"option": {
"feedUrl": {
"label": "URL du flux"
@@ -2104,7 +2121,7 @@
"label": "Muet"
},
"hasControls": {
"label": "Afficher les controles"
"label": "Afficher les contrôles"
}
},
"error": {
@@ -2330,7 +2347,7 @@
"pause": "Mettre en pause l'élément",
"resume": "Reprendre l'élément",
"delete": {
"title": "Supprimer l'élement",
"title": "Supprimer l'élément",
"modalTitle": "Êtes-vous sûr de vouloir supprimer cette tâche ?",
"entry": "Supprimer l'entrée",
"entryAndFiles": "Supprimer l'entrée et le(s) fichier(s)"
@@ -2542,7 +2559,7 @@
"allImagesAlreadyImported": "Toutes les images déjà importées",
"onlyAdminCanImport": "Seuls les administrateurs peuvent importer depuis docker",
"selectAll": "Tout sélectionner",
"deselectAll": "Tout déselectionner"
"deselectAll": "Tout désélectionner"
},
"provider": {
"label": "Fournisseur"
@@ -2667,7 +2684,7 @@
},
"error": {
"integrationsDisconnected": "Aucune donnée disponible, toutes les intégrations sont déconnectées",
"unknownContentOption": "Option de contenu inconnue pour le widget d'état du réseau: "
"unknownContentOption": "Option de contenu inconnue pour le widget d'état du réseau : "
},
"name": "État du réseau",
"description": "Afficher les périphériques connectés sur un réseau"
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "Mémoire",
"gpu": "",
"network": "Réseau"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "CPU",
"memory": "RAM",
"gpu": "",
"network": "NET",
"up": "UP",
"down": "HORS SERVICE"
@@ -2757,7 +2776,7 @@
"label": "Mode d'affichage",
"option": {
"percentage": {
"label": "Pourcentage (ex : 70%)"
"label": "Pourcentage (ex : 76%)"
},
"absolute": {
"label": "Valeurs absolues (ex. : 800 Go / 1 To)"
@@ -2965,7 +2984,7 @@
},
"customCss": {
"label": "CSS personnalisé pour ce tableau",
"description": "En outre, vous pouvez personnaliser votre tableau de bord à l'aide de CSS. Réservé aux utilisateurs expérimentés.",
"description": "De plus, personnalisez votre tableau de bord à l'aide de CSS, recommandé uniquement pour les utilisateurs expérimentés",
"customClassesAlert": {
"title": "Classes personnalisées",
"description": "Vous pouvez ajouter des classes personnalisées aux éléments de votre tableau dans les options avancées de chaque élément et les utiliser dans le CSS personnalisé ci-dessus."
@@ -3243,7 +3262,7 @@
"label": "Supprimer définitivement",
"confirm": {
"title": "Supprimer le tableau de bord",
"description": "Êtes-vous sûr de vouloir supprimer le tableau {name}?"
"description": "Êtes-vous sûr de vouloir supprimer le tableau {name} ?"
}
}
},
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "État du serveur Minecraft"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "Mise à jour des icônes"
},
@@ -3575,7 +3597,7 @@
"label": "Météo"
},
"tracearr": {
"label": ""
"label": "Tracearr"
}
},
"interval": {
+22
View File
@@ -1525,6 +1525,17 @@
"offline": "לא מקוון"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "פנקס רשימות",
"description": "ווידג'ט פשוט של מחברת התומך ב-markdown",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "אפשר מיקום גיאוגרפי"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "הצג מידע זיכרון"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "הצג זמן פעולה"
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "מעבד",
"memory": "זיכרון",
"gpu": "",
"network": "רשת"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "מעבד",
"memory": "זיכרון",
"gpu": "",
"network": "רשת",
"up": "העלאה",
"down": "הורדה"
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "סטטוס שרת מיינקראפט"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "עדכון אייקונים"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": ""
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Bilježnica",
"description": "",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Dopusti geolociranje"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": ""
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": ""
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": ""
},
+112 -90
View File
@@ -51,7 +51,7 @@
"title": "Import token megadása",
"field": {
"token": {
"label": "",
"label": "Token",
"description": "Írja be a megjelenített import tokent az előző homarr példányból"
}
},
@@ -153,10 +153,10 @@
"label": "Ikonok használata a pingekhez"
},
"defaultSearchEngine": {
"label": ""
"label": "Alapértelmezett kereső"
},
"openSearchInNewTab": {
"label": ""
"label": "Nyissa meg a keresési eredményeket új ablakban"
}
},
"error": {
@@ -219,10 +219,10 @@
"changeSearchPreferences": {
"notification": {
"success": {
"message": ""
"message": "A keresési beállítások sikeresen megváltoztak"
},
"error": {
"message": ""
"message": "A keresési beállításokat nem sikerült megváltoztatni"
}
}
},
@@ -307,12 +307,12 @@
"name": "Név",
"members": "Tagok",
"homeBoard": {
"label": "",
"description": ""
"label": "Főoldal",
"description": "Csak a csoport számára elérhető oldalak választhatók ki"
},
"mobileBoard": {
"label": "",
"description": ""
"label": "Mobiloldal",
"description": "Csak a csoport számára elérhető oldalak választhatók ki"
}
},
"permission": {
@@ -514,12 +514,12 @@
"board": {
"notification": {
"success": {
"title": "",
"message": ""
"title": "Beállítások elmentve",
"message": "Oldal beállítások sikeresen elmentve"
},
"error": {
"title": "",
"message": ""
"title": "Nem sikerült menteni a beállításokat",
"message": "Az oldalbeállítások nem menthetők"
}
}
}
@@ -527,21 +527,21 @@
"changePosition": {
"notification": {
"success": {
"message": ""
"message": "Pozíció sikeresen megváltozott"
},
"error": {
"message": ""
"message": "Pozíció nem módosítható"
}
}
}
},
"defaultGroup": {
"name": "",
"description": ""
"name": "Alapértelmezett csoport",
"description": "{name} - Minden bejelentkezett felhasználó"
}
},
"app": {
"search": "",
"search": "Alkalmazás megtalálása",
"page": {
"list": {
"title": "Alkalmazások",
@@ -599,12 +599,12 @@
"label": "Leírás"
},
"url": {
"label": ""
"label": "Url"
},
"useDifferentUrlForPing": {
"checkbox": {
"label": "",
"description": ""
"label": "Más URL használata a ping-hez",
"description": "Hasznos, ha a Homarr közvetlenül, egy belső hosztnév vagy hálózaton keresztül fér hozzá, hogy elkerülje az internetszolgáltató által használt sávszélesség terhelését"
}
}
},
@@ -612,17 +612,17 @@
"select": {
"label": "Alkalmazás kiválasztása",
"notFound": "Nem található alkalmazás",
"search": "",
"noResults": "",
"action": "",
"title": ""
"search": "Alkalmazás keresése",
"noResults": "Nincs találat",
"action": "Kiválasztás {app}",
"title": "Válassz egy programot az oldalhoz adáshoz"
},
"create": {
"title": "",
"description": "",
"action": ""
"title": "Új alkalmazás készítése",
"description": "Új alkalmazás létrehozása ",
"action": "Alkalmazás készítő megnyitása"
},
"add": ""
"add": "Alkalmazás hozzáadása"
}
},
"integration": {
@@ -649,14 +649,14 @@
"app": {
"option": {
"existing": {
"title": "",
"label": ""
"title": "Meglévő",
"label": "Meglévő alkalmazás kiválasztása"
},
"new": {
"title": "",
"title": "Új",
"url": {
"label": "",
"description": ""
"label": "Alkalmazás url",
"description": "Az url, amelyre az alkalmazás a műszerfalról történő eléréskor irányít"
}
}
}
@@ -676,9 +676,9 @@
},
"app": {
"action": {
"add": "",
"remove": "",
"select": ""
"add": "Alkalmazás megtalálása",
"remove": "Leválasztás",
"select": "Válasszon ki egy alkalmazást a hivatkozáshoz"
}
}
},
@@ -702,21 +702,21 @@
"label": "Név"
},
"url": {
"label": ""
"label": "Url"
},
"attemptSearchEngineCreation": {
"label": "",
"description": ""
"label": "Keresőmotor létrehozása",
"description": "A \"{kind}\" integráció használható a keresőmotorokkal. Jelölje be ezt a jelölőnégyzetet, ha automatikusan szeretné konfigurálni a keresőmotort."
},
"app": {
"sectionTitle": ""
"sectionTitle": "Hivattkozott alkalmazás"
},
"createApp": {
"label": "",
"description": ""
"label": "Alkalmazás létrehozása",
"description": "Hozzon létre egy alkalmazást, amelynek ugyanaz a neve és ikonja van, mint az integrációnak. Hagyja üresen az alábbi beviteli mezőt, hogy az alkalmazás az integrációs URL-lel jöjjön létre."
},
"appHref": {
"placeholder": ""
"placeholder": "Egyéni alkalmazás URL"
}
},
"action": {
@@ -730,59 +730,59 @@
"error": {
"common": {
"cause": {
"title": ""
"title": "Részletesebb leírás a problémáról"
}
},
"unknown": {
"title": "",
"description": ""
"title": "Ismeretlen hiba",
"description": "Egy ismeretlen hiba történt, nyissa meg a részleteket alul, hogy többet megtudjon"
},
"parse": {
"title": "",
"description": ""
"title": "Elemzési hiba",
"description": "A válasz nem volt elemezhető. Kérjük, ellenőrizze, hogy az URL a szolgáltatás alap URL-jére mutat-e."
},
"authorization": {
"title": "",
"description": ""
"title": "Hitelesítési hiba",
"description": "A kérés nem volt engedélyezett. Kérjük, ellenőrizze, hogy a hitelesítő adatok helyesek-e, és hogy a megfelelő engedélyekkel vannak-e beállítva."
},
"statusCode": {
"title": "",
"description": "",
"otherDescription": "",
"title": "Válaszhiba",
"description": "Váratlan {statusCode} ({reason}) választ kapott a <url></url> címen. Kérjük, ellenőrizze, hogy az URL az integráció alap URL-jére mutat-e.",
"otherDescription": "Váratlan {statusCode} választ kapott a <url></url> címen. Kérjük, ellenőrizze, hogy az URL az integráció alap URL-jére mutat-e.",
"reason": {
"badRequest": "",
"notFound": "",
"tooManyRequests": "",
"internalServerError": "",
"serviceUnavailable": "",
"gatewayTimeout": ""
"badRequest": "Hibás kérés",
"notFound": "Nem található",
"tooManyRequests": "Túl sok kérés",
"internalServerError": "Belső szerverhiba",
"serviceUnavailable": "A szolgáltatás nem elérhető",
"gatewayTimeout": "Átjáró időtúllépés"
}
},
"certificate": {
"title": "",
"title": "Tanúsítványhiba",
"description": {
"expired": "",
"notYetValid": "",
"untrusted": "",
"hostnameMismatch": ""
"expired": "A tanúsítvány lejárt.",
"notYetValid": "A tanúsítvány még nem érvényes.",
"untrusted": "A tanúsítvány nem megbízható.",
"hostnameMismatch": "A tanúsítványban szereplő hosztnév nem egyezik meg az URL-lel."
},
"alert": {
"permission": {
"title": "",
"message": ""
"title": "Nincsenek megfelelő engedélyek",
"message": "Önnek nincs joga tanúsítványokat megbízhatónak nyilvánítani vagy feltölteni. Kérjük, lépjen kapcsolatba a rendszergazdájával, hogy feltölthesse a szükséges gyökér tanúsítványt."
},
"hostnameMismatch": {
"title": "",
"message": ""
"title": "Állomásnév eltérés",
"message": "A tanúsítványban szereplő hosztnév nem egyezik meg azzal a hosztnévvel, amelyhez kapcsolódik. Ez biztonsági kockázatot jelezhet, de mégis dönthet úgy, hogy megbízik ebben a tanúsítványban."
},
"extract": {
"title": "",
"message": ""
"title": "A tanúsító hatóság (CA) tanúsítványának kinyerése sikertelen",
"message": "Csak a lánc nélküli, önaláírt tanúsítványok tölthetők le automatikusan. Ha önaláírt tanúsítványt használ, kérjük, feltétlenül töltse fel manuálisan a tanúsító hatóság (CA) tanúsítványát. Az ehhez szükséges lépések leírását itt találja: <docsLink></docsLink>."
}
},
"action": {
"retry": {
"label": ""
"label": "Újrapróbál"
},
"trust": {
"label": ""
@@ -920,8 +920,8 @@
"message": "Az útvonal valószínűleg nem helyes"
},
"tooManyRequests": {
"title": "",
"message": ""
"title": "Túl sok kérés adott idő alatt",
"message": "Túl sok kérést küldött. Valószínűleg a célrendszer korlátozza vagy elutasítja a kéréseket"
}
}
},
@@ -955,36 +955,36 @@
"newLabel": "Új jelszó"
},
"tokenId": {
"label": "",
"newLabel": ""
"label": "Token azonosító",
"newLabel": "Új token azonosító"
},
"realm": {
"label": "",
"newLabel": ""
"label": "Tartomány",
"newLabel": "Új tartomány"
},
"personalAccessToken": {
"label": "",
"newLabel": ""
"label": "Személyes Hozzáférési Token",
"newLabel": "Új Személyes Hozzáférési Token"
},
"topic": {
"label": "",
"newLabel": ""
"label": "Téma",
"newLabel": "Új téma"
},
"url": {
"label": "",
"newLabel": ""
"label": "Url",
"newLabel": "Új url"
},
"opnsenseApiKey": {
"label": "",
"newLabel": ""
"label": "API kulcs (Kulcs)",
"newLabel": "Új API Kulcs (Kulcs)"
},
"opnsenseApiSecret": {
"label": "",
"newLabel": ""
"label": "Api Kulcs (Titkos)",
"newLabel": "Új API Kulcs (Titkos)"
},
"githubAppId": {
"label": "",
"newLabel": ""
"label": "App Azonosító",
"newLabel": "Új App Azonosító"
},
"githubInstallationId": {
"label": "",
@@ -1525,6 +1525,17 @@
"offline": ""
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Jegyzettömb",
"description": "Egy egyszerű notebook widget, amely támogatja a leértékelést",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Geolokáció engedélyezése"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Memóriainformációk megjelenítése"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": ""
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": ""
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": "Offline"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "Blocco note",
"description": "Un semplice widget notebook che supporta markdown",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "Consenti geo-localizzazione"
},
"allowModals": {
"label": "Consenti modali"
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "Mostra Informazioni Memoria"
},
"gpu": {
"label": "Mostra informazioni sulla GPU"
},
"showUptime": {
"label": "Mostra Uptime"
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "Memoria",
"gpu": "GPU",
"network": "Rete"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "CPU",
"memory": "MEM",
"gpu": "GPU",
"network": "NET",
"up": "SU",
"down": "DOWN"
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Stato del server Minecraft"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "Aggiornatore Icone"
},
+22
View File
@@ -1525,6 +1525,17 @@
"offline": "オフライン"
}
},
"timetable": {
"name": "",
"description": "",
"title": "",
"noStation": "",
"option": {
"station": {
"label": ""
}
}
},
"notebook": {
"name": "メモ帳",
"description": "マークダウンをサポートするシンプルなノートブックウィジェット",
@@ -1625,6 +1636,9 @@
},
"allowGeolocation": {
"label": "位置情報を許可する"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1872,9 @@
"memory": {
"label": "メモリー情報を表示"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "稼働時間を表示"
},
@@ -2725,6 +2742,7 @@
"option": {
"cpu": "CPU",
"memory": "メモリ",
"gpu": "",
"network": "ネットワーク"
}
},
@@ -2741,6 +2759,7 @@
"card": {
"cpu": "CPU",
"memory": "MEM",
"gpu": "",
"network": "NET",
"up": "UP",
"down": "DOWN"
@@ -3505,6 +3524,9 @@
"minecraftServerStatus": {
"label": "Minecraft サーバーの状態"
},
"timetable": {
"label": ""
},
"iconsUpdater": {
"label": "アイコンアップデータ"
},
Loaded 100 of 141 files, more files were not shown because too many files have changed in this diff. Show more