Compare commits

...
150 Commits
Author SHA1 Message Date
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
homarr-renovate[bot] d4e4c9669d fix(deps): update dependency typescript-eslint to ^8.57.1 (#5286)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-25 02:24:41 +00:00
homarr-renovate[bot] 1db20df19c chore(deps): update dependency proxmox-api&gt;undici to v7.24.0 [security] (#5235)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 23:35:41 +00:00
homarr-renovate[bot] feaf022965 fix(deps): update dependency @immich/sdk to ^2.6.1 (#5319)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 21:47:26 +00:00
homarr-renovate[bot] 5b4e6fcc11 fix(deps): update turbo monorepo to ^2.8.20 (#5317)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 20:52:30 +00:00
homarr-renovate[bot] 20a9b3d556 fix(deps): update dependency @immich/sdk to ^2.6.0 (#5318)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 19:21:16 +00:00
homarr-renovate[bot] 47b2d36f7a fix(deps): update dependency ioredis to v5.10.1 (#5316)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 18:05:01 +00:00
homarr-renovate[bot] 2336f9ee5e fix(deps): update dependency fast-xml-parser to ^5.5.7 (#5315)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 18:04:14 +00:00
homarr-renovate[bot] 6fa6ba385e fix(deps): update dependency @tanstack/react-query to ^5.91.2 (#5314)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 11:02:14 +00:00
homarr-renovate[bot] dadf596a31 chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=8.0.1 (#5312)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-24 05:00:20 +00:00
homarr-renovate[bot] 7537184a81 fix(deps): update turbo monorepo to ^2.8.19 (#5310)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-23 22:41:28 +00:00
homarr-update-contributors[bot] 5a81d035cf chore: update contributors (#5277)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-03-23 19:11:50 +00:00
homarr-crowdin[bot] b0b6fbd315 chore(lang): updated translations from crowdin (#5284)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-03-23 20:10:33 +01:00
homarr-renovate[bot] 883161357d fix(deps): update nextjs monorepo to v16.2.0 (#5309)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-23 19:06:43 +00:00
homarr-renovate[bot] 24694b9241 fix(deps): update turbo monorepo to ^2.8.18 (#5308)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-23 18:01:05 +00:00
homarr-renovate[bot] 13b99d021b fix(deps): update dependency @tanstack/react-query to ^5.91.0 (#5305)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-23 15:02:58 +00:00
homarr-renovate[bot] 94dfddf8bd fix(deps): update dependency isomorphic-dompurify to ^3.5.1 (#5300)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 23:35:49 +00:00
homarr-renovate[bot] b6b11c39d2 fix(deps): update dependency isomorphic-dompurify to ^3.5.0 (#5299)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 20:37:26 +00:00
homarr-renovate[bot] 369b4d4863 fix(deps): update tiptap monorepo to v3.20.4 (#5287)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 17:36:05 +00:00
homarr-renovate[bot] 6f8e62963a fix(deps): update mantine monorepo to ^8.3.18 (#5298)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 16:44:42 +00:00
homarr-renovate[bot] 991044ebd7 fix(deps): update dependency fast-xml-parser to ^5.5.6 (#5297)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 16:42:40 +00:00
homarr-renovate[bot] b3f012226b chore(deps): update dependency fast-xml-parser to v5.5.7 [security] (#5295)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 16:34:54 +00:00
homarr-renovate[bot] 63c55cec57 fix(deps): update dependency swagger-ui-react to ^5.32.1 (#5296)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 14:35:44 +00:00
homarr-renovate[bot] c009dd7de6 fix(deps): update testcontainers-node monorepo to ^11.13.0 (#5293)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 10:40:06 +00:00
homarr-renovate[bot] 62764499e7 fix(deps): update dependency isomorphic-dompurify to ^3.4.0 (#5292)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 09:42:54 +00:00
homarr-renovate[bot] 09227a3884 fix(deps): update dependency drizzle-kit to ^0.31.10 (#5291)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 09:41:50 +00:00
homarr-renovate[bot] 37bafaa71a chore(deps): update dependency @types/adm-zip to v0.5.8 (#5288)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 05:00:54 +00:00
homarr-renovate[bot] 237d11d2c0 fix(deps): update dependency @next/eslint-plugin-next to v16.1.7 (#5289)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-22 02:34:01 +00:00
homarr-renovate[bot] 4efe7496a5 chore(deps): update babel monorepo to >=7.29.2 (#5290)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-21 23:37:03 +00:00
homarr-renovate[bot] c3fa4e866c chore(deps): update dependency nanoid@&gt;&#x3d;4.0.0 &lt;5.0.9 to >=5.1.7 (#5282)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-21 15:34:29 +00:00
Meier Lukas 1938798ba1 Merge commit from fork 2026-03-21 16:07:05 +01:00
homarr-renovate[bot] 4fb48c23a2 fix(deps): update dependency mysql2 to v3.20.0 (#5283)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-20 22:37:51 +00:00
homarr-releases[bot] e13afb721e chore: update bug report template (#5281)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-03-20 21:51:13 +01:00
Releases Homarr 3ed31d8f59 chore(release): 1.56.1 [skip ci] 2026-03-20 19:37:26 +00:00
homarr-releases[bot] 1d5b4fd7fb chore(release): automatic release v1.56.1 2026-03-20 19:36:28 +00:00
homarr-renovate[bot] 55cf3c72a3 fix(deps): update tiptap monorepo to v3.20.2 (#5279)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-20 18:59:21 +00:00
homarr-renovate[bot] 9648d9e99f fix(deps): update trpc monorepo to ^11.13.4 (#5278)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-20 14:49:03 +00:00
homarr-renovate[bot] 0e0b857905 fix(deps): update trpc monorepo to ^11.13.3 (#5276)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-20 11:40:40 +00:00
homarr-renovate[bot] 7d93b10032 fix(deps): update trpc monorepo to ^11.13.2 (#5273)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-20 10:45:12 +00:00
homarr-renovate[bot] 6fa9087a11 fix(deps): update mantine monorepo to ^8.3.17 (#5271)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-19 16:04:34 +00:00
homarr-renovate[bot] d2f438f294 fix(deps): update turbo monorepo to ^2.8.17 (#5265)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-19 15:01:48 +00:00
homarr-renovate[bot] 2423ecdce5 fix(deps): update dependency fast-xml-parser to ^5.5.5 (#5270)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-19 15:01:24 +00:00
homarr-renovate[bot] 7094110da2 chore(deps): update dependency fast-xml-parser to v5.5.6 [security] (#5269)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-19 14:52:36 +00:00
homarr-renovate[bot] 72ec253644 chore(deps): update dependency hono@&lt;4.6.5 to >=4.12.8 (#5268)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-19 09:51:42 +00:00
homarr-renovate[bot] 3bbde3cbb8 fix(deps): update dependency next to v16.1.7 [security] (#5263)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 21:41:38 +00:00
homarr-renovate[bot] 6f85b2161f chore(deps): update vitest monorepo to ^4.1.0 (#5259)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 19:27:32 +00:00
homarr-renovate[bot] c85910d24f chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to v8 (#5261)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 19:55:32 +01:00
homarr-renovate[bot] 397c1d3dbc chore(deps): update dependency esbuild to ^0.27.4 (#5256)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 19:53:21 +01:00
homarr-renovate[bot] 078fb6de93 chore(deps): update dependency esbuild@&lt;&#x3d;0.24.2 to >=0.27.4 (#5257)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 19:53:15 +01:00
homarr-crowdin[bot] 8159b4f146 chore(lang): update translations from crowdin (#5248)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-03-17 19:49:40 +01:00
homarr-renovate[bot] e8f6876d24 fix(deps): update dependency isomorphic-dompurify to ^3.3.0 (#5252)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 16:22:11 +00:00
homarr-renovate[bot] 615062c6b1 fix(deps): update dependency dayjs to ^1.11.20 (#5253)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 16:20:32 +00:00
homarr-renovate[bot] d9294ed4bf chore(deps): update dependency @vitejs/plugin-react to ^5.2.0 (#5258)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-17 15:06:30 +00:00
homarr-renovate[bot] d1d8659f95 fix(deps): update dependency fast-xml-parser to ^5.5.3 (#5251)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-16 17:09:31 +00:00
homarr-renovate[bot] ac80008ce0 fix(deps): update turbo monorepo to ^2.8.16 (#5250)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-16 02:42:50 +00:00
homarr-renovate[bot] 13853f2981 chore(deps): update pnpm to v10.32.1 (#5249)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-16 02:41:02 +00:00
homarr-renovate[bot] f995cba157 fix(deps): update dependency sass to ^1.98.0 (#5247)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-15 23:38:23 +00:00
homarr-renovate[bot] 91a9b6bec9 fix(deps): update dependency fast-xml-parser to ^5.5.1 (#5246)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-15 21:37:58 +00:00
homarr-crowdin[bot]andMeier Lukas 314ab3a28e 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-03-15 21:55:12 +01:00
homarr-renovate[bot] 7841b58f0f fix(deps): update dependency typescript-eslint to ^8.57.0 (#5241)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-14 23:37:16 +00:00
homarr-renovate[bot] 63f91b6352 fix(deps): update dependency isomorphic-dompurify to ^3.1.0 (#5239)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-14 23:36:12 +00:00
homarr-renovate[bot] 4fd8ac9118 chore(deps): update pnpm to v10.32.0 (#5244)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-14 22:36:25 +00:00
homarr-renovate[bot] 1bd924ea99 fix(deps): update turbo monorepo to ^2.8.15 (#5243)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-14 22:35:44 +00:00
homarr-renovate[bot] 1040ecac04 fix(deps): update dependency mysql2 to v3.19.1 (#5240)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-14 16:41:47 +00:00
homarr-renovate[bot] 729b49ffa9 fix(deps): update dependency jotai to ^2.18.1 (#5238)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-03-14 15:35:15 +00:00
homarr-crowdin[bot]andMeier Lukas dbf5b375f5 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-03-14 12:58:16 +00:00
Meier Lukas 7e8723ce57 chore(docs): remove temporary ts-expect-error for documentation-url 2026-03-14 13:53:07 +01:00
homarr-releases[bot] c5e83943a4 chore: update bug report template (#5234)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-03-14 08:54:18 +01:00
144 changed files with 6359 additions and 3091 deletions

No files matched your search

+4
View File
@@ -33,6 +33,10 @@ body:
options:
# The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION#
- 1.57.1
- 1.57.0
- 1.56.1
- 1.56.0
- 1.55.0
- 1.54.0
- 1.53.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
+112
View File
@@ -1,3 +1,115 @@
## [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
* **deps:** update dependency dayjs to ^1.11.20 ([#5253](https://github.com/homarr-labs/homarr/issues/5253)) ([615062c](https://github.com/homarr-labs/homarr/commit/615062c6b141fa1e4bc91d2fab505fa1652608d2))
* **deps:** update dependency fast-xml-parser to ^5.5.1 ([#5246](https://github.com/homarr-labs/homarr/issues/5246)) ([91a9b6b](https://github.com/homarr-labs/homarr/commit/91a9b6bec9438e9a812960b829cea343e8478c63))
* **deps:** update dependency fast-xml-parser to ^5.5.3 ([#5251](https://github.com/homarr-labs/homarr/issues/5251)) ([d1d8659](https://github.com/homarr-labs/homarr/commit/d1d8659f951cdd22bf4a22b407a4f3027f036ec2))
* **deps:** update dependency fast-xml-parser to ^5.5.5 ([#5270](https://github.com/homarr-labs/homarr/issues/5270)) ([2423ecd](https://github.com/homarr-labs/homarr/commit/2423ecdce57353dd50d678444da800c02a246223))
* **deps:** update dependency isomorphic-dompurify to ^3.1.0 ([#5239](https://github.com/homarr-labs/homarr/issues/5239)) ([63f91b6](https://github.com/homarr-labs/homarr/commit/63f91b6352e9ba58a0823434d86915371f1c1d30))
* **deps:** update dependency isomorphic-dompurify to ^3.3.0 ([#5252](https://github.com/homarr-labs/homarr/issues/5252)) ([e8f6876](https://github.com/homarr-labs/homarr/commit/e8f6876d2477fb0e1936006b81f7324b90b6eb33))
* **deps:** update dependency jotai to ^2.18.1 ([#5238](https://github.com/homarr-labs/homarr/issues/5238)) ([729b49f](https://github.com/homarr-labs/homarr/commit/729b49ffa9b1f91ebd7bcd2300febba4d9d0fbb9))
* **deps:** update dependency mysql2 to v3.19.1 ([#5240](https://github.com/homarr-labs/homarr/issues/5240)) ([1040eca](https://github.com/homarr-labs/homarr/commit/1040ecac04fd0b10e101495d5230a3fa4898097a))
* **deps:** update dependency next to v16.1.7 [security] ([#5263](https://github.com/homarr-labs/homarr/issues/5263)) ([3bbde3c](https://github.com/homarr-labs/homarr/commit/3bbde3cbb8eb3aa528485f17d40817cb982a8687))
* **deps:** update dependency sass to ^1.98.0 ([#5247](https://github.com/homarr-labs/homarr/issues/5247)) ([f995cba](https://github.com/homarr-labs/homarr/commit/f995cba157c9954b6f266bd5b2bb8d60a08f51f1))
* **deps:** update dependency typescript-eslint to ^8.57.0 ([#5241](https://github.com/homarr-labs/homarr/issues/5241)) ([7841b58](https://github.com/homarr-labs/homarr/commit/7841b58f0fdd4a4494b6f3d61881a1efb4c9eba3))
* **deps:** update mantine monorepo to ^8.3.17 ([#5271](https://github.com/homarr-labs/homarr/issues/5271)) ([6fa9087](https://github.com/homarr-labs/homarr/commit/6fa9087a119334dee63e4ca4ed51276b25c4564a))
* **deps:** update tiptap monorepo to v3.20.2 ([#5279](https://github.com/homarr-labs/homarr/issues/5279)) ([55cf3c7](https://github.com/homarr-labs/homarr/commit/55cf3c72a3d9cd9f702e802716d549d7d2644105))
* **deps:** update trpc monorepo to ^11.13.2 ([#5273](https://github.com/homarr-labs/homarr/issues/5273)) ([7d93b10](https://github.com/homarr-labs/homarr/commit/7d93b1003245ab89fdba7175b0b4f745041cd1fe))
* **deps:** update trpc monorepo to ^11.13.3 ([#5276](https://github.com/homarr-labs/homarr/issues/5276)) ([0e0b857](https://github.com/homarr-labs/homarr/commit/0e0b857905a7b5196dbf529520f86467aa94d8ce))
* **deps:** update trpc monorepo to ^11.13.4 ([#5278](https://github.com/homarr-labs/homarr/issues/5278)) ([9648d9e](https://github.com/homarr-labs/homarr/commit/9648d9e99f02be8ba16a5238bd47866f7303724d))
* **deps:** update turbo monorepo to ^2.8.15 ([#5243](https://github.com/homarr-labs/homarr/issues/5243)) ([1bd924e](https://github.com/homarr-labs/homarr/commit/1bd924ea9917949ac64ab754d97964b12c083b82))
* **deps:** update turbo monorepo to ^2.8.16 ([#5250](https://github.com/homarr-labs/homarr/issues/5250)) ([ac80008](https://github.com/homarr-labs/homarr/commit/ac80008ce0796cb6927642c83ff2e667f6e9d24c))
* **deps:** update turbo monorepo to ^2.8.17 ([#5265](https://github.com/homarr-labs/homarr/issues/5265)) ([d2f438f](https://github.com/homarr-labs/homarr/commit/d2f438f294df3093b249213ac0a018b770705e3b))
## [1.56.0](https://github.com/homarr-labs/homarr/compare/v1.55.0...v1.56.0) (2026-03-13)
### Features
+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
+21 -21
View File
@@ -48,46 +48,46 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"@mantine/colors-generator": "^8.3.16",
"@mantine/core": "^8.3.16",
"@mantine/dropzone": "^8.3.16",
"@mantine/hooks": "^8.3.16",
"@mantine/modals": "^8.3.16",
"@mantine/tiptap": "^8.3.16",
"@mantine/colors-generator": "^8.3.18",
"@mantine/core": "^8.3.18",
"@mantine/dropzone": "^8.3.18",
"@mantine/hooks": "^8.3.18",
"@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.90.21",
"@tanstack/react-query-devtools": "^5.91.3",
"@tanstack/react-query-next-experimental": "^5.91.0",
"@trpc/client": "^11.12.0",
"@trpc/next": "^11.12.0",
"@trpc/react-query": "^11.12.0",
"@trpc/server": "^11.12.0",
"@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",
"babel-plugin-react-compiler": "^1.0.0",
"chroma-js": "^3.2.0",
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"dayjs": "^1.11.20",
"dotenv": "^17.3.1",
"flag-icons": "^7.5.0",
"glob": "^13.0.6",
"isomorphic-dompurify": "^3.0.0",
"jotai": "^2.18.0",
"isomorphic-dompurify": "^3.7.1",
"jotai": "^2.19.0",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.1.6",
"next": "16.2.1",
"postcss-preset-mantine": "^1.18.0",
"prismjs": "^1.30.0",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-error-boundary": "^6.1.1",
"react-simple-code-editor": "^0.14.1",
"sass": "^1.97.3",
"sass": "^1.98.0",
"superjson": "2.2.6",
"swagger-ui-react": "^5.32.0",
"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]);
/**
+4 -4
View File
@@ -37,11 +37,11 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"dayjs": "^1.11.19",
"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",
@@ -49,7 +49,7 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node": "^24.12.0",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.3",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"tsx": "4.20.4",
+2 -2
View File
@@ -27,14 +27,14 @@
"@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",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/ws": "^8.18.1",
"esbuild": "^0.27.3",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
+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.31.0",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">=24.14.0",
"pnpm": ">=10.31.0"
"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/>
+25 -25
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.56.0",
"version": "1.58.0",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -42,27 +42,27 @@
"@semantic-release/github": "^12.0.6",
"@semantic-release/npm": "^13.1.5",
"@semantic-release/release-notes-generator": "^14.1.0",
"@testcontainers/redis": "^11.12.0",
"@turbo/gen": "^2.8.14",
"@vitejs/plugin-react": "^5.1.4",
"@vitest/coverage-v8": "^4.0.18",
"@vitest/ui": "^4.0.18",
"conventional-changelog-conventionalcommits": "^9.3.0",
"@testcontainers/redis": "^11.13.0",
"@turbo/gen": "^2.8.21",
"@vitejs/plugin-react": "^5.2.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.12.0",
"turbo": "^2.8.14",
"testcontainers": "^11.13.0",
"turbo": "^2.8.21",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.0.18"
"vitest": "^4.1.2"
},
"packageManager": "pnpm@10.31.0",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">=24.14.0",
"pnpm": ">=10.31.0"
"node": ">=24.14.1",
"pnpm": ">=10.33.0"
},
"pnpm": {
"onlyBuiltDependencies": [
@@ -77,25 +77,25 @@
"tree-sitter-json"
],
"overrides": {
"@babel/helpers@<7.26.10": ">=7.28.6",
"@babel/runtime@<7.26.10": ">=7.28.6",
"axios@>=1.0.0 <1.8.2": ">=1.13.6",
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.4",
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.4",
"esbuild@<=0.24.2": ">=0.27.3",
"@babel/helpers@<7.26.10": ">=7.29.2",
"@babel/runtime@<7.26.10": ">=7.29.2",
"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.7",
"hono@<4.6.5": ">=4.12.9",
"linkifyjs@<4.3.2": ">=4.3.2",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.7",
"prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.19.2",
"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": ">=7.3.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.90.21",
"@trpc/client": "^11.12.0",
"@trpc/react-query": "^11.12.0",
"@trpc/server": "^11.12.0",
"@trpc/tanstack-react-query": "^11.12.0",
"@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.1.6",
"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;
+53 -6
View File
@@ -1,11 +1,13 @@
import { TRPCError } from "@trpc/server";
import type { MySqlRawQueryResult } from "drizzle-orm/mysql2";
import type { QueryResult } from "pg";
import { z } from "zod/v4";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { createId } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Database } from "@homarr/db";
import { and, eq, like } from "@homarr/db";
import { and, eq, handleTransactionsAsync, like } from "@homarr/db";
import { getMaxGroupPositionAsync } from "@homarr/db/queries";
import { boards, groupMembers, groupPermissions, groups, invites, users } from "@homarr/db/schema";
import { selectUserSchema } from "@homarr/db/validationSchemas";
@@ -90,15 +92,60 @@ export const userRouter = createTRPCRouter({
await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.username);
await createUserAsync(ctx.db, input);
const salt = await createSaltAsync();
const hashedPassword = await hashPasswordAsync(input.password, salt);
// Delete invite as it's used
await ctx.db.delete(invites).where(inviteWhere);
const userId = createId();
const user = {
id: userId,
name: input.username,
password: hashedPassword,
salt,
};
await handleTransactionsAsync(ctx.db, {
async handleAsync(db, schema) {
await db.transaction(async (trx) => {
await trx.insert(schema.users).values(user);
// Delete invite as it's used
const queryResult = (await trx.delete(schema.invites).where(inviteWhere)) as
| QueryResult
| MySqlRawQueryResult;
let count = 0;
if (Array.isArray(queryResult)) {
count = queryResult[0].affectedRows;
} else {
count = queryResult.rowCount ?? 0;
}
if (count === 0) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Invalid invite",
});
}
});
},
handleSync(db) {
db.transaction((trx) => {
trx.insert(users).values(user).run();
// Delete invite as it's used
const result = trx.delete(invites).where(inviteWhere).run();
if (result.changes === 0) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Invalid invite",
});
}
});
},
});
}),
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();
@@ -329,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.1.6",
"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",
+1 -1
View File
@@ -36,7 +36,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"esbuild": "^0.27.3",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
}
+4 -4
View File
@@ -29,13 +29,13 @@
"@homarr/core": "workspace:^0.1.0",
"@paralleldrive/cuid2": "^3.3.0",
"cookie": "1.1.1",
"dayjs": "^1.11.19",
"next": "16.1.6",
"dayjs": "^1.11.20",
"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": {
+7 -7
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",
"ioredis": "5.10.0",
"mysql2": "3.19.0",
"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.90.21",
"@trpc/client": "^11.12.0",
"@trpc/server": "^11.12.0",
"@trpc/tanstack-react-query": "^11.12.0",
"@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,
}
: [],
},
}),
);
+9 -9
View File
@@ -47,16 +47,16 @@
"@homarr/core": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/core": "^8.3.16",
"@mantine/core": "^8.3.18",
"@paralleldrive/cuid2": "^3.3.0",
"@testcontainers/mysql": "^11.12.0",
"@testcontainers/postgresql": "^11.12.0",
"better-sqlite3": "^12.6.2",
"@testcontainers/mysql": "^11.13.0",
"@testcontainers/postgresql": "^11.13.0",
"better-sqlite3": "^12.8.0",
"dotenv": "^17.3.1",
"drizzle-kit": "^0.31.9",
"drizzle-orm": "^0.45.1",
"drizzle-kit": "^0.31.10",
"drizzle-orm": "^0.45.2",
"drizzle-zod": "^0.8.3",
"mysql2": "3.19.0",
"mysql2": "3.20.0",
"pg": "^8.20.0",
"superjson": "2.2.6"
},
@@ -65,9 +65,9 @@
"@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.3",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"tsx": "4.20.4",
+2 -2
View File
@@ -24,8 +24,8 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"fast-xml-parser": "^5.4.2",
"zod": "^4.2.1"
"fast-xml-parser": "^5.5.9",
"zod": "^4.3.6"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -109,7 +109,9 @@ export type HomarrDocumentationPath =
| "/docs/tags/variables"
| "/docs/advanced/command-line"
| "/docs/advanced/command-line/fix-usernames"
| "/docs/advanced/command-line/integrations"
| "/docs/advanced/command-line/password-recovery"
| "/docs/advanced/command-line/users"
| "/docs/advanced/development/getting-started"
| "/docs/advanced/development/kubernetes"
| "/docs/advanced/environment-variables"
@@ -164,6 +166,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/glances"
| "/docs/integrations/home-assistant"
| "/docs/integrations/ical"
| "/docs/integrations/immich"
| "/docs/integrations/jellyfin"
| "/docs/integrations/jellyseerr"
| "/docs/integrations/kubernetes"
@@ -189,6 +192,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/slskd"
| "/docs/integrations/sonarr"
| "/docs/integrations/tdarr"
| "/docs/integrations/tracearr"
| "/docs/integrations/transmission"
| "/docs/integrations/truenas"
| "/docs/integrations/unifi-controller"
@@ -214,6 +218,8 @@ export type HomarrDocumentationPath =
| "/docs/widgets/firewall"
| "/docs/widgets/health-monitoring"
| "/docs/widgets/iframe"
| "/docs/widgets/immich-album-carousel"
| "/docs/widgets/immich-server-stats"
| "/docs/widgets/indexer-manager"
| "/docs/widgets/media-releases"
| "/docs/widgets/media-request-list"
@@ -231,6 +237,7 @@ export type HomarrDocumentationPath =
| "/docs/widgets/smart-home-execute-automation"
| "/docs/widgets/stock-price"
| "/docs/widgets/system-resources"
| "/docs/widgets/tracearr"
| "/docs/widgets/video"
| "/docs/widgets/weather"
| ""
+11 -3
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,12 +334,20 @@ 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"],
// @ts-expect-error - docs page will be created when integration is merged
documentationUrl: createDocumentationLink("/docs/integrations/search-ch"),
},
immich: {
name: "Immich",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/immich.svg",
category: ["photoService"],
// @ts-expect-error TS2345
documentationUrl: createDocumentationLink("/docs/integrations/immich"),
},
tracearr: {
@@ -347,7 +355,6 @@ export const integrationDefs = {
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/tracearr.svg",
category: ["mediaMonitoring"],
// @ts-expect-error - docs page will be created when integration is merged
documentationUrl: createDocumentationLink("/docs/integrations/tracearr"),
},
// This integration only returns mock data, it is used during development (but can also be used in production by directly going to the create page)
@@ -436,6 +443,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",
+2 -2
View File
@@ -26,9 +26,9 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/form": "^8.3.16",
"@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",
+2 -2
View File
@@ -30,9 +30,9 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.16",
"@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.5.6",
"@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");
}
+5 -5
View File
@@ -33,13 +33,13 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.16",
"@tabler/icons-react": "^3.40.0",
"dayjs": "^1.11.19",
"next": "16.1.6",
"@mantine/core": "^8.3.18",
"@tabler/icons-react": "^3.41.0",
"dayjs": "^1.11.20",
"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}`;
}
+2 -2
View File
@@ -24,8 +24,8 @@
"dependencies": {
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.3.16",
"@mantine/hooks": "^8.3.16",
"@mantine/core": "^8.3.18",
"@mantine/hooks": "^8.3.18",
"react": "19.2.4"
},
"devDependencies": {
+2 -2
View File
@@ -24,8 +24,8 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/ui": "workspace:^0.1.0",
"@mantine/notifications": "^8.3.16",
"@tabler/icons-react": "^3.40.0"
"@mantine/notifications": "^8.3.18",
"@tabler/icons-react": "^3.41.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+5 -5
View File
@@ -37,21 +37,21 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.16",
"@mantine/hooks": "^8.3.16",
"@mantine/core": "^8.3.18",
"@mantine/hooks": "^8.3.18",
"adm-zip": "0.5.16",
"next": "16.1.6",
"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": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/adm-zip": "0.5.7",
"@types/adm-zip": "0.5.8",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
}
@@ -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",
+1 -1
View File
@@ -26,7 +26,7 @@
"@homarr/core": "workspace:^",
"@homarr/db": "workspace:^",
"@homarr/definitions": "workspace:^",
"ioredis": "5.10.0",
"ioredis": "5.10.1",
"superjson": "2.2.6"
},
"devDependencies": {
+3 -3
View File
@@ -30,11 +30,11 @@
"@homarr/docker": "workspace:^0.1.0",
"@homarr/integrations": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"dayjs": "^1.11.19",
"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"),
});
+2 -2
View File
@@ -25,8 +25,8 @@
"dependencies": {
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/dates": "^8.3.16",
"next": "16.1.6",
"@mantine/dates": "^8.3.18",
"next": "16.2.1",
"react": "19.2.4",
"react-dom": "19.2.4"
},
+6 -6
View File
@@ -33,12 +33,12 @@
"@homarr/settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.3.16",
"@mantine/hooks": "^8.3.16",
"@mantine/spotlight": "^8.3.16",
"@tabler/icons-react": "^3.40.0",
"jotai": "^2.18.0",
"next": "16.1.6",
"@mantine/core": "^8.3.18",
"@mantine/hooks": "^8.3.18",
"@mantine/spotlight": "^8.3.18",
"@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]);
};
+2 -2
View File
@@ -29,10 +29,10 @@
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"dayjs": "^1.11.19",
"dayjs": "^1.11.20",
"deepmerge": "4.3.1",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.1.6",
"next": "16.2.1",
"next-intl": "4.8.3",
"react": "19.2.4",
"react-dom": "19.2.4"
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": ""
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": ""
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": ""
},
"tracearr": {
"label": ""
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "允许地理位置"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "显示内存信息"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "显示运行时间"
},
@@ -1996,6 +2002,61 @@
"title": "实例"
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "城市/邮编",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "CPU",
"memory": "内存",
"gpu": "",
"network": "网络"
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "CPU",
"memory": "内存",
"gpu": "",
"network": "网络",
"up": "上传",
"down": "下载"
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": "天气"
},
"tracearr": {
"label": ""
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "crwdns970:0crwdne970:0"
},
"allowModals": {
"label": "crwdns3992:0crwdne3992:0"
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "crwdns1080:0crwdne1080:0"
},
"gpu": {
"label": "crwdns3994:0crwdne3994:0"
},
"showUptime": {
"label": "crwdns3446:0crwdne3446:0"
},
@@ -1996,6 +2002,61 @@
"title": "crwdns3876:0crwdne3876:0"
}
},
"tracearr": {
"name": "crwdns3928:0crwdne3928:0",
"description": "crwdns3930:0crwdne3930:0",
"option": {
"showStreams": {
"label": "crwdns3932:0crwdne3932:0"
},
"showStats": {
"label": "crwdns3934:0crwdne3934:0"
},
"showRecentActivity": {
"label": "crwdns3936:0crwdne3936:0"
},
"showViolations": {
"label": "crwdns3938:0crwdne3938:0"
}
},
"stats": {
"activeStreams": "crwdns3940:0crwdne3940:0",
"totalUsers": "crwdns3942:0crwdne3942:0",
"transcodes": "crwdns3944:0crwdne3944:0",
"bandwidth": "crwdns3946:0crwdne3946:0"
},
"streams": {
"empty": "crwdns3948:0crwdne3948:0",
"user": "crwdns3950:0crwdne3950:0",
"media": "crwdns3952:0crwdne3952:0",
"state": "crwdns3954:0crwdne3954:0",
"quality": "crwdns3956:0crwdne3956:0",
"device": "crwdns3958:0crwdne3958:0",
"progress": "crwdns3960:0crwdne3960:0",
"videoDecision": {
"directPlay": "crwdns3962:0crwdne3962:0",
"transcode": "crwdns3964:0crwdne3964:0",
"directStream": "crwdns3966:0crwdne3966:0"
}
},
"recentActivity": {
"title": "crwdns3968:0crwdne3968:0",
"empty": "crwdns3970:0crwdne3970:0",
"watched": "crwdns3972:0crwdne3972:0",
"partial": "crwdns3974:0crwdne3974:0"
},
"violations": {
"title": "crwdns3976:0crwdne3976:0",
"empty": "crwdns3978:0crwdne3978:0",
"rule": "crwdns3980:0crwdne3980:0",
"user": "crwdns3982:0crwdne3982:0"
},
"noSectionsEnabled": "crwdns3984:0crwdne3984:0",
"error": {
"noIntegration": "crwdns3986:0crwdne3986:0",
"internalServerError": "crwdns3988:0crwdne3988:0"
}
},
"common": {
"location": {
"query": "crwdns1110:0crwdne1110:0",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "crwdns3700:0crwdne3700:0",
"memory": "crwdns3702:0crwdne3702:0",
"gpu": "crwdns3996:0crwdne3996:0",
"network": "crwdns3704:0crwdne3704:0"
}
},
@@ -2686,6 +2748,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"
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": "crwdns3724:0crwdne3724:0"
},
"tracearr": {
"label": "crwdns3990:0crwdne3990:0"
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Povolit polohu"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "Zobrazit informace o paměti"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": ""
},
"tracearr": {
"label": ""
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Tillad geolokalisering"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "Vis hukommelsesoplysninger"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "Vis Oppetid"
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "By / Postnummer",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "CPU",
"memory": "Hukommelse",
"gpu": "",
"network": "Netværk"
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "CPU",
"memory": "MEM",
"gpu": "",
"network": "NET",
"up": "OP",
"down": "NED"
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": "Vejr"
},
"tracearr": {
"label": ""
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Geolokalisierung zulassen"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "Speicher-Info anzeigen"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "Stadt / Postleitzahl",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": ""
},
"tracearr": {
"label": ""
}
},
"interval": {
+67 -1
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Geolokalisierung zulassen"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "Speicher-Info anzeigen"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "Laufzeit anzeigen"
},
@@ -1996,6 +2002,61 @@
"title": "Instanzen"
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": "Aktive Streams anzeigen"
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "Aktive Streams",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "Stadt / Postleitzahl",
@@ -2096,7 +2157,7 @@
"description": "",
"option": {
"showUsers": {
"label": "",
"label": "Benutzer anzeigen",
"description": ""
},
"showPhotos": {
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "CPU",
"memory": "Speicher",
"gpu": "",
"network": "Netzwerk"
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "CPU",
"memory": "Arbeitsspeicher",
"gpu": "",
"network": "Netzwerk",
"up": "OBEN",
"down": "UNTEN"
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": "Wetter"
},
"tracearr": {
"label": ""
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Επιτρέπεται ο γεωεντοπισμός"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "Εμφάνιση Πληροφοριών Μνήμης"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": ""
},
"tracearr": {
"label": ""
}
},
"interval": {
+254 -188
View File
@@ -1125,17 +1125,17 @@
"userAvatar": {
"menu": {
"switchToDarkMode": "Switch to dark mode",
"switchToLightMode": "Switch to light mode",
"management": "Management",
"preferences": "Your preferences",
"logout": "Log-out",
"switchToLightMode": "التبديل إلى الوضع الفاتح",
"management": "الإدارة",
"preferences": "تفضيلاتك",
"logout": "تسجيل الخروج",
"login": "Log-in",
"homeBoard": "Your home board",
"homeBoard": "لوحة منزلك",
"loggedOut": "Logged out",
"updateAvailable": "New update available: {tag}"
"updateAvailable": "{countUpdates} يتوفر تحديث جديد: {tag}"
}
},
"dangerZone": "Danger zone",
"dangerZone": "المنطقة الخطرة",
"noResults": "No results have been found",
"unsavedChanges": "You have unsaved changes!",
"servers": "Servers",
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Allow geolocation"
},
"allowModals": {
"label": "Allow modals"
}
},
"error": {
@@ -1689,274 +1692,277 @@
"label": "3 Months"
},
"6mo": {
"label": ""
"label": "6 Months"
},
"ytd": {
"label": ""
"label": "Year to Date"
},
"1y": {
"label": ""
"label": "1 Year"
},
"2y": {
"label": ""
"label": "2 Years"
},
"5y": {
"label": ""
"label": "5 Years"
},
"10y": {
"label": ""
"label": "10 Years"
},
"max": {
"label": ""
"label": "Max"
}
}
},
"timeInterval": {
"label": "",
"label": "Time Interval",
"option": {
"5m": {
"label": ""
"label": "5 Minutes"
},
"15m": {
"label": ""
"label": "15 Minutes"
},
"30m": {
"label": ""
"label": "30 Minutes"
},
"1h": {
"label": ""
"label": "1 Hour"
},
"1d": {
"label": ""
"label": "1 Day"
},
"5d": {
"label": ""
"label": "5 Days"
},
"1wk": {
"label": ""
"label": "1 Week"
},
"1mo": {
"label": ""
"label": "1 Month"
}
}
}
}
},
"calendar": {
"name": "",
"description": "",
"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": {
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3094,98 +3157,98 @@
},
"navbar": {
"items": {
"home": "",
"boards": "",
"apps": "",
"integrations": "",
"searchEngies": "",
"medias": "",
"home": "الصفحة الرئسية",
"boards": "الألواح",
"apps": "التطبيقات",
"integrations": "التكاملات",
"searchEngies": "محركات البحث",
"medias": "الوسائط",
"users": {
"label": "",
"label": "المستخدمين",
"items": {
"manage": "",
"invites": "",
"groups": ""
"manage": "إدارة المستخدمين",
"invites": "الدعوات",
"groups": "المجموعات"
}
},
"tools": {
"label": "",
"label": "الأدوات",
"items": {
"docker": "",
"docker": "الحاويات",
"kubernetes": "",
"logs": "",
"api": "",
"certificates": "",
"tasks": ""
"logs": "السجلات",
"api": "API",
"certificates": "الشهادات",
"tasks": "المهام"
}
},
"settings": "",
"settings": "الإعدادات",
"help": {
"label": "",
"label": "المساعدة",
"items": {
"documentation": "",
"submitIssue": "",
"discord": "",
"sourceCode": ""
"documentation": "المستندات",
"submitIssue": "إبلاغ عن مشكلة",
"discord": "مجتمع ديسكورد",
"sourceCode": "كود المصدر"
}
},
"about": ""
"about": "حول"
}
},
"page": {
"home": {
"statistic": {
"board": "",
"user": "",
"invite": "",
"integration": "",
"app": "",
"group": "",
"searchEngine": "",
"media": ""
"board": "الألواح",
"user": "مستخدمين",
"invite": "دعوات",
"integration": "التكاملات",
"app": "البرامج",
"group": "المجموعات",
"searchEngine": "محركات البحث",
"media": "الوسائط"
},
"statisticLabel": {
"boards": "",
"resources": "",
"authentication": "",
"authorization": ""
"boards": "اللوحات",
"resources": "الموارد",
"authentication": "المصادقة",
"authorization": "الأذونات"
},
"heroBanner": {
"title": "",
"subtitle": ""
"title": "مرحبًا بعودتك إلى",
"subtitle": "{app} اللوحات"
}
},
"board": {
"title": "",
"title": "لوحاتك",
"action": {
"new": {
"label": ""
"label": "لوحة جديدة"
},
"open": {
"label": ""
"label": "فتح اللوحة"
},
"settings": {
"label": ""
"label": "الإعدادات"
},
"setHomeBoard": {
"label": "",
"label": "تعيينه كلوحة رئيسية",
"badge": {
"label": "",
"label": "الرئيسية",
"tooltip": ""
}
},
"setMobileHomeBoard": {
"label": "",
"label": "تعيينه كصفحتك الرئيسية للجوال",
"badge": {
"label": "",
"label": "الجوال",
"tooltip": ""
}
},
"duplicate": {
"label": ""
"label": "تكرار اللوحة"
},
"delete": {
"label": "",
"label": "حذف نهائيًا",
"confirm": {
"title": "",
"description": ""
@@ -3274,10 +3337,10 @@
}
},
"invite": {
"title": "",
"title": "إدارة دعوات المستخدمين",
"action": {
"new": {
"title": "",
"title": "دعوة جديدة",
"description": ""
},
"copy": {
@@ -3293,13 +3356,13 @@
},
"field": {
"id": {
"label": ""
"label": "المعرّف"
},
"creator": {
"label": ""
"label": "المنشئ"
},
"expirationDate": {
"label": ""
"label": "انتهاء الصلاحية"
},
"token": {
"label": ""
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": ""
},
"tracearr": {
"label": ""
}
},
"interval": {
@@ -4063,7 +4129,7 @@
"manage": {
"label": "",
"boards": {
"label": ""
"label": "الألواح"
},
"integrations": {
"label": "",
@@ -4096,7 +4162,7 @@
}
},
"users": {
"label": "",
"label": "المستخدمين",
"create": {
"label": ""
},
@@ -4107,7 +4173,7 @@
"label": ""
},
"invites": {
"label": ""
"label": "الدعوات"
}
},
"tools": {
@@ -4164,7 +4230,7 @@
}
},
"search": {
"placeholder": "",
"placeholder": "البحث عن",
"nothingFound": "",
"error": {
"fetch": ""
@@ -4324,13 +4390,13 @@
"title": "",
"option": {
"documentation": {
"label": ""
"label": "Documentation"
},
"submitIssue": {
"label": ""
"label": "Submit an issue"
},
"discord": {
"label": ""
"label": "Community Discord"
}
}
}
@@ -4339,20 +4405,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"
},
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Permitir geolocalización"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "Mostrar información de la memoria"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "Mostrar tiempo de actividad"
},
@@ -1996,6 +2002,61 @@
"title": "Instancias"
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "Ciudad / Código postal",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "CPU",
"memory": "Memoria",
"gpu": "",
"network": "Red"
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "CPU",
"memory": "MEM",
"gpu": "",
"network": "RED",
"up": "ENCENDIDO",
"down": "APAGADO"
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": "Tiempo"
},
"tracearr": {
"label": ""
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": ""
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": ""
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": ""
},
"tracearr": {
"label": ""
}
},
"interval": {
+119 -53
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"
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Autoriser la géolocalisation"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1634,7 +1637,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 +1861,9 @@
"memory": {
"label": "Afficher les infos de la mémoire"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "Afficher le temps de disponibilité"
},
@@ -1877,16 +1883,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 +1928,7 @@
"memory": "Mémoire",
"storage": "Stockage",
"uptime": "Disponibilité",
"haState": "Etat HA",
"haState": "État HA",
"storageType": {
"local": "Stockage local",
"shared": "Stockage partagé"
@@ -1996,12 +2002,67 @@
"title": "Instances"
}
},
"tracearr": {
"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": "Afficher le résumé des statistiques"
},
"showRecentActivity": {
"label": "Afficher l'activité récente"
},
"showViolations": {
"label": "Afficher les violations"
}
},
"stats": {
"activeStreams": "",
"totalUsers": "Utilisateurs totaux",
"transcodes": "",
"bandwidth": "Bande passante"
},
"streams": {
"empty": "",
"user": "Utilisateur",
"media": "Média",
"state": "",
"quality": "Qualité",
"device": "Appareil",
"progress": "Progression",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "Activité récente",
"empty": "Aucune activité récente",
"watched": "",
"partial": ""
},
"violations": {
"title": "Violations",
"empty": "Aucune violation récente",
"rule": "Règle",
"user": "Utilisateur"
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "Aucune intégration Tracearr configurée",
"internalServerError": "Impossible de récupérer les données de Tracearr"
}
},
"common": {
"location": {
"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": {
@@ -2020,7 +2081,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": {
@@ -2036,7 +2097,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"
@@ -2049,7 +2110,7 @@
"label": "Muet"
},
"hasControls": {
"label": "Afficher les controles"
"label": "Afficher les contrôles"
}
},
"error": {
@@ -2092,50 +2153,50 @@
}
},
"immich-serverStats": {
"name": "",
"description": "",
"name": "Statistiques du serveur Immich",
"description": "Informations sur votre instance Immich",
"option": {
"showUsers": {
"label": "",
"description": ""
"label": "Afficher les utilisateurs",
"description": "Afficher le nombre d'utilisateurs de l'instance"
},
"showPhotos": {
"label": "",
"description": ""
"label": "Afficher les photos",
"description": "Afficher le nombre de photos de l'instance"
},
"showVideos": {
"label": "",
"description": ""
"label": "Afficher les vidéos",
"description": "Afficher le nombre de vidéos de l'instance"
},
"showStorage": {
"label": "",
"description": ""
"label": "Affiche le stockage",
"description": "Afficher le stockage total utilisé"
}
},
"users": "",
"photos": "",
"videos": "",
"storage": ""
"users": "Utilisateurs",
"photos": "Photos",
"videos": "Vidéos",
"storage": "Stockage utilisé"
},
"immich-albumCarousel": {
"name": "",
"name": "Album Immich",
"option": {
"albumId": {
"label": "",
"description": ""
"label": "Identifiant de l'album",
"description": "C'est l'identifiant unique qui correspond à l'album Immich. Vous pouvez le trouver en ouvrant un album et copiant l'identifiant unique depuis l'URL, par exemple 'a21aca09-9f05-47dc-b1a1-7b53c547226e'"
},
"rotationIntervalSeconds": {
"label": "",
"description": ""
"label": "Intervalle en secondes",
"description": "Rotation entre les images de l'album chaque X secondes. Doit être entre 1 et 3600."
},
"showPhotoInfo": {
"label": "",
"description": ""
"label": "Afficher les informations de la photo",
"description": "Afficher la date et l'index de la photo en bas du diaporama"
}
},
"description": "",
"noAlbumSelected": "",
"noPhotos": ""
"description": "Diaporama de photos dans votre album Immich",
"noAlbumSelected": "Aucun album sélectionné, modifier le widget et choisissez un album",
"noPhotos": "Aucune photo dans cet album"
},
"downloads": {
"name": "Client de téléchargement",
@@ -2275,7 +2336,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)"
@@ -2487,7 +2548,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"
@@ -2612,7 +2673,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"
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "CPU",
"memory": "Mémoire",
"gpu": "",
"network": "Réseau"
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "CPU",
"memory": "RAM",
"gpu": "",
"network": "NET",
"up": "UP",
"down": "HORS SERVICE"
@@ -2702,7 +2765,7 @@
"label": "Mode d'affichage",
"option": {
"percentage": {
"label": "Pourcentage (ex : 70%)"
"label": "Pourcentage (ex : 76%)"
},
"absolute": {
"label": "Valeurs absolues (ex. : 800 Go / 1 To)"
@@ -2910,7 +2973,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."
@@ -3188,7 +3251,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} ?"
}
}
},
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": "Météo"
},
"tracearr": {
"label": "Tracearr"
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "אפשר מיקום גיאוגרפי"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": "הצג מידע זיכרון"
},
"gpu": {
"label": ""
},
"showUptime": {
"label": "הצג זמן פעולה"
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "עיר / מיקוד",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "מעבד",
"memory": "זיכרון",
"gpu": "",
"network": "רשת"
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "מעבד",
"memory": "זיכרון",
"gpu": "",
"network": "רשת",
"up": "העלאה",
"down": "הורדה"
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": "מזג אוויר"
},
"tracearr": {
"label": ""
}
},
"interval": {
+66
View File
@@ -1625,6 +1625,9 @@
},
"allowGeolocation": {
"label": "Dopusti geolociranje"
},
"allowModals": {
"label": ""
}
},
"error": {
@@ -1858,6 +1861,9 @@
"memory": {
"label": ""
},
"gpu": {
"label": ""
},
"showUptime": {
"label": ""
},
@@ -1996,6 +2002,61 @@
"title": ""
}
},
"tracearr": {
"name": "",
"description": "",
"option": {
"showStreams": {
"label": ""
},
"showStats": {
"label": ""
},
"showRecentActivity": {
"label": ""
},
"showViolations": {
"label": ""
}
},
"stats": {
"activeStreams": "",
"totalUsers": "",
"transcodes": "",
"bandwidth": ""
},
"streams": {
"empty": "",
"user": "",
"media": "",
"state": "",
"quality": "",
"device": "",
"progress": "",
"videoDecision": {
"directPlay": "",
"transcode": "",
"directStream": ""
}
},
"recentActivity": {
"title": "",
"empty": "",
"watched": "",
"partial": ""
},
"violations": {
"title": "",
"empty": "",
"rule": "",
"user": ""
},
"noSectionsEnabled": "",
"error": {
"noIntegration": "",
"internalServerError": ""
}
},
"common": {
"location": {
"query": "",
@@ -2670,6 +2731,7 @@
"option": {
"cpu": "",
"memory": "",
"gpu": "",
"network": ""
}
},
@@ -2686,6 +2748,7 @@
"card": {
"cpu": "",
"memory": "",
"gpu": "",
"network": "",
"up": "",
"down": ""
@@ -3518,6 +3581,9 @@
},
"weather": {
"label": ""
},
"tracearr": {
"label": ""
}
},
"interval": {
Loaded 100 of 144 files, more files were not shown because too many files have changed in this diff. Show more