Compare commits

...
194 Commits
Author SHA1 Message Date
Releases Homarr cf2e18d248 chore(release): 1.59.2 [skip ci] 2026-04-17 19:58:17 +00:00
homarr-releases[bot] 1efe97d8e6 chore(release): automatic release v1.59.2 2026-04-17 19:57:21 +00:00
homarr-update-contributors[bot] 469d7bf6fb chore: update contributors (#5528)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-17 13:11:09 +00:00
homarr-renovate[bot] 0082984f9c chore(deps): update tanstack-query monorepo to ^5.99.0 (#5527)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-17 10:39:11 +00:00
2e789795ed fix: preserve all-day event dates in Nextcloud calendar integration (#5523)
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-04-17 10:53:43 +02:00
homarr-renovate[bot] 88591d973f chore(deps): update pnpm/action-setup action to v6 (#5521)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-17 10:17:39 +02:00
homarr-crowdin[bot] 6218a401de chore(lang): updated translations from crowdin (#5522)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-04-17 10:12:57 +02:00
homarr-renovate[bot] a787e3deca chore(deps): update tanstack-query monorepo to ^5.98.0 (#5525)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-16 17:22:33 +00:00
homarr-renovate[bot] d195d260aa chore(deps): update dependency prettier to ^3.8.2 (#5515)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-15 20:09:26 +00:00
homarr-renovate[bot] d0973a9290 chore(deps): update dependency @immich/sdk to ^2.7.4 (#5520)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-15 18:15:03 +00:00
homarr-renovate[bot] 6866735cc1 chore(deps): update dependency mysql2 to v3.22.0 (#5519)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-15 14:55:24 +00:00
homarr-renovate[bot] 481999af5c chore(deps): update turbo monorepo to ^2.9.6 (#5518)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-15 14:54:53 +00:00
homarr-renovate[bot] 6a45645354 chore(deps): update dependency mysql2 to v3.21.1 (#5514)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-15 02:44:06 +00:00
homarr-crowdin[bot] f22a763d96 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>
2026-04-15 01:21:25 +00:00
homarr-renovate[bot] 7dd579d3c0 chore(deps): update dependency @immich/sdk to ^2.7.3 (#5511)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-14 19:23:50 +00:00
homarr-renovate[bot] 13ea6d08a0 chore(deps): update tanstack-query monorepo to ^5.97.0 (#5509)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-14 09:28:43 +00:00
homarr-renovate[bot] 8c1e5c8947 chore(deps): update dependency mysql2 to v3.21.0 (#5508)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-14 09:27:46 +00:00
homarr-renovate[bot] b397ab72c9 chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=8.0.8 (#5506)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-14 09:27:27 +00:00
homarr-renovate[bot] 792a8bd11e chore(deps): update vitest monorepo to ^4.1.4 (#5507)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-14 07:53:42 +00:00
homarr-crowdin[bot] f680fd170f chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-04-14 01:24:45 +00:00
homarr-renovate[bot] 916a261467 chore(deps): update dependency @next/eslint-plugin-next to v16.2.3 (#5501)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 21:58:44 +00:00
Meier Lukas 72d636c0f0 refactor(deps): migrate to pnpm catalog (#5464) 2026-04-13 22:45:24 +02:00
homarr-renovate[bot] c8a61a9df3 fix(deps): update react monorepo to v19.2.5 (#5502)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 20:14:59 +00:00
homarr-renovate[bot] 0731b7c5ef fix(deps): update dependency isomorphic-dompurify to ^3.8.0 (#5503)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 20:14:28 +00:00
homarr-renovate[bot] f2437c4731 fix(deps): update dependency next-intl to v4.9.1 [security] (#5470)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 18:04:13 +00:00
homarr-renovate[bot] 652ed1a5ed fix(deps): update dependency fast-xml-parser to ^5.5.11 (#5500)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 14:58:44 +00:00
Meier Lukas c48588c6a7 fix(about): add missing dependencies (#5480) 2026-04-13 16:51:38 +02:00
Meier Lukas ce0c72b008 ci: approve homarr-releases pull-requests automatically (#5468) 2026-04-13 16:46:10 +02:00
homarr-renovate[bot] 1057b478c3 fix(deps): update testcontainers-node monorepo to ^11.14.0 (#5498)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 14:43:08 +00:00
homarr-crowdin[bot] 4d53193fcc chore(lang): updated translations from crowdin (#5483)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-04-13 16:39:22 +02:00
homarr-renovate[bot] 95abd2c9d3 fix(deps): update tiptap monorepo to v3.22.3 (#5499)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 12:11:25 +00:00
homarr-renovate[bot] f88984cda8 fix(deps): update dependency typescript-eslint to ^8.58.1 (#5496)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-13 02:54:07 +00:00
homarr-renovate[bot] 0927f45b25 fix(deps): update dependency @immich/sdk to ^2.7.2 (#5494)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 21:42:41 +00:00
homarr-renovate[bot] ed24ce6381 fix(deps): update turbo monorepo to ^2.9.5 (#5491)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 17:46:20 +00:00
homarr-renovate[bot] 24607e266b chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=8.0.7 (#5486)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 17:42:25 +00:00
homarr-renovate[bot] 9cdf2aa1f2 fix(deps): update dependency @immich/sdk to ^2.7.0 (#5490)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 16:49:27 +00:00
homarr-renovate[bot] 74611b75a7 fix(deps): update dependency @drizzle-team/brocli to ^0.12.0 (#5489)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 15:44:44 +00:00
homarr-renovate[bot] cf131c5c51 fix(deps): update dependency swagger-ui-react to ^5.32.2 (#5488)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 14:47:24 +00:00
homarr-renovate[bot] efd8a55b6e fix(deps): update dependency jotai to ^2.19.1 (#5487)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 14:46:52 +00:00
homarr-renovate[bot] c0c16be9e4 chore(deps): update vitest monorepo to ^4.1.3 (#5485)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 10:47:52 +00:00
homarr-renovate[bot] 327611dfc6 chore(deps): update dependency jsdom to ^29.0.2 (#5484)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-12 05:32:06 +00:00
homarr-renovate[bot] c5149d4262 chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=8.0.5 (#5482)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-11 13:06:56 +00:00
homarr-renovate[bot] 3f039bf57d chore(deps): update dependency vite@&gt;&#x3d;5.0.0 &lt;&#x3d;5.4.18 to >=8.0.4 (#5479)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-11 07:56:52 +00:00
homarr-renovate[bot] 053cb8bec3 chore(deps): update dependency hono@&lt;4.6.5 to >=4.12.12 [security] (#5438)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-11 06:04:19 +00:00
homarr-renovate[bot] 813f2fa5ef fix(deps): update turbo monorepo to ^2.9.4 (#5476)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-11 02:33:48 +00:00
homarr-crowdin[bot] 3d0c9fde0c 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>
2026-04-11 01:15:30 +00:00
homarr-releases[bot] c74367e564 chore: update bug report template (#5475)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-04-10 22:49:51 +00:00
Releases Homarr 5e2b693f6a chore(release): 1.59.1 [skip ci] 2026-04-10 22:39:52 +00:00
homarr-releases[bot] 8df71f7695 chore(release): automatic release v1.59.1 2026-04-10 22:39:07 +00:00
Meier Lukas db6bb870e3 fix(qbittorrent): requests fail (#5473) 2026-04-11 00:38:05 +02:00
Meier Lukas dc44fac42a revert(deps): downgrade undici from v8 to v7 (#5473) 2026-04-11 00:33:22 +02:00
Meier Lukas 26c2fa555b chore: update documentation sitemap 2026-04-10 23:33:03 +02:00
homarr-releases[bot] 3ce3d221cd chore: update bug report template (#5466)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-04-10 21:54:40 +02:00
Releases Homarr 62e9bb0c49 chore(release): 1.59.0 [skip ci] 2026-04-10 19:43:43 +00:00
homarr-releases[bot] b11eaff043 chore(release): automatic release v1.59.0 2026-04-10 19:42:45 +00:00
homarr-renovate[bot] 845f11526e fix(deps): update dependency dotenv to ^17.4.1 (#5462)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-10 19:22:28 +00:00
homarr-renovate[bot] 7982def853 fix(deps): update dependency next to v16.2.3 [security] (#5461)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-10 21:05:50 +02:00
homarr-renovate[bot] 13e13ba352 fix(deps): update dependency react-quill-new to ^3.8.3 (#5463)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-10 19:05:14 +00:00
homarr-renovate[bot] 55b743496b chore(deps): update dependency axios@&gt;&#x3d;1.0.0 &lt;1.8.2 to >=1.15.0 [security] (#5453)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-10 20:57:19 +02:00
homarr-update-contributors[bot] 4a6aecceea chore: update integrations list readme (#5459)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-10 18:49:31 +00:00
Meier Lukas 2454cac7be fix(security): remove salt from database (#5412) 2026-04-10 20:48:20 +02:00
Meier Lukas 41a03ef4e0 fix(logs): missing fields from top level error with metadata in output (#5456) 2026-04-10 20:46:59 +02:00
7879b31630 feat: Anchor integration and Anchor Note widget (#5040)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-04-10 20:43:43 +02:00
homarr-update-contributors[bot] 57ea82f088 chore: update integrations list readme (#5457)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-10 17:21:07 +00:00
KyleandMeier Lukas 22d599c0cf feat: speedtest tracker integration (#5363)
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-04-10 19:04:42 +02:00
homarr-crowdin[bot] c861878058 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>
2026-04-10 16:13:35 +00:00
homarr-update-contributors[bot] 9f5ae31222 chore: update contributors (#5455)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-10 16:13:04 +00:00
Meier Lukas 6ec058c835 feat(rss-feed): add optional poster (#5452) 2026-04-10 18:11:20 +02:00
Meier Lukas faffb818f9 feat(rss-feed): add periodic live updates (#5449) 2026-04-09 22:45:44 +02:00
Meier Lukas bdf9439e49 ci: disable skip-stability-days for forks (#5451) 2026-04-09 22:45:01 +02:00
homarr-renovate[bot] 75b36bb160 fix(deps): update dependency undici to v8.0.2 (#5446)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-09 16:01:05 +00:00
homarr-renovate[bot] 43587656bb chore(deps): update dependency proxmox-api&gt;undici to v8.0.2 (#5445)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-09 15:59:56 +00:00
homarr-renovate[bot] 8cab4b0333 fix(deps): update tiptap monorepo to v3.22.2 (#5448)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-09 14:14:48 +00:00
homarr-crowdin[bot] 154ad1e01e chore(lang): updated translations from crowdin
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-04-09 01:09:23 +00:00
homarr-renovate[bot] cd2d0ba53f chore(deps): update dependency esbuild@&lt;&#x3d;0.24.2 to >=0.28.0 (#5436)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 18:14:49 +02:00
homarr-renovate[bot] 37d7c8535f chore(deps): update dependency esbuild to ^0.28.0 (#5435)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 18:14:39 +02:00
homarr-renovate[bot] a0e0083b16 fix(deps): update dependency undici to v8.0.1 (#5440)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 12:06:50 +00:00
homarr-renovate[bot] 5317a25cfb chore(deps): update dependency @types/node to ^24.12.2 (#5443)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 12:05:47 +00:00
homarr-renovate[bot] b619ba1d46 fix(deps): update tanstack-query monorepo to ^5.96.2 (#5442)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 11:08:35 +00:00
homarr-renovate[bot] 2baab5402d fix(deps): update tiptap monorepo to v3.22.1 (#5434)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 11:08:05 +00:00
homarr-renovate[bot] 174851bd19 chore(deps): update dependency @types/node to ^24.12.1 (#5441)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 11:05:37 +00:00
homarr-renovate[bot] a7b195d2cb fix(deps): update dependency fast-xml-parser to ^5.5.10 (#5439)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 11:04:39 +00:00
homarr-renovate[bot] 0ab78142b3 chore(deps): update dependency proxmox-api&gt;undici to v8.0.1 (#5431)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-08 09:08:51 +00:00
homarr-renovate[bot] 3f73474d31 fix(deps): update dependency sass to ^1.99.0 (#5437)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-07 23:47:06 +00:00
homarr-renovate[bot]andMeier Lukas f2f5128cbd fix(deps): update dependency undici to v8 (#5432)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2026-04-07 16:52:42 +02:00
homarr-renovate[bot] 50bf7cdf19 fix(deps): update dependency dotenv to ^17.4.0 (#5427)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-07 14:24:59 +00:00
homarr-renovate[bot] ec15529ca4 chore(deps): update dependency hono@&lt;4.6.5 to >=4.12.10 (#5430)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-07 14:22:54 +00:00
homarr-renovate[bot] db8a3291da chore(deps): update dependency esbuild@&lt;&#x3d;0.24.2 to >=0.27.5 (#5429)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-07 16:18:33 +02:00
homarr-renovate[bot] e2410f143c chore(deps): update dependency esbuild to ^0.27.5 (#5428)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-07 16:18:19 +02:00
homarr-crowdin[bot] cb2862bc79 chore(lang): updated translations from crowdin (#5417)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-04-07 16:12:15 +02:00
homarr-renovate[bot] dbe8d84202 fix(deps): update dependency adm-zip to v0.5.17 (#5422)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-06 21:45:44 +00:00
homarr-renovate[bot] 612038ad07 fix(deps): update dependency next-intl to v4.9.0 (#5426)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-06 20:46:21 +00:00
homarr-renovate[bot] 72ad935ddf fix(deps): update tanstack-query monorepo to ^5.96.1 (#5421)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-06 09:17:17 +00:00
homarr-renovate[bot] 859747037b fix(deps): update nextjs monorepo to v16.2.2 (#5418)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-06 02:45:18 +00:00
homarr-renovate[bot] dfa87939f2 fix(deps): update turbo monorepo to ^2.9.3 (#5419)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-06 02:44:53 +00:00
homarr-renovate[bot] 402d948fe7 fix(deps): update tiptap monorepo to v3.22.0 (#5414)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-05 21:39:56 +00:00
homarr-renovate[bot] d8480c05a6 fix(deps): update turbo monorepo to ^2.9.2 (#5415)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-05 20:44:00 +00:00
homarr-renovate[bot] 6047eb4477 fix(deps): update dependency next-intl to v4.8.4 (#5413)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-05 16:45:33 +00:00
homarr-renovate[bot] 8b1e3babc0 fix(deps): update tanstack-query monorepo to ^5.96.0 (#5410)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-05 11:37:24 +00:00
homarr-renovate[bot] 1e869f6a96 fix(deps): update dependency typescript-eslint to ^8.58.0 (#5409)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-04 21:40:41 +00:00
homarr-renovate[bot] 7607140f94 fix(deps): update turbo monorepo to ^2.9.1 (#5408)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-04 20:41:33 +00:00
homarr-renovate[bot] f037da5831 chore(deps): update dependency rollup@&gt;&#x3d;4.0.0 &lt;4.22.4 to >=4.60.1 (#5402)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-04 11:36:15 +00:00
homarr-renovate[bot] f0ef4729bf fix(deps): update dependency @tabler/icons-react to ^3.41.1 (#5400)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-04 10:42:36 +00:00
homarr-releases[bot] ab1418514d chore: update bug report template (#5406)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-04-04 10:08:48 +00:00
Releases Homarr a5b6c120dd chore(release): 1.58.1 [skip ci] 2026-04-04 09:53:34 +00:00
homarr-releases[bot] 9e7d5ad629 chore(release): automatic release v1.58.1 2026-04-04 09:52:41 +00:00
Meier Lukas a86cdb9fb9 fix: arabic text in english translations 2026-04-04 11:50:19 +02:00
homarr-update-contributors[bot] 270fce6a0e chore: update integrations list readme (#5404)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-04 09:46:50 +00:00
homarr-crowdin[bot] 2680255f44 chore(lang): updated translations from crowdin (#5401)
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com>
2026-04-04 11:45:23 +02:00
Meier Lukas ddf87d278e chore: remove temporary ts-expect-error for search-ch documentation link 2026-04-04 11:41:25 +02:00
homarr-releases[bot] 822d2c194b chore: update bug report template (#5399)
Co-authored-by: Releases Homarr <175486441+homarr-releases[bot]@users.noreply.github.com>
2026-04-03 21:50:53 +02:00
Releases Homarr f5f7f3e433 chore(release): 1.58.0 [skip ci] 2026-04-03 19:38:14 +00:00
homarr-releases[bot] b1b3b79cd9 chore(release): automatic release v1.58.0 2026-04-03 19:37:17 +00:00
homarr-update-contributors[bot] 35d40d0dea chore: update integrations list readme (#5397)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-03 18:28:41 +00:00
Meier Lukas abb2dfc92a feat(integrations): add search.ch integration with timetable widget (#5180) 2026-04-03 20:23:15 +02:00
homarr-renovate[bot] 279d830b79 chore(deps): update dependency conventional-changelog-conventionalcommits to ^9.3.1 (#5396)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-03 17:25:35 +00:00
homarr-renovate[bot] 72a93e681f fix(deps): update trpc monorepo to ^11.16.0 (#5392)
Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
2026-04-03 16:48:45 +00:00
Meier Lukas c977b84e2d fix: node-ical prevents many trpc queries from loading (#5395) 2026-04-03 18:26:52 +02:00
homarr-update-contributors[bot] e92c407beb chore: update contributors (#5394)
Co-authored-by: Homarr Update Contributors <210161987+homarr-update-contributors[bot]@users.noreply.github.com>
2026-04-03 13:24:06 +00:00
homarr-crowdin[bot]andMeier Lukas 05af82bc02 chore(lang): updated translations from crowdin
* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

---------

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

* chore(lang): update translations from crowdin

* chore(lang): update translations from crowdin

---------

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

* chore(lang): update translations from crowdin

---------

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

* fix: omit can not be used with refine

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

* fix: remove omit after refine

---------

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

* fix: date from node-ical represents utc value

---------

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

* fix: use testing fetch for Immich connection check

* fix: typecheck issues

---------

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

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

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

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

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

Addresses review comments from manuel-rw on PR #5255

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

Fixes #5311

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

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

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

* Update nextcloud.integration.ts

* defined notificationSchema outside of class

---------

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

No files matched your search

+6
View File
@@ -33,6 +33,12 @@ body:
options:
# The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION#
- 1.59.1
- 1.59.0
- 1.58.1
- 1.58.0
- 1.57.1
- 1.57.0
- 1.56.1
- 1.56.0
- 1.55.0
+2 -2
View File
@@ -15,11 +15,11 @@ runs:
with:
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Build docker image for ${{ inputs.architecture }}
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
push: false
load: true
+12
View File
@@ -11,6 +11,12 @@
matchPackageNames: ["tsx"],
enabled: false,
},
// Reenable once bug with undici v8 is fixed (https://github.com/homarr-labs/homarr/issues/5467)
{
matchPackageNames: ["undici"],
matchNewValue: "8.*",
enabled: false,
},
{
matchUpdateTypes: ["minor", "patch", "pin", "digest"],
automerge: true,
@@ -31,6 +37,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,
+7 -2
View File
@@ -9,13 +9,18 @@ jobs:
approve-automatic-prs:
runs-on: ubuntu-latest
timeout-minutes: 2
if: github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 # Id of renovate bot and crowdin bot see https://api.github.com/users/homarr-renovate%5Bbot%5D and https://api.github.com/users/homarr-crowdin%5Bbot%5D and https://api.github.com/users/homarr-update-contributors%5Bbot%5D
# 158783068: homarr-renovate[bot]
# 190541745: homarr-crowdin[bot]
# 210161987: homarr-update-contributors[bot]
# 175486441: homarr-releases[bot]
# Extracted from https://api.github.com/users/<username>
if: github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 || github.actor_id == 175486441
steps:
- name: Checkout code
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@v6
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 }}
@@ -7,7 +7,7 @@ on:
jobs:
skip-stability-days:
if: ${{ !startsWith(github.head_ref, 'renovate/') }}
if: ${{ !startsWith(github.head_ref, 'renovate/') && github.event.pull_request.head.repo.full_name == github.repository }}
name: Skip Stability Days
runs-on: ubuntu-latest
timeout-minutes: 1
+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@v6
- 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 }}
+5 -1
View File
@@ -67,4 +67,8 @@ e2e/shared/tmp
apps/nextjs/public/images/background.png
# next-intl
en.d.json.ts
en.d.json.ts
# personal agent config
AGENTS.md
.opencode
+1 -1
View File
@@ -1 +1 @@
24.14.0
24.14.1
+163
View File
@@ -1,3 +1,166 @@
## [1.59.2](https://github.com/homarr-labs/homarr/compare/v1.59.1...v1.59.2) (2026-04-17)
### Bug Fixes
* **about:** add missing dependencies ([#5480](https://github.com/homarr-labs/homarr/issues/5480)) ([c48588c](https://github.com/homarr-labs/homarr/commit/c48588c6a748920b05f8afcd79fad17dc65f2930))
* **deps:** update dependency @drizzle-team/brocli to ^0.12.0 ([#5489](https://github.com/homarr-labs/homarr/issues/5489)) ([74611b7](https://github.com/homarr-labs/homarr/commit/74611b75a736df43a77ebc780d2e0eee5c40e3f3))
* **deps:** update dependency @immich/sdk to ^2.7.0 ([#5490](https://github.com/homarr-labs/homarr/issues/5490)) ([9cdf2aa](https://github.com/homarr-labs/homarr/commit/9cdf2aa1f2b1477522879095b603b100c1dd3ac0))
* **deps:** update dependency @immich/sdk to ^2.7.2 ([#5494](https://github.com/homarr-labs/homarr/issues/5494)) ([0927f45](https://github.com/homarr-labs/homarr/commit/0927f45b2573753152fd5e9e318ba62b88abca35))
* **deps:** update dependency fast-xml-parser to ^5.5.11 ([#5500](https://github.com/homarr-labs/homarr/issues/5500)) ([652ed1a](https://github.com/homarr-labs/homarr/commit/652ed1a5ed0beeb2dbd44976890f24dc12b90cf6))
* **deps:** update dependency isomorphic-dompurify to ^3.8.0 ([#5503](https://github.com/homarr-labs/homarr/issues/5503)) ([0731b7c](https://github.com/homarr-labs/homarr/commit/0731b7c5ef272bcf69c566cdec723f2696309e67))
* **deps:** update dependency jotai to ^2.19.1 ([#5487](https://github.com/homarr-labs/homarr/issues/5487)) ([efd8a55](https://github.com/homarr-labs/homarr/commit/efd8a55b6ea113bc42bc90d9ccb0db6c1d2b68ef))
* **deps:** update dependency next-intl to v4.9.1 [security] ([#5470](https://github.com/homarr-labs/homarr/issues/5470)) ([f2437c4](https://github.com/homarr-labs/homarr/commit/f2437c473106689e9fa93e7dcbea84b00800ea4a))
* **deps:** update dependency swagger-ui-react to ^5.32.2 ([#5488](https://github.com/homarr-labs/homarr/issues/5488)) ([cf131c5](https://github.com/homarr-labs/homarr/commit/cf131c5c51f6c21f942ab89d02353f93dc05774c))
* **deps:** update dependency typescript-eslint to ^8.58.1 ([#5496](https://github.com/homarr-labs/homarr/issues/5496)) ([f88984c](https://github.com/homarr-labs/homarr/commit/f88984cda844beb1099a08c9cdeb6691ad13b5a8))
* **deps:** update react monorepo to v19.2.5 ([#5502](https://github.com/homarr-labs/homarr/issues/5502)) ([c8a61a9](https://github.com/homarr-labs/homarr/commit/c8a61a9df3087de28c76f573623725eec246dd32))
* **deps:** update testcontainers-node monorepo to ^11.14.0 ([#5498](https://github.com/homarr-labs/homarr/issues/5498)) ([1057b47](https://github.com/homarr-labs/homarr/commit/1057b478c37d02e9de7bcd155f0b3fb764149abe))
* **deps:** update tiptap monorepo to v3.22.3 ([#5499](https://github.com/homarr-labs/homarr/issues/5499)) ([95abd2c](https://github.com/homarr-labs/homarr/commit/95abd2c9d3a563c7eaebe12f3b8015e3299e27e8))
* **deps:** update turbo monorepo to ^2.9.4 ([#5476](https://github.com/homarr-labs/homarr/issues/5476)) ([813f2fa](https://github.com/homarr-labs/homarr/commit/813f2fa5efb999d3364882578fdf456537dba3a6))
* **deps:** update turbo monorepo to ^2.9.5 ([#5491](https://github.com/homarr-labs/homarr/issues/5491)) ([ed24ce6](https://github.com/homarr-labs/homarr/commit/ed24ce6381466fa530ce225363231303be4ed92e))
* preserve all-day event dates in Nextcloud calendar integration ([#5523](https://github.com/homarr-labs/homarr/issues/5523)) ([2e78979](https://github.com/homarr-labs/homarr/commit/2e789795ed11c816550b6a3ce88fe92a360ede9c))
## [1.59.1](https://github.com/homarr-labs/homarr/compare/v1.59.0...v1.59.1) (2026-04-10)
### Bug Fixes
* **qbittorrent:** requests fail ([#5473](https://github.com/homarr-labs/homarr/issues/5473)) ([db6bb87](https://github.com/homarr-labs/homarr/commit/db6bb870e3be14c7895a2882755d3aee09e44fa6))
### Reverts
* **deps:** downgrade undici from v8 to v7 ([#5473](https://github.com/homarr-labs/homarr/issues/5473)) ([dc44fac](https://github.com/homarr-labs/homarr/commit/dc44fac42aab5679427b2921402dfa11e634e1cf))
## [1.59.0](https://github.com/homarr-labs/homarr/compare/v1.58.1...v1.59.0) (2026-04-10)
### Features
* Anchor integration and Anchor Note widget ([#5040](https://github.com/homarr-labs/homarr/issues/5040)) ([7879b31](https://github.com/homarr-labs/homarr/commit/7879b3163074b6af9381b8eb70dced55faddc506))
* **rss-feed:** add optional poster ([#5452](https://github.com/homarr-labs/homarr/issues/5452)) ([6ec058c](https://github.com/homarr-labs/homarr/commit/6ec058c835040dbf72cd2490d49f94be7da066dc))
* **rss-feed:** add periodic live updates ([#5449](https://github.com/homarr-labs/homarr/issues/5449)) ([faffb81](https://github.com/homarr-labs/homarr/commit/faffb818f99900e3abb5fa78cfbb7980692362b5))
* speedtest tracker integration ([#5363](https://github.com/homarr-labs/homarr/issues/5363)) ([22d599c](https://github.com/homarr-labs/homarr/commit/22d599c0cf64b39f3d616ceadc6c2ac32551f99d))
### Bug Fixes
* **deps:** update dependency @tabler/icons-react to ^3.41.1 ([#5400](https://github.com/homarr-labs/homarr/issues/5400)) ([f0ef472](https://github.com/homarr-labs/homarr/commit/f0ef4729bfb7c3672968b77b9b757bc34877c60f))
* **deps:** update dependency adm-zip to v0.5.17 ([#5422](https://github.com/homarr-labs/homarr/issues/5422)) ([dbe8d84](https://github.com/homarr-labs/homarr/commit/dbe8d84202b00687f16784cbedf61d7488efbbff))
* **deps:** update dependency dotenv to ^17.4.0 ([#5427](https://github.com/homarr-labs/homarr/issues/5427)) ([50bf7cd](https://github.com/homarr-labs/homarr/commit/50bf7cdf194c13bcbf95ef8a6a1bc7eb4c9c3865))
* **deps:** update dependency dotenv to ^17.4.1 ([#5462](https://github.com/homarr-labs/homarr/issues/5462)) ([845f115](https://github.com/homarr-labs/homarr/commit/845f11526ea7b3dd6f543fad48a158a4782c52bb))
* **deps:** update dependency fast-xml-parser to ^5.5.10 ([#5439](https://github.com/homarr-labs/homarr/issues/5439)) ([a7b195d](https://github.com/homarr-labs/homarr/commit/a7b195d2cb25fffe81266e7f6fa9499d94cae733))
* **deps:** update dependency next to v16.2.3 [security] ([#5461](https://github.com/homarr-labs/homarr/issues/5461)) ([7982def](https://github.com/homarr-labs/homarr/commit/7982def853fd69c269946dc2d6377186512baaf9))
* **deps:** update dependency next-intl to v4.8.4 ([#5413](https://github.com/homarr-labs/homarr/issues/5413)) ([6047eb4](https://github.com/homarr-labs/homarr/commit/6047eb4477584172f7d8e6e14237c251ac487d81))
* **deps:** update dependency next-intl to v4.9.0 ([#5426](https://github.com/homarr-labs/homarr/issues/5426)) ([612038a](https://github.com/homarr-labs/homarr/commit/612038ad070681c582cb54ac4298bc07700765c9))
* **deps:** update dependency react-quill-new to ^3.8.3 ([#5463](https://github.com/homarr-labs/homarr/issues/5463)) ([13e13ba](https://github.com/homarr-labs/homarr/commit/13e13ba3526436b044ac83d08c346e8e406ca3d4))
* **deps:** update dependency sass to ^1.99.0 ([#5437](https://github.com/homarr-labs/homarr/issues/5437)) ([3f73474](https://github.com/homarr-labs/homarr/commit/3f73474d3125279d94395272365aa67e0a0f69e8))
* **deps:** update dependency typescript-eslint to ^8.58.0 ([#5409](https://github.com/homarr-labs/homarr/issues/5409)) ([1e869f6](https://github.com/homarr-labs/homarr/commit/1e869f6a96d7e34af068b5666e01093a4b20fe97))
* **deps:** update dependency undici to v8 ([#5432](https://github.com/homarr-labs/homarr/issues/5432)) ([f2f5128](https://github.com/homarr-labs/homarr/commit/f2f5128cbd961b6576e195ac8b695f12996c377e))
* **deps:** update dependency undici to v8.0.1 ([#5440](https://github.com/homarr-labs/homarr/issues/5440)) ([a0e0083](https://github.com/homarr-labs/homarr/commit/a0e0083b16ea6ce42a0e3594ad53321210c98141))
* **deps:** update dependency undici to v8.0.2 ([#5446](https://github.com/homarr-labs/homarr/issues/5446)) ([75b36bb](https://github.com/homarr-labs/homarr/commit/75b36bb1609a0ea1a97158f4674ebf8f4c5f205e))
* **deps:** update nextjs monorepo to v16.2.2 ([#5418](https://github.com/homarr-labs/homarr/issues/5418)) ([8597470](https://github.com/homarr-labs/homarr/commit/859747037b4a0caca9e208e0b4ebce638b6c5d4e))
* **deps:** update tanstack-query monorepo to ^5.96.0 ([#5410](https://github.com/homarr-labs/homarr/issues/5410)) ([8b1e3ba](https://github.com/homarr-labs/homarr/commit/8b1e3babc02d8ab6a7db9fd85325a0b92813ce8c))
* **deps:** update tanstack-query monorepo to ^5.96.1 ([#5421](https://github.com/homarr-labs/homarr/issues/5421)) ([72ad935](https://github.com/homarr-labs/homarr/commit/72ad935ddf831c008e53ae3a1c5e924cc82710d1))
* **deps:** update tanstack-query monorepo to ^5.96.2 ([#5442](https://github.com/homarr-labs/homarr/issues/5442)) ([b619ba1](https://github.com/homarr-labs/homarr/commit/b619ba1d46768cc3ed1d3739e0f379e0059bc2d9))
* **deps:** update tiptap monorepo to v3.22.0 ([#5414](https://github.com/homarr-labs/homarr/issues/5414)) ([402d948](https://github.com/homarr-labs/homarr/commit/402d948fe7d36406b7a75a4e4861330c6bcaa78b))
* **deps:** update tiptap monorepo to v3.22.1 ([#5434](https://github.com/homarr-labs/homarr/issues/5434)) ([2baab54](https://github.com/homarr-labs/homarr/commit/2baab5402d79caba0e1b3c53f52fdf0cbeb6875d))
* **deps:** update tiptap monorepo to v3.22.2 ([#5448](https://github.com/homarr-labs/homarr/issues/5448)) ([8cab4b0](https://github.com/homarr-labs/homarr/commit/8cab4b03333050376ae22a14eaaa1c0f045992d3))
* **deps:** update turbo monorepo to ^2.9.1 ([#5408](https://github.com/homarr-labs/homarr/issues/5408)) ([7607140](https://github.com/homarr-labs/homarr/commit/7607140f94f5b19a6b9cbacc7deabd2afc004d24))
* **deps:** update turbo monorepo to ^2.9.2 ([#5415](https://github.com/homarr-labs/homarr/issues/5415)) ([d8480c0](https://github.com/homarr-labs/homarr/commit/d8480c05a63525dc655a73675c2a9eec575a9539))
* **deps:** update turbo monorepo to ^2.9.3 ([#5419](https://github.com/homarr-labs/homarr/issues/5419)) ([dfa8793](https://github.com/homarr-labs/homarr/commit/dfa87939f200719af9f34e0e409895cf6e5a9c13))
* **logs:** missing fields from top level error with metadata in output ([#5456](https://github.com/homarr-labs/homarr/issues/5456)) ([41a03ef](https://github.com/homarr-labs/homarr/commit/41a03ef4e03675c5adf1171be152a767679bc466))
* **security:** remove salt from database ([#5412](https://github.com/homarr-labs/homarr/issues/5412)) ([2454cac](https://github.com/homarr-labs/homarr/commit/2454cac7be14207f822bf3e38f9a7fd261645dc5))
## [1.58.1](https://github.com/homarr-labs/homarr/compare/v1.58.0...v1.58.1) (2026-04-04)
### Bug Fixes
* arabic text in english translations ([a86cdb9](https://github.com/homarr-labs/homarr/commit/a86cdb9fb91a10c8250f849e70c17777c18ada77))
## [1.58.0](https://github.com/homarr-labs/homarr/compare/v1.57.1...v1.58.0) (2026-04-03)
### Features
* **integrations:** add search.ch integration with timetable widget ([#5180](https://github.com/homarr-labs/homarr/issues/5180)) ([abb2dfc](https://github.com/homarr-labs/homarr/commit/abb2dfc92ab51b16e7172374cfee8b9c0c752454))
### Bug Fixes
* **boards:** unable to click on links at settings page ([#5230](https://github.com/homarr-labs/homarr/issues/5230)) ([6a200c3](https://github.com/homarr-labs/homarr/commit/6a200c39afaa1a9a1057643612065a40ad1cdd82))
* **deps:** update dependency @ctrl/deluge to ^7.5.3 ([#5365](https://github.com/homarr-labs/homarr/issues/5365)) ([9c134c5](https://github.com/homarr-labs/homarr/commit/9c134c5ceb742b21b021ef3b49de8cc0d526d460))
* **deps:** update dependency @ctrl/deluge to v8 ([#5367](https://github.com/homarr-labs/homarr/issues/5367)) ([e88e3d1](https://github.com/homarr-labs/homarr/commit/e88e3d17c01dab8836bec11d2ac01195f06cdc2e))
* **deps:** update dependency @immich/sdk to ^2.6.2 ([#5370](https://github.com/homarr-labs/homarr/issues/5370)) ([4a5bafa](https://github.com/homarr-labs/homarr/commit/4a5bafa79092b209e94a56f72362011f416bf115))
* **deps:** update dependency @immich/sdk to ^2.6.3 ([#5380](https://github.com/homarr-labs/homarr/issues/5380)) ([2dd3c90](https://github.com/homarr-labs/homarr/commit/2dd3c906c4215b55e23f4b15ba4b423e23be8376))
* **deps:** update dependency @tabler/icons-react to ^3.41.0 ([#5384](https://github.com/homarr-labs/homarr/issues/5384)) ([a9bcc9d](https://github.com/homarr-labs/homarr/commit/a9bcc9d04bdfcf28ea561cd590ca2a4bd775593c))
* **deps:** update dependency better-sqlite3 to ^12.8.0 ([#5266](https://github.com/homarr-labs/homarr/issues/5266)) ([46db8b6](https://github.com/homarr-labs/homarr/commit/46db8b6ab32406fb4ff7a7a9938692954778a070))
* **deps:** update dependency drizzle-orm to ^0.45.2 ([#5388](https://github.com/homarr-labs/homarr/issues/5388)) ([0a78e3f](https://github.com/homarr-labs/homarr/commit/0a78e3f12aa99509f42d7af647a478c5abaf5c96))
* **deps:** update dependency fast-xml-parser to ^5.5.9 ([#5359](https://github.com/homarr-labs/homarr/issues/5359)) ([189806b](https://github.com/homarr-labs/homarr/commit/189806b5a2b8e67f925cc8def6bc8ddefdc5bfc4))
* **deps:** update dependency fastify to ^5.8.4 ([#5351](https://github.com/homarr-labs/homarr/issues/5351)) ([728f7e4](https://github.com/homarr-labs/homarr/commit/728f7e489aa499bcd7e97c7ec8571e29e7e2cab3))
* **deps:** update dependency isomorphic-dompurify to ^3.7.0 ([#5366](https://github.com/homarr-labs/homarr/issues/5366)) ([c2b54dd](https://github.com/homarr-labs/homarr/commit/c2b54ddfaab93daa4e65c0c38bf5c27e4402a492))
* **deps:** update dependency isomorphic-dompurify to ^3.7.1 ([#5369](https://github.com/homarr-labs/homarr/issues/5369)) ([8d1ff40](https://github.com/homarr-labs/homarr/commit/8d1ff4065d669268f0e6a03f2b68c1ba728a756a))
* **deps:** update dependency jotai to ^2.19.0 ([#5368](https://github.com/homarr-labs/homarr/issues/5368)) ([14d8717](https://github.com/homarr-labs/homarr/commit/14d87178b5a93bdc6fb74793feed95d014814d0b))
* **deps:** update dependency node-ical to ^0.23.1 - abandoned ([#4856](https://github.com/homarr-labs/homarr/issues/4856)) ([b07c545](https://github.com/homarr-labs/homarr/commit/b07c545f17f4dafa62167047baa9eed4b7ee4395))
* **deps:** update dependency typescript-eslint to ^8.57.2 ([#5360](https://github.com/homarr-labs/homarr/issues/5360)) ([cefbf0e](https://github.com/homarr-labs/homarr/commit/cefbf0e2715e9adbc72cdb0b83d84c309e3257d6))
* **deps:** update dependency undici to v7.24.6 ([#5374](https://github.com/homarr-labs/homarr/issues/5374)) ([4f2cc9a](https://github.com/homarr-labs/homarr/commit/4f2cc9af2a42e19624def3bdea60fcb00334c483))
* **deps:** update dependency zod to ^4.3.5 - abandoned ([#4803](https://github.com/homarr-labs/homarr/issues/4803)) ([2b4c83d](https://github.com/homarr-labs/homarr/commit/2b4c83dccac168ffa6f9b6e184ed004659373cf7))
* **deps:** update dependency zod to ^4.3.6 ([#5376](https://github.com/homarr-labs/homarr/issues/5376)) ([a9582f0](https://github.com/homarr-labs/homarr/commit/a9582f04edb0e161d1fab6e64b13d0ce463ef341))
* **deps:** update tanstack-query monorepo to ^5.95.0 ([#5325](https://github.com/homarr-labs/homarr/issues/5325)) ([b11bf36](https://github.com/homarr-labs/homarr/commit/b11bf367d0567e4c8b9068804442ebd200dab01c))
* **deps:** update tanstack-query monorepo to ^5.95.1 ([#5355](https://github.com/homarr-labs/homarr/issues/5355)) ([332b6b4](https://github.com/homarr-labs/homarr/commit/332b6b4f42e7c90570cd4c25a6e6a331a76c660f))
* **deps:** update tanstack-query monorepo to ^5.95.2 ([#5357](https://github.com/homarr-labs/homarr/issues/5357)) ([6820991](https://github.com/homarr-labs/homarr/commit/682099189b689ea17e16af41fd280a72ed68016f))
* **deps:** update tiptap monorepo to v3.20.5 ([#5371](https://github.com/homarr-labs/homarr/issues/5371)) ([6820f8a](https://github.com/homarr-labs/homarr/commit/6820f8afea622587c1ee18b13d13a041ca0572ee))
* **deps:** update tiptap monorepo to v3.21.0 ([#5386](https://github.com/homarr-labs/homarr/issues/5386)) ([b30dbaf](https://github.com/homarr-labs/homarr/commit/b30dbafbbbad468ff37ff11237a64ec29df62345))
* **deps:** update trpc monorepo to ^11.15.0 ([#5354](https://github.com/homarr-labs/homarr/issues/5354)) ([2fb2981](https://github.com/homarr-labs/homarr/commit/2fb2981d2fb79ca2ca6a9bc5c08d91c8fa5710e7))
* **deps:** update trpc monorepo to ^11.15.1 ([#5381](https://github.com/homarr-labs/homarr/issues/5381)) ([451e68a](https://github.com/homarr-labs/homarr/commit/451e68ab57551e23dd7ae772b7483cc56d57ebbb))
* **deps:** update trpc monorepo to ^11.16.0 ([#5392](https://github.com/homarr-labs/homarr/issues/5392)) ([72a93e6](https://github.com/homarr-labs/homarr/commit/72a93e681f3701459176692857220730c279f80b))
* **deps:** update turbo monorepo to ^2.8.21 ([#5391](https://github.com/homarr-labs/homarr/issues/5391)) ([fa40b6b](https://github.com/homarr-labs/homarr/commit/fa40b6b647b884a7f10c268a22330e0e68b50d4d))
* honor trusted certificates in Immich integration requests ([#5275](https://github.com/homarr-labs/homarr/issues/5275)) ([7679553](https://github.com/homarr-labs/homarr/commit/7679553f16ff2945a3eb6cb668ca3a701d13d5ce)), closes [#5242](https://github.com/homarr-labs/homarr/issues/5242)
* node-ical prevents many trpc queries from loading ([#5395](https://github.com/homarr-labs/homarr/issues/5395)) ([c977b84](https://github.com/homarr-labs/homarr/commit/c977b84e2d84d5724219f5ff5f99bc550438e168))
## [1.57.1](https://github.com/homarr-labs/homarr/compare/v1.57.0...v1.57.1) (2026-03-27)
### Bug Fixes
* dashdot gpu ([#5347](https://github.com/homarr-labs/homarr/issues/5347)) ([192a43a](https://github.com/homarr-labs/homarr/commit/192a43a031eb9431da2c11395eda69a6c7236b9b))
* **deps:** update dependency @t3-oss/env-nextjs to ^0.13.11 ([#5345](https://github.com/homarr-labs/homarr/issues/5345)) ([49d9394](https://github.com/homarr-labs/homarr/commit/49d9394cd78b2a1146c8dc00dd982215a2f9a580))
* **deps:** update dependency dockerode to ^4.0.10 ([#5327](https://github.com/homarr-labs/homarr/issues/5327)) ([42a3791](https://github.com/homarr-labs/homarr/commit/42a3791e6b5664a0f28d3ee4d29d5257ef3765f6))
* **deps:** update dependency fast-xml-parser to ^5.5.8 ([#5324](https://github.com/homarr-labs/homarr/issues/5324)) ([7982938](https://github.com/homarr-labs/homarr/commit/798293813cf82e38d7059069823f927b7cdfb3b8))
## [1.57.0](https://github.com/homarr-labs/homarr/compare/v1.56.1...v1.57.0) (2026-03-27)
### Features
* add "allowModals" option for iframe widget ([#5339](https://github.com/homarr-labs/homarr/issues/5339)) ([2fefd9d](https://github.com/homarr-labs/homarr/commit/2fefd9da01c827160fa5fd9b63a38615ea9969e2))
* add GPU monitoring support via Glances integration ([#5255](https://github.com/homarr-labs/homarr/issues/5255)) ([003dac4](https://github.com/homarr-labs/homarr/commit/003dac4744f2e23535f1cbb7494187201af4b164)), closes [#5102](https://github.com/homarr-labs/homarr/issues/5102)
* add nextcloud notification provider ([#5343](https://github.com/homarr-labs/homarr/issues/5343)) ([f88bf95](https://github.com/homarr-labs/homarr/commit/f88bf9582e1cf45a6b7fd898e12d227869a3849f))
### Bug Fixes
* **deps:** update dependency @immich/sdk to ^2.6.0 ([#5318](https://github.com/homarr-labs/homarr/issues/5318)) ([20a9b3d](https://github.com/homarr-labs/homarr/commit/20a9b3d556a15e6fdf7d1dbd78b677b5ce219c75))
* **deps:** update dependency @immich/sdk to ^2.6.1 ([#5319](https://github.com/homarr-labs/homarr/issues/5319)) ([feaf022](https://github.com/homarr-labs/homarr/commit/feaf022965cf4a27112c7e3bcb9ef3ad0dd5c281))
* **deps:** update dependency @next/eslint-plugin-next to v16.1.7 ([#5289](https://github.com/homarr-labs/homarr/issues/5289)) ([237d11d](https://github.com/homarr-labs/homarr/commit/237d11d2c021e28d58b0d627d2802d509d4c1b2b))
* **deps:** update dependency @tanstack/react-query to ^5.91.0 ([#5305](https://github.com/homarr-labs/homarr/issues/5305)) ([13b99d0](https://github.com/homarr-labs/homarr/commit/13b99d021b46fe0541ce9b42ba66756fafd26c30))
* **deps:** update dependency @tanstack/react-query to ^5.91.2 ([#5314](https://github.com/homarr-labs/homarr/issues/5314)) ([6fa6ba3](https://github.com/homarr-labs/homarr/commit/6fa6ba385e6691fefbb9f045a0ff976bcd85de16))
* **deps:** update dependency drizzle-kit to ^0.31.10 ([#5291](https://github.com/homarr-labs/homarr/issues/5291)) ([09227a3](https://github.com/homarr-labs/homarr/commit/09227a3884d291032f1012b59ce06babc3c87103))
* **deps:** update dependency fast-xml-parser to ^5.5.6 ([#5297](https://github.com/homarr-labs/homarr/issues/5297)) ([991044e](https://github.com/homarr-labs/homarr/commit/991044ebd78288bea7b441d3c14b159b5e66915b))
* **deps:** update dependency fast-xml-parser to ^5.5.7 ([#5315](https://github.com/homarr-labs/homarr/issues/5315)) ([2336f9e](https://github.com/homarr-labs/homarr/commit/2336f9ee5e35f7b610743dee55b91caf8434c1e4))
* **deps:** update dependency ioredis to v5.10.1 ([#5316](https://github.com/homarr-labs/homarr/issues/5316)) ([47b2d36](https://github.com/homarr-labs/homarr/commit/47b2d36f7a0933b6a6bbb61a449888be9304e1c5))
* **deps:** update dependency isomorphic-dompurify to ^3.4.0 ([#5292](https://github.com/homarr-labs/homarr/issues/5292)) ([6276449](https://github.com/homarr-labs/homarr/commit/62764499e7bbc9995ecf3de7351c9224d6aee4b6))
* **deps:** update dependency isomorphic-dompurify to ^3.5.0 ([#5299](https://github.com/homarr-labs/homarr/issues/5299)) ([b6b11c3](https://github.com/homarr-labs/homarr/commit/b6b11c39d2b4a33c5732ae239d7c1aaba6003cc6))
* **deps:** update dependency isomorphic-dompurify to ^3.5.1 ([#5300](https://github.com/homarr-labs/homarr/issues/5300)) ([94dfddf](https://github.com/homarr-labs/homarr/commit/94dfddf8bde2966b46bcd2d30e5e4f886ebe4605))
* **deps:** update dependency isomorphic-dompurify to ^3.6.0 ([#5334](https://github.com/homarr-labs/homarr/issues/5334)) ([8d64cb8](https://github.com/homarr-labs/homarr/commit/8d64cb8a5e276677b033774152c921aedc1e6c8a))
* **deps:** update dependency mysql2 to v3.20.0 ([#5283](https://github.com/homarr-labs/homarr/issues/5283)) ([4fb48c2](https://github.com/homarr-labs/homarr/commit/4fb48c23a2ccb1c047bab0d354933561e81cc3ed))
* **deps:** update dependency swagger-ui-react to ^5.32.1 ([#5296](https://github.com/homarr-labs/homarr/issues/5296)) ([63c55ce](https://github.com/homarr-labs/homarr/commit/63c55cec5711c41544c65ab83f24d09e63b10354))
* **deps:** update dependency trpc-to-openapi to ^3.2.0 ([#5320](https://github.com/homarr-labs/homarr/issues/5320)) ([22f5d3b](https://github.com/homarr-labs/homarr/commit/22f5d3b95959cf0a835c9750b41106eaafc4432e))
* **deps:** update dependency typescript-eslint to ^8.57.1 ([#5286](https://github.com/homarr-labs/homarr/issues/5286)) ([d4e4c96](https://github.com/homarr-labs/homarr/commit/d4e4c9669d648632a055b5ec0515821b50821142))
* **deps:** update dependency undici to v7.24.5 ([#5338](https://github.com/homarr-labs/homarr/issues/5338)) ([22bbef9](https://github.com/homarr-labs/homarr/commit/22bbef9d8e03215abddf6c6e26a5df45f252626d))
* **deps:** update dependency ws to ^8.20.0 ([#5337](https://github.com/homarr-labs/homarr/issues/5337)) ([d53c922](https://github.com/homarr-labs/homarr/commit/d53c922688606bd7d9494089e1ec43b930ad9923))
* **deps:** update mantine monorepo to ^8.3.18 ([#5298](https://github.com/homarr-labs/homarr/issues/5298)) ([6f8e629](https://github.com/homarr-labs/homarr/commit/6f8e62963a4eb47cb3095dff46ee0e759e1cbd3c))
* **deps:** update nextjs monorepo to v16.2.0 ([#5309](https://github.com/homarr-labs/homarr/issues/5309)) ([8831613](https://github.com/homarr-labs/homarr/commit/883161357dab4d94ff794f9444040d88074c464c))
* **deps:** update nextjs monorepo to v16.2.1 ([#5330](https://github.com/homarr-labs/homarr/issues/5330)) ([e23a397](https://github.com/homarr-labs/homarr/commit/e23a397a80e4409f5da50c737aeb26b295ce79fc))
* **deps:** update testcontainers-node monorepo to ^11.13.0 ([#5293](https://github.com/homarr-labs/homarr/issues/5293)) ([c009dd7](https://github.com/homarr-labs/homarr/commit/c009dd7de649bf24ab9bcf5ea25c65ebd18540bb))
* **deps:** update tiptap monorepo to v3.20.4 ([#5287](https://github.com/homarr-labs/homarr/issues/5287)) ([369b4d4](https://github.com/homarr-labs/homarr/commit/369b4d486327d0eaa04b70dd692c426f9a39afd7))
* **deps:** update trpc monorepo to ^11.14.1 ([#5328](https://github.com/homarr-labs/homarr/issues/5328)) ([d7f0596](https://github.com/homarr-labs/homarr/commit/d7f0596db8e44e2e79bb26095abd142b50675cc3))
* **deps:** update turbo monorepo to ^2.8.18 ([#5308](https://github.com/homarr-labs/homarr/issues/5308)) ([24694b9](https://github.com/homarr-labs/homarr/commit/24694b92411aae818b97a3a5c9add97d707a2f38))
* **deps:** update turbo monorepo to ^2.8.19 ([#5310](https://github.com/homarr-labs/homarr/issues/5310)) ([7537184](https://github.com/homarr-labs/homarr/commit/7537184a8168a4837d0a83a925a70c938555f199))
* **deps:** update turbo monorepo to ^2.8.20 ([#5317](https://github.com/homarr-labs/homarr/issues/5317)) ([5b4e6fc](https://github.com/homarr-labs/homarr/commit/5b4e6fcc11c877815c4730607de84ab70d630fb8))
* handle null ports in Docker container table ([#5313](https://github.com/homarr-labs/homarr/issues/5313)) ([1639da5](https://github.com/homarr-labs/homarr/commit/1639da57794d2555426e56030b4b34e2cc201aa9)), closes [#5311](https://github.com/homarr-labs/homarr/issues/5311)
* warnings in code ([4f2b821](https://github.com/homarr-labs/homarr/commit/4f2b8217f819b91ec4b05e32aebd6bc5473db155))
## [1.56.1](https://github.com/homarr-labs/homarr/compare/v1.56.0...v1.56.1) (2026-03-20)
### Bug Fixes
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:24.14.0-alpine AS base
FROM node:24.14.1-alpine AS base
FROM base AS builder
RUN apk add --no-cache libc6-compat
+57 -56
View File
@@ -15,10 +15,10 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@dnd-kit/core": "catalog:",
"@dnd-kit/modifiers": "catalog:",
"@dnd-kit/sortable": "catalog:",
"@dnd-kit/utilities": "catalog:",
"@homarr/analytics": "workspace:^0.1.0",
"@homarr/api": "workspace:^0.1.0",
"@homarr/auth": "workspace:^0.1.0",
@@ -31,7 +31,7 @@
"@homarr/docker": "workspace:^0.1.0",
"@homarr/form": "workspace:^0.1.0",
"@homarr/forms-collection": "workspace:^0.1.0",
"@homarr/gridstack": "^1.12.0",
"@homarr/gridstack": "catalog:",
"@homarr/icons": "workspace:^0.1.0",
"@homarr/image-proxy": "workspace:^0.1.0",
"@homarr/integrations": "workspace:^0.1.0",
@@ -48,61 +48,62 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"@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.91.2",
"@tanstack/react-query-devtools": "^5.91.3",
"@tanstack/react-query-next-experimental": "^5.91.0",
"@trpc/client": "^11.13.4",
"@trpc/next": "^11.13.4",
"@trpc/react-query": "^11.13.4",
"@trpc/server": "^11.13.4",
"@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.20",
"dotenv": "^17.3.1",
"flag-icons": "^7.5.0",
"glob": "^13.0.6",
"isomorphic-dompurify": "^3.5.1",
"jotai": "^2.18.1",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.2.0",
"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.98.0",
"superjson": "2.2.6",
"swagger-ui-react": "^5.32.1",
"use-deep-compare-effect": "^1.8.1",
"zod": "^4.2.1"
"@mantine/colors-generator": "catalog:",
"@mantine/core": "catalog:",
"@mantine/dropzone": "catalog:",
"@mantine/hooks": "catalog:",
"@mantine/modals": "catalog:",
"@mantine/tiptap": "catalog:",
"@million/lint": "catalog:",
"@tabler/icons-react": "catalog:",
"@tanstack/react-query": "catalog:",
"@tanstack/react-query-devtools": "catalog:",
"@tanstack/react-query-next-experimental": "catalog:",
"@trpc/client": "catalog:",
"@trpc/next": "catalog:",
"@trpc/react-query": "catalog:",
"@trpc/server": "catalog:",
"@xterm/addon-canvas": "catalog:",
"@xterm/addon-fit": "catalog:",
"@xterm/xterm": "catalog:",
"babel-plugin-react-compiler": "catalog:",
"chroma-js": "catalog:",
"clsx": "catalog:",
"dayjs": "catalog:",
"dotenv": "catalog:",
"flag-icons": "catalog:",
"glob": "catalog:",
"isomorphic-dompurify": "catalog:",
"jotai": "catalog:",
"mantine-react-table": "catalog:",
"next": "catalog:",
"postcss-preset-mantine": "catalog:",
"prismjs": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"react-error-boundary": "catalog:",
"react-simple-code-editor": "catalog:",
"sass": "catalog:",
"superjson": "catalog:",
"swagger-ui-react": "catalog:",
"use-deep-compare-effect": "catalog:",
"yaml": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/chroma-js": "3.1.2",
"@types/node": "^24.12.0",
"@types/prismjs": "^1.26.6",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"@types/swagger-ui-react": "^5.18.0",
"concurrently": "^9.2.1",
"eslint": "^9.39.4",
"node-loader": "^2.1.0",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
"@types/chroma-js": "catalog:",
"@types/node": "catalog:",
"@types/prismjs": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/swagger-ui-react": "catalog:",
"concurrently": "catalog:",
"eslint": "catalog:",
"node-loader": "catalog:",
"prettier": "catalog:",
"typescript": "catalog:"
}
}
@@ -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;
};
@@ -1,3 +1,7 @@
.contributorCard {
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
}
.dependencyCard:hover {
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
}
@@ -12,8 +12,7 @@ import {
Flex,
Group,
Kbd,
List,
ListItem,
SimpleGrid,
Stack,
Table,
TableTbody,
@@ -22,9 +21,11 @@ import {
TableThead,
TableTr,
Text,
ThemeIcon,
Title,
UnstyledButton,
} from "@mantine/core";
import { IconKeyboard, IconLanguage, IconLibrary, IconUsers } from "@tabler/icons-react";
import { IconKeyboard, IconLanguage, IconLibrary, IconPackage, IconUsers } from "@tabler/icons-react";
import { capitalize, objectEntries } from "@homarr/common";
import { hotkeys } from "@homarr/definitions";
@@ -33,7 +34,8 @@ import { getScopedI18n } from "@homarr/translation/server";
import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { createMetaTitle } from "~/metadata";
import { getPackageAttributesAsync } from "~/versions/package-reader";
import type { PackageJsonDependencies } from "~/versions/package-reader";
import { getPackageVersion } from "~/versions/package-reader";
import type githubContributorsJson from "../../../../../../../static-data/contributors.json";
import type crowdinContributorsJson from "../../../../../../../static-data/translators.json";
import classes from "./about.module.css";
@@ -57,7 +59,10 @@ const getHostAsync = async () => {
export default async function AboutPage() {
const baseServerUrl = `http://${await getHostAsync()}`;
const t = await getScopedI18n("management.page.about");
const attributes = await getPackageAttributesAsync();
const version = getPackageVersion();
const dependencies = (await fetch(`${baseServerUrl}/api/about/dependencies`).then((res) =>
res.json(),
)) as PackageJsonDependencies;
const githubContributors = (await fetch(`${baseServerUrl}/api/about/contributors/github`).then((res) =>
res.json(),
)) as typeof githubContributorsJson;
@@ -76,7 +81,7 @@ export default async function AboutPage() {
<Title order={1} tt="uppercase">
Homarr
</Title>
<Title order={2}>{t("version", { version: attributes.version })}</Title>
<Title order={2}>{t("version", { version })}</Title>
</Stack>
</Group>
</Center>
@@ -137,25 +142,42 @@ export default async function AboutPage() {
<Text>{t("accordion.libraries.title")}</Text>
<Text size="sm" c="dimmed">
{t("accordion.libraries.subtitle", {
count: String(Object.keys(attributes.dependencies).length),
count: String(Object.keys(dependencies).length),
})}
</Text>
</Stack>
</AccordionControl>
<AccordionPanel>
<List>
{Object.entries(attributes.dependencies)
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }} spacing="md">
{Object.entries(dependencies)
.filter(([, value]) => !value.includes("workspace:"))
.sort(([key1], [key2]) => key1.localeCompare(key2))
.map(([key, value]) => (
<ListItem key={key}>
{value.includes("workspace:") ? (
<Text>{key}</Text>
) : (
<a href={`https://www.npmjs.com/package/${key}`}>{key}</a>
)}
</ListItem>
.map(([name, version]) => (
<UnstyledButton
key={name}
component="a"
href={`https://www.npmjs.com/package/${name}`}
target="_blank"
>
<Card withBorder radius="md" p="xs" className={classes.dependencyCard}>
<Group gap="sm" wrap="nowrap">
<ThemeIcon variant="light" size="lg" radius="md">
<IconPackage size="1.5rem" stroke={1.5} />
</ThemeIcon>
<Stack gap={0}>
<Text size="sm" fw="bold" lineClamp={1} title={name}>
{name}
</Text>
<Text size="sm" c="dimmed">
v{version.replace("^", "").replace("~", "")}
</Text>
</Stack>
</Group>
</Card>
</UnstyledButton>
))}
</List>
</SimpleGrid>
</AccordionPanel>
</AccordionItem>
<AccordionItem value="hotkeys">
@@ -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();
}
@@ -0,0 +1,10 @@
import { NextResponse } from "next/server";
import { getDependenciesAsync } from "../../../../versions/package-reader";
export const GET = async () => {
const dependencies = await getDependenciesAsync();
return NextResponse.json(dependencies);
};
export const dynamic = "force-static";
@@ -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]);
/**
+30 -10
View File
@@ -1,12 +1,16 @@
import fsPromises from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { glob } from "glob";
import { parse as parseYaml } from "yaml";
import z from "zod";
import packageJson from "../../../../package.json";
const getPackageVersion = () => packageJson.version;
const getDependenciesAsync = async (): Promise<PackageJsonDependencies> => {
export const getPackageVersion = () => packageJson.version;
export const getDependenciesAsync = async (): Promise<PackageJsonDependencies> => {
const pathNames = await glob("**/package.json", {
ignore: "node_modules/**",
ignore: "**/node_modules/**",
cwd: "../../",
absolute: true,
});
@@ -15,21 +19,37 @@ const getDependenciesAsync = async (): Promise<PackageJsonDependencies> => {
.map((packageContent) => (JSON.parse(packageContent) as PackageJson).dependencies)
.filter((dependencies) => dependencies !== undefined);
const catalog = await parseDependencyCatalogsAsync();
let dependencies = {};
for (const dependenciesOfPackage of packageDependencies) {
dependencies = { ...dependencies, ...dependenciesOfPackage };
const resolvedDependencies = Object.entries(dependenciesOfPackage).map(([name, version]) => {
const catalogVersion = catalog.get(name);
return [name, catalogVersion ?? version] as const;
});
dependencies = { ...dependencies, ...Object.fromEntries(resolvedDependencies) };
}
return dependencies;
};
export const getPackageAttributesAsync = async () => {
return {
version: getPackageVersion(),
dependencies: await getDependenciesAsync(),
};
type DependencyCatalog = Map<string, string>;
const parseDependencyCatalogsAsync = async (): Promise<DependencyCatalog> => {
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const workspaceConfigContent = await fsPromises.readFile(
path.join(currentDir, "..", "..", "..", "..", "pnpm-workspace.yaml"),
"utf-8",
);
const workspaceConfig: unknown = parseYaml(workspaceConfigContent);
const parseResult = workspaceConfigCatalogSchema.parse(workspaceConfig);
return new Map(Object.entries(parseResult.catalog ?? {}));
};
type PackageJsonDependencies = Record<string, string>;
const workspaceConfigCatalogSchema = z.object({
catalog: z.record(z.string(), z.string()).optional(),
});
export type PackageJsonDependencies = Record<string, string>;
interface PackageJson {
dependencies: PackageJsonDependencies | undefined;
}
+12 -12
View File
@@ -37,22 +37,22 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
"dayjs": "^1.11.20",
"dotenv": "^17.3.1",
"fastify": "^5.8.2",
"superjson": "2.2.6",
"undici": "7.18.2"
"dayjs": "catalog:",
"dotenv": "catalog:",
"fastify": "catalog:",
"superjson": "catalog:",
"undici": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node": "^24.12.0",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"tsx": "4.20.4",
"typescript": "^5.9.3"
"@types/node": "catalog:",
"dotenv-cli": "catalog:",
"esbuild": "catalog:",
"eslint": "catalog:",
"prettier": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:"
}
}
+8 -8
View File
@@ -25,18 +25,18 @@
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"dotenv": "^17.3.1",
"tsx": "4.20.4",
"ws": "^8.19.0"
"dotenv": "catalog:",
"tsx": "catalog:",
"ws": "catalog:"
},
"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.4",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
"@types/ws": "catalog:",
"esbuild": "catalog:",
"eslint": "catalog:",
"prettier": "catalog:",
"typescript": "catalog:"
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:24.14.0-trixie AS base
FROM node:24.14.1-trixie AS base
WORKDIR /app
COPY package.json .
COPY pnpm-lock.yaml .
+4 -4
View File
@@ -2,12 +2,12 @@
"name": "homarr-prebuilt-debian",
"private": true,
"dependencies": {
"better-sqlite3": "^12.6.2"
"better-sqlite3": "^12.8.0"
},
"packageManager": "pnpm@10.32.1",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">=24.14.0",
"pnpm": ">=10.32.1"
"node": ">=24.14.1",
"pnpm": ">=10.33.0"
},
"pnpm": {
"onlyBuiltDependencies": [
+6 -5
View File
@@ -9,16 +9,16 @@ importers:
.:
dependencies:
better-sqlite3:
specifier: ^12.6.2
version: 12.6.2
specifier: ^12.8.0
version: 12.8.0
packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
better-sqlite3@12.6.2:
resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==}
better-sqlite3@12.8.0:
resolution: {integrity: sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==}
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
bindings@1.5.0:
@@ -93,6 +93,7 @@ packages:
prebuild-install@7.1.3:
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
engines: {node: '>=10'}
deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
hasBin: true
pump@3.0.3:
@@ -147,7 +148,7 @@ snapshots:
base64-js@1.5.1: {}
better-sqlite3@12.6.2:
better-sqlite3@12.8.0:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.3
+45 -24
View File
@@ -64,6 +64,13 @@
</a>
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/anchor" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/anchor.svg" alt="Anchor" width="90" height="90" />
<br/>
<p align="center">Anchor</p>
</a>
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/aria2" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/PapirusDevelopmentTeam/papirus_icons@latest/src/system_downloads_3.svg" alt="Aria2" width="90" height="90" />
<br/>
@@ -97,15 +104,15 @@
<br/>
<p align="center">Deluge</p>
</a>
</td>
<td align="center">
</td></tr>
<tr><td align="center">
<a href="https://homarr.dev/docs/integrations/docker-hub" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/docker.svg" alt="Docker Hub" width="90" height="90" />
<br/>
<p align="center">Docker<br/>Hub</p>
</a>
</td></tr>
<tr><td align="center">
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/emby" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/emby.svg" alt="Emby" width="90" height="90" />
<br/>
@@ -146,15 +153,15 @@
<br/>
<p align="center">Home<br/>Assistant</p>
</a>
</td>
<td align="center">
</td></tr>
<tr><td align="center">
<a href="https://homarr.dev/docs/integrations/ical" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/ical.svg" alt="iCal" width="90" height="90" />
<br/>
<p align="center">iCal</p>
</a>
</td></tr>
<tr><td align="center">
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/immich" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/immich.svg" alt="Immich" width="90" height="90" />
<br/>
@@ -195,15 +202,15 @@
<br/>
<p align="center">Nextcloud</p>
</a>
</td>
<td align="center">
</td></tr>
<tr><td align="center">
<a href="https://homarr.dev/docs/integrations/npm" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/npm.svg" alt="NPM" width="90" height="90" />
<br/>
<p align="center">NPM</p>
</a>
</td></tr>
<tr><td align="center">
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/ntfy" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/ntfy.svg" alt="ntfy" width="90" height="90" />
<br/>
@@ -244,15 +251,15 @@
<br/>
<p align="center">Pi-hole</p>
</a>
</td>
<td align="center">
</td></tr>
<tr><td align="center">
<a href="https://homarr.dev/docs/integrations/plex" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/plex.svg" alt="Plex" width="90" height="90" />
<br/>
<p align="center">Plex</p>
</a>
</td></tr>
<tr><td align="center">
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/prowlarr" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/prowlarr.svg" alt="Prowlarr" width="90" height="90" />
<br/>
@@ -293,15 +300,22 @@
<br/>
<p align="center">Readarr</p>
</a>
</td>
<td align="center">
</td></tr>
<tr><td align="center">
<a href="https://homarr.dev/docs/integrations/sabnzbd" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sabnzbd.svg" alt="SABnzbd" width="90" height="90" />
<br/>
<p align="center">SABnzbd</p>
</a>
</td></tr>
<tr><td align="center">
</td>
<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/>
@@ -323,13 +337,20 @@
</a>
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/speedtest-tracker" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png" alt="Speedtest Tracker" width="90" height="90" />
<br/>
<p align="center">Speedtest<br/>Tracker</p>
</a>
</td>
<td align="center">
<a href="https://homarr.dev/docs/integrations/tdarr" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/tdarr.png" alt="Tdarr" width="90" height="90" />
<br/>
<p align="center">Tdarr</p>
</a>
</td>
<td align="center">
</td></tr>
<tr><td align="center">
<a href="https://homarr.dev/docs/integrations/tracearr" target="_blank" rel="noreferrer noopener">
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/tracearr.svg" alt="Tracearr" width="90" height="90" />
<br/>
@@ -349,8 +370,8 @@
<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/>
+35 -35
View File
@@ -1,6 +1,6 @@
{
"name": "homarr",
"version": "1.56.1",
"version": "1.59.2",
"private": true,
"scripts": {
"build": "cross-env CI=true turbo build",
@@ -36,33 +36,33 @@
"prettier": "@homarr/prettier-config",
"devDependencies": {
"@homarr/prettier-config": "workspace:^0.1.0",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/commit-analyzer": "^13.0.1",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^12.0.6",
"@semantic-release/npm": "^13.1.5",
"@semantic-release/release-notes-generator": "^14.1.0",
"@testcontainers/redis": "^11.13.0",
"@turbo/gen": "^2.8.20",
"@vitejs/plugin-react": "^5.2.0",
"@vitest/coverage-v8": "^4.1.0",
"@vitest/ui": "^4.1.0",
"conventional-changelog-conventionalcommits": "^9.3.0",
"cross-env": "^10.1.0",
"jsdom": "^28.1.0",
"json5": "^2.2.3",
"prettier": "^3.8.1",
"semantic-release": "^25.0.3",
"testcontainers": "^11.13.0",
"turbo": "^2.8.20",
"typescript": "^5.9.3",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.1.0"
"@semantic-release/changelog": "catalog:",
"@semantic-release/commit-analyzer": "catalog:",
"@semantic-release/git": "catalog:",
"@semantic-release/github": "catalog:",
"@semantic-release/npm": "catalog:",
"@semantic-release/release-notes-generator": "catalog:",
"@testcontainers/redis": "catalog:",
"@turbo/gen": "catalog:",
"@vitejs/plugin-react": "catalog:",
"@vitest/coverage-v8": "catalog:",
"@vitest/ui": "catalog:",
"conventional-changelog-conventionalcommits": "catalog:",
"cross-env": "catalog:",
"jsdom": "catalog:",
"json5": "catalog:",
"prettier": "catalog:",
"semantic-release": "catalog:",
"testcontainers": "catalog:",
"turbo": "catalog:",
"typescript": "catalog:",
"vite-tsconfig-paths": "catalog:",
"vitest": "catalog:"
},
"packageManager": "pnpm@10.32.1",
"packageManager": "pnpm@10.33.0",
"engines": {
"node": ">=24.14.0",
"pnpm": ">=10.32.1"
"node": ">=24.14.1",
"pnpm": ">=10.33.0"
},
"pnpm": {
"onlyBuiltDependencies": [
@@ -79,23 +79,23 @@
"overrides": {
"@babel/helpers@<7.26.10": ">=7.29.2",
"@babel/runtime@<7.26.10": ">=7.29.2",
"axios@>=1.0.0 <1.8.2": ">=1.13.6",
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.4",
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.4",
"esbuild@<=0.24.2": ">=0.27.4",
"axios@>=1.0.0 <1.8.2": ">=1.15.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.28.0",
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
"hono@<4.6.5": ">=4.12.8",
"hono@<4.6.5": ">=4.12.12",
"linkifyjs@<4.3.2": ">=4.3.2",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.7",
"prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.24.0",
"react-is": "^19.2.4",
"rollup@>=4.0.0 <4.22.4": ">=4.59.0",
"proxmox-api>undici": "7.24.6",
"react-is": "^19.2.5",
"rollup@>=4.0.0 <4.22.4": ">=4.60.1",
"sha.js@<=2.4.11": ">=2.4.12",
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
"tmp@<=0.2.3": ">=0.2.5",
"vite@>=5.0.0 <=5.4.18": ">=8.0.1"
"vite@>=5.0.0 <=5.4.18": ">=8.0.8"
},
"patchedDependencies": {
"@types/node-unifi": "patches/@types__node-unifi.patch",
+4 -4
View File
@@ -28,14 +28,14 @@
"@homarr/db": "workspace:^0.1.0",
"@homarr/docker": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@umami/node": "^0.4.0",
"superjson": "2.2.6"
"@umami/node": "catalog:",
"superjson": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+16 -16
View File
@@ -40,26 +40,26 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@kubernetes/client-node": "^1.4.0",
"@tanstack/react-query": "^5.91.2",
"@trpc/client": "^11.13.4",
"@trpc/react-query": "^11.13.4",
"@trpc/server": "^11.13.4",
"@trpc/tanstack-react-query": "^11.13.4",
"lodash.clonedeep": "^4.5.0",
"next": "16.2.0",
"react": "19.2.4",
"react-dom": "19.2.4",
"superjson": "2.2.6",
"trpc-to-openapi": "^3.1.0",
"zod": "^4.2.1"
"@kubernetes/client-node": "catalog:",
"@tanstack/react-query": "catalog:",
"@trpc/client": "catalog:",
"@trpc/react-query": "catalog:",
"@trpc/server": "catalog:",
"@trpc/tanstack-react-query": "catalog:",
"lodash.clonedeep": "catalog:",
"next": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"superjson": "catalog:",
"trpc-to-openapi": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
"eslint": "catalog:",
"prettier": "catalog:",
"typescript": "catalog:"
}
}
+2 -5
View File
@@ -1,6 +1,6 @@
import { z } from "zod/v4";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { hashPasswordAsync } from "@homarr/auth";
import { createId } from "@homarr/common";
import { generateSecureRandomToken } from "@homarr/common/server";
import { db, eq } from "@homarr/db";
@@ -14,7 +14,6 @@ export const apiKeysRouter = createTRPCRouter({
columns: {
id: true,
apiKey: false,
salt: false,
},
with: {
user: {
@@ -29,14 +28,12 @@ export const apiKeysRouter = createTRPCRouter({
});
}),
create: permissionRequiredProcedure.requiresPermission("admin").mutation(async ({ ctx }) => {
const salt = await createSaltAsync();
const randomToken = generateSecureRandomToken(64);
const hashedRandomToken = await hashPasswordAsync(randomToken, salt);
const hashedRandomToken = await hashPasswordAsync(randomToken);
const id = createId();
await db.insert(apiKeys).values({
id,
apiKey: hashedRandomToken,
salt,
userId: ctx.session.user.id,
});
return {
@@ -140,7 +140,7 @@ interface DockerContainer {
id: string;
state: ContainerState;
image: string;
ports: Port[];
ports: Port[] | undefined;
iconUrl: string | null;
cpuUsage: number;
memoryUsage: number;
@@ -208,6 +208,7 @@ async function setupUrlRestrictionAsync(dbUrls: string[]) {
hideDescription: false,
textLinesClamp: 3,
maximumAmountPosts: 5,
showPosterImage: true,
} satisfies WidgetComponentProps<"rssFeed">["options"]),
}) satisfies InferInsertModel<typeof items>,
),
+8 -15
View File
@@ -3,7 +3,7 @@ import type { MySqlRawQueryResult } from "drizzle-orm/mysql2";
import type { QueryResult } from "pg";
import { z } from "zod/v4";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { comparePasswordsAsync, hashPasswordAsync } from "@homarr/auth";
import { createId } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Database } from "@homarr/db";
@@ -11,8 +11,8 @@ 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";
import { credentialsAdminGroup } from "@homarr/definitions";
import type { SupportedAuthProvider } from "@homarr/definitions";
import { credentialsAdminGroup } from "@homarr/definitions";
import { byIdSchema } from "@homarr/validation/common";
import type { userBaseCreateSchema } from "@homarr/validation/user";
import {
@@ -92,15 +92,13 @@ export const userRouter = createTRPCRouter({
await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.username);
const salt = await createSaltAsync();
const hashedPassword = await hashPasswordAsync(input.password, salt);
const hashedPassword = await hashPasswordAsync(input.password);
const userId = createId();
const user = {
id: userId,
name: input.username,
password: hashedPassword,
salt,
};
await handleTransactionsAsync(ctx.db, {
@@ -145,7 +143,7 @@ export const userRouter = createTRPCRouter({
create: permissionRequiredProcedure
.requiresPermission("admin")
.meta({ openapi: { method: "POST", path: "/api/users", tags: ["users"], protect: true } })
.input(userCreateSchema)
.input(convertIntersectionToZodObject(userCreateSchema))
.output(z.void())
.mutation(async ({ ctx, input }) => {
throwIfCredentialsDisabled();
@@ -376,7 +374,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 }) => {
@@ -393,7 +391,6 @@ export const userRouter = createTRPCRouter({
columns: {
id: true,
password: true,
salt: true,
provider: true,
},
where: eq(users.id, input.userId),
@@ -423,8 +420,7 @@ export const userRouter = createTRPCRouter({
});
if (isPreviousPasswordRequired) {
const previousPasswordHash = await hashPasswordAsync(input.previousPassword, dbUser.salt ?? "");
const isValid = previousPasswordHash === dbUser.password;
const isValid = await comparePasswordsAsync(input.previousPassword, dbUser.password ?? "");
if (!isValid) {
throw new TRPCError({
@@ -434,8 +430,7 @@ export const userRouter = createTRPCRouter({
}
}
const salt = await createSaltAsync();
const hashedPassword = await hashPasswordAsync(input.password, salt);
const hashedPassword = await hashPasswordAsync(input.password);
await ctx.db
.update(users)
.set({
@@ -583,8 +578,7 @@ export const userRouter = createTRPCRouter({
});
const createUserAsync = async (db: Database, input: Omit<z.infer<typeof userBaseCreateSchema>, "groupIds">) => {
const salt = await createSaltAsync();
const hashedPassword = await hashPasswordAsync(input.password, salt);
const hashedPassword = await hashPasswordAsync(input.password);
const userId = createId();
await db.insert(users).values({
@@ -592,7 +586,6 @@ const createUserAsync = async (db: Database, input: Omit<z.infer<typeof userBase
name: input.username,
email: input.email,
password: hashedPassword,
salt,
});
return userId;
};
@@ -0,0 +1,114 @@
import { TRPCError } from "@trpc/server";
import { z } from "zod/v4";
import { ResponseError } from "@homarr/common/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { anchorNotesListInputSchema, anchorNoteUpdateInputSchema, createIntegrationAsync } from "@homarr/integrations";
import { anchorNoteRequestHandler, anchorNotesListRequestHandler } from "@homarr/request-handler/anchor-notes";
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../../trpc";
const noteIdInput = z.object({
noteId: z.string(),
});
const logger = createLogger({ module: "anchorNotesRouter" });
const isJsonDeltaString = (value: string) => {
try {
const parsed: unknown = JSON.parse(value);
return typeof parsed === "object" && parsed !== null && Array.isArray((parsed as { ops?: unknown }).ops);
} catch {
return false;
}
};
const normalizeAnchorContent = (content: string | undefined) => {
if (content === undefined) return undefined;
const trimmed = content.trim();
if (!trimmed) {
return JSON.stringify({ ops: [{ insert: "\n" }] });
}
if (isJsonDeltaString(content)) return content;
const normalizedText = content.endsWith("\n") ? content : `${content}\n`;
return JSON.stringify({ ops: [{ insert: normalizedText }] });
};
export const anchorNotesRouter = createTRPCRouter({
listNotes: publicProcedure
.concat(createOneIntegrationMiddleware("query", "anchor"))
.input(anchorNotesListInputSchema)
.query(async ({ ctx, input }) => {
const handler = anchorNotesListRequestHandler.handler(ctx.integration, { ...input, limit: input.limit ?? 50 });
const { data } = await handler.getCachedOrUpdatedDataAsync({
forceUpdate: false,
});
return data;
}),
getNote: publicProcedure
.concat(createOneIntegrationMiddleware("query", "anchor"))
.input(noteIdInput)
.query(async ({ ctx, input }) => {
const handler = anchorNoteRequestHandler.handler(ctx.integration, { noteId: input.noteId });
const { data } = await handler.getCachedOrUpdatedDataAsync({
forceUpdate: false,
});
return data;
}),
updateNote: protectedProcedure
.concat(createOneIntegrationMiddleware("interact", "anchor"))
.input(anchorNoteUpdateInputSchema)
.mutation(async ({ ctx, input }) => {
const integrationInstance = await createIntegrationAsync(ctx.integration);
const normalizedContent = normalizeAnchorContent(input.content);
const isContentNormalized =
input.content !== undefined && normalizedContent !== undefined && input.content !== normalizedContent;
try {
const updatedNote = await integrationInstance.updateNoteAsync({
noteId: input.noteId,
...(input.title !== undefined ? { title: input.title } : {}),
...(normalizedContent !== undefined ? { content: normalizedContent } : {}),
});
const noteHandler = anchorNoteRequestHandler.handler(ctx.integration, { noteId: input.noteId });
await noteHandler.invalidateAsync();
return updatedNote;
} catch (error) {
if (error instanceof ResponseError && error.statusCode === 400) {
logger.warn("Anchor update note failed validation", {
noteId: input.noteId,
integrationId: ctx.integration.id,
contentLength: input.content?.length ?? 0,
contentIsJsonDelta: input.content ? isJsonDeltaString(input.content) : false,
contentNormalized: isContentNormalized,
});
}
if (error instanceof ResponseError && error.statusCode === 403) {
throw new TRPCError({
code: "FORBIDDEN",
message: "You do not have permission to edit this note",
});
}
if (error instanceof ResponseError && error.statusCode === 404) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Note not found",
});
}
throw error;
}
}),
});
+3
View File
@@ -3,6 +3,7 @@ import { lazy } from "@trpc/server";
import { createTRPCRouter } from "../../trpc";
export const widgetRouter = createTRPCRouter({
anchorNotes: lazy(() => import("./anchor-notes").then((mod) => mod.anchorNotesRouter)),
coolify: lazy(() => import("./coolify").then((mod) => mod.coolifyRouter)),
immich: lazy(() => import("./immich").then((mod) => mod.immichRouter)),
notebook: lazy(() => import("./notebook").then((mod) => mod.notebookRouter)),
@@ -26,5 +27,7 @@ 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)),
speedtestTracker: lazy(() => import("./speedtest-tracker").then((mod) => mod.speedtestTrackerRouter)),
});
+46 -31
View File
@@ -1,3 +1,4 @@
import { observable } from "@trpc/server/observable";
import SuperJSON from "superjson";
import { z } from "zod/v4";
@@ -5,45 +6,59 @@ import type { Session } from "@homarr/auth";
import type { Database } from "@homarr/db";
import { eq } from "@homarr/db";
import { items, users } from "@homarr/db/schema";
import type { ExtendedFeedEntry } from "@homarr/request-handler/rss-feeds";
import { rssFeedsRequestHandler } from "@homarr/request-handler/rss-feeds";
import type { WidgetComponentProps } from "../../../../widgets/src";
import { createTRPCRouter, publicProcedure } from "../../trpc";
const feedsInput = z.object({
urls: z.array(z.string()).max(100),
maximumAmountPosts: z.number(),
});
export const rssFeedRouter = createTRPCRouter({
getFeeds: publicProcedure
.input(
z.object({
urls: z.array(z.string()).max(100),
maximumAmountPosts: z.number(),
}),
)
.query(async ({ ctx, input }) => {
const urls = (await canAccessAllFeedsAsync(ctx.db, ctx.session))
? input.urls
: await restrictUrlsAsync(ctx.db, input.urls);
getFeeds: publicProcedure.input(feedsInput).query(async ({ ctx, input }) => {
const urls = (await canAccessAllFeedsAsync(ctx.db, ctx.session))
? input.urls
: await restrictUrlsAsync(ctx.db, input.urls);
const rssFeeds = await Promise.all(
urls.map(async (url) => {
const innerHandler = rssFeedsRequestHandler.handler({
url,
count: input.maximumAmountPosts,
});
return await innerHandler.getCachedOrUpdatedDataAsync({
forceUpdate: false,
});
}),
);
return rssFeeds
.flatMap((rssFeed) => rssFeed.data.entries)
.slice(0, input.maximumAmountPosts)
.sort((entryA, entryB) => {
return entryA.published && entryB.published
? new Date(entryB.published).getTime() - new Date(entryA.published).getTime()
: 0;
const rssFeeds = await Promise.all(
urls.map(async (url) => {
const innerHandler = rssFeedsRequestHandler.handler({
url,
count: input.maximumAmountPosts,
});
}),
return await innerHandler.getCachedOrUpdatedDataAsync({
forceUpdate: false,
});
}),
);
return rssFeeds
.flatMap((rssFeed) => rssFeed.data.entries)
.slice(0, input.maximumAmountPosts)
.sort((entryA, entryB) => {
return entryA.published && entryB.published
? new Date(entryB.published).getTime() - new Date(entryA.published).getTime()
: 0;
});
}),
subscribeFeeds: publicProcedure.input(feedsInput).subscription(({ input }) => {
// It is not necessary to check access to all feeds here, as the client will only subscribe to feeds which already exist
return observable<{ url: string; entries: ExtendedFeedEntry[] }>((emit) => {
const unsubscribes = input.urls.map((url) => {
const innerHandler = rssFeedsRequestHandler.handler({ url, count: input.maximumAmountPosts });
return innerHandler.subscribe((data) => {
emit.next({ url, entries: data.entries });
});
});
return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe());
};
});
}),
});
export async function canAccessAllFeedsAsync(db: Database, session: Session | null) {
@@ -0,0 +1,55 @@
import { observable } from "@trpc/server/observable";
import type { SpeedtestTrackerDashboardData } from "@homarr/integrations/types";
import { speedtestTrackerRequestHandler } from "@homarr/request-handler/speedtest-tracker";
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
import { createTRPCRouter, publicProcedure } from "../../trpc";
export const speedtestTrackerRouter = createTRPCRouter({
getDashboard: publicProcedure
.concat(createManyIntegrationMiddleware("query", "speedtestTracker"))
.query(async ({ ctx }) => {
const results = await Promise.all(
ctx.integrations.map(async (integration) => {
const innerHandler = speedtestTrackerRequestHandler.handler(integration, {});
const { data, timestamp } = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
return {
integrationId: integration.id,
integrationName: integration.name,
integrationUrl: integration.url,
dashboard: data,
updatedAt: timestamp,
};
}),
);
return results;
}),
subscribeToDashboard: publicProcedure
.concat(createManyIntegrationMiddleware("query", "speedtestTracker"))
.subscription(({ ctx }) => {
return observable<{
integrationId: string;
dashboard: SpeedtestTrackerDashboardData;
timestamp: Date;
}>((emit) => {
const unsubscribes = ctx.integrations.map((integration) => {
const innerHandler = speedtestTrackerRequestHandler.handler(integration, {});
return innerHandler.subscribe((dashboard) => {
emit.next({
integrationId: integration.id,
dashboard,
timestamp: new Date(),
});
});
});
return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe());
};
});
}),
});
@@ -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;
}),
});
+3 -4
View File
@@ -5,7 +5,7 @@ import type { Database } from "@homarr/db";
import { eq } from "@homarr/db";
import { apiKeys } from "@homarr/db/schema";
import { hashPasswordAsync } from "../security";
import { comparePasswordsAsync } from "../security";
import { createSessionAsync } from "../server";
const logger = createLogger({ module: "apiKeyAuth" });
@@ -41,7 +41,6 @@ export const getSessionFromApiKeyAsync = async (
columns: {
id: true,
apiKey: true,
salt: true,
},
with: {
user: {
@@ -60,9 +59,9 @@ export const getSessionFromApiKeyAsync = async (
return null;
}
const hashedApiKey = await hashPasswordAsync(apiKey, apiKeyFromDb.salt);
const isValid = await comparePasswordsAsync(apiKey, apiKeyFromDb.apiKey);
if (apiKeyFromDb.apiKey !== hashedApiKey) {
if (!isValid) {
logger.warn("Failed to authenticate with api-key", { ipAddress, userAgent, reason: "API_KEY_MISMATCH" });
return null;
}
@@ -5,7 +5,7 @@ import { createId } from "@homarr/common";
import { apiKeys, users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
import { createSaltAsync, hashPasswordAsync } from "../../security";
import { hashPasswordAsync } from "../../security";
import { getSessionFromApiKeyAsync } from "../get-api-key-session";
// Mock the logger to avoid console output during tests
@@ -118,11 +118,9 @@ const setupAsync = async (options?: SetupOptions) => {
});
if (options?.token) {
const salt = await createSaltAsync();
await db.insert(apiKeys).values({
id: defaultApiKeyId,
apiKey: await hashPasswordAsync(options.token, salt),
salt,
apiKey: await hashPasswordAsync(options.token),
userId: defaultUserId,
});
}
+15 -15
View File
@@ -24,30 +24,30 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@auth/core": "^0.41.1",
"@auth/drizzle-adapter": "^1.11.1",
"@auth/core": "catalog:",
"@auth/drizzle-adapter": "catalog:",
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"bcrypt": "^6.0.0",
"cookies": "^0.9.1",
"ldapts": "8.1.7",
"next": "16.2.0",
"next-auth": "5.0.0-beta.30",
"react": "19.2.4",
"react-dom": "19.2.4",
"zod": "^4.2.1"
"bcrypt": "catalog:",
"cookies": "catalog:",
"ldapts": "catalog:",
"next": "catalog:",
"next-auth": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/bcrypt": "6.0.0",
"@types/cookies": "0.9.2",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"typescript": "^5.9.3"
"@types/bcrypt": "catalog:",
"@types/cookies": "catalog:",
"eslint": "catalog:",
"prettier": "catalog:",
"typescript": "catalog:"
}
}
@@ -1,4 +1,3 @@
import bcrypt from "bcrypt";
import type { z } from "zod/v4";
import { createLogger } from "@homarr/core/infrastructure/logs";
@@ -7,6 +6,8 @@ import { and, eq } from "@homarr/db";
import { users } from "@homarr/db/schema";
import type { userSignInSchema } from "@homarr/validation/user";
import { comparePasswordsAsync } from "../../../security";
const logger = createLogger({ module: "basicAuthorization" });
export const authorizeWithBasicCredentialsAsync = async (
@@ -23,7 +24,7 @@ export const authorizeWithBasicCredentialsAsync = async (
}
logger.info("User is trying to log in. Checking password...", { userName: user.name });
const isValidPassword = await bcrypt.compare(credentials.password, user.password);
const isValidPassword = await comparePasswordsAsync(credentials.password, user.password);
if (!isValidPassword) {
logger.warn("Password for user was incorrect", { userName: user.name });
@@ -4,7 +4,7 @@ import { createId } from "@homarr/common";
import { users } from "@homarr/db/schema";
import { createDb } from "@homarr/db/test";
import { createSaltAsync, hashPasswordAsync } from "../../security";
import { hashPasswordAsync } from "../../security";
import { authorizeWithBasicCredentialsAsync } from "../credentials/authorization/basic-authorization";
const defaultUserId = createId();
@@ -13,12 +13,10 @@ describe("authorizeWithBasicCredentials", () => {
test("should authorize user with correct credentials", async () => {
// Arrange
const db = createDb();
const salt = await createSaltAsync();
await db.insert(users).values({
id: defaultUserId,
name: "test",
salt,
password: await hashPasswordAsync("test", salt),
password: await hashPasswordAsync("test"),
});
// Act
@@ -34,12 +32,10 @@ describe("authorizeWithBasicCredentials", () => {
test("should not authorize user with incorrect credentials", async () => {
// Arrange
const db = createDb();
const salt = await createSaltAsync();
await db.insert(users).values({
id: defaultUserId,
name: "test",
salt,
password: await hashPasswordAsync("test", salt),
password: await hashPasswordAsync("test"),
});
// Act
@@ -55,12 +51,10 @@ describe("authorizeWithBasicCredentials", () => {
test("should not authorize user with incorrect username", async () => {
// Arrange
const db = createDb();
const salt = await createSaltAsync();
await db.insert(users).values({
id: defaultUserId,
name: "test",
salt,
password: await hashPasswordAsync("test", salt),
password: await hashPasswordAsync("test"),
});
// Act
+10 -4
View File
@@ -1,9 +1,15 @@
import bcrypt from "bcrypt";
export const createSaltAsync = async () => {
return bcrypt.genSalt(10);
export const hashPasswordAsync = async (password: string) => {
return bcrypt.hash(password, 10);
};
export const hashPasswordAsync = async (password: string, salt: string) => {
return bcrypt.hash(password, salt);
/**
* Compares a plaintext password with a hashed password and returns whether they match.
* @param password plaintext password
* @param hash hashed password
* @returns wheter it is the same
*/
export const comparePasswordsAsync = async (password: string, hash: string) => {
return await bcrypt.compare(password, hash);
};
+32 -23
View File
@@ -1,25 +1,11 @@
import { describe, expect, it } from "vitest";
import { createSaltAsync, hashPasswordAsync } from "../security";
describe("createSalt should return a salt", () => {
it("should return a salt", async () => {
const result = await createSaltAsync();
expect(result).toBeDefined();
expect(result.length).toBeGreaterThan(25);
});
it("should return a different salt each time", async () => {
const result1 = await createSaltAsync();
const result2 = await createSaltAsync();
expect(result1).not.toEqual(result2);
});
});
import { comparePasswordsAsync, hashPasswordAsync } from "../security";
describe("hashPassword should return a hash", () => {
it("should return a hash", async () => {
const password = "password";
const salt = await createSaltAsync();
const result = await hashPasswordAsync(password, salt);
const result = await hashPasswordAsync(password);
expect(result).toBeDefined();
expect(result.length).toBeGreaterThan(55);
expect(result).not.toEqual(password);
@@ -27,21 +13,44 @@ describe("hashPassword should return a hash", () => {
it("should return a different hash each time", async () => {
const password = "password";
const password2 = "another password";
const salt = await createSaltAsync();
const result1 = await hashPasswordAsync(password, salt);
const result2 = await hashPasswordAsync(password2, salt);
const result1 = await hashPasswordAsync(password);
const result2 = await hashPasswordAsync(password2);
expect(result1).not.toEqual(result2);
});
it("should return a different hash for the same password with different salts", async () => {
const password = "password";
const salt1 = await createSaltAsync();
const salt2 = await createSaltAsync();
const result1 = await hashPasswordAsync(password, salt1);
const result2 = await hashPasswordAsync(password, salt2);
const result1 = await hashPasswordAsync(password);
const result2 = await hashPasswordAsync(password);
expect(result1).not.toEqual(result2);
});
});
describe("comparePasswordsAsync should compare passwords correctly", () => {
it("should return true for matching passwords", async () => {
// Arrange
const password = "password";
const hash = await hashPasswordAsync(password);
// Act
const result = await comparePasswordsAsync(password, hash);
// Assert
expect(result).toBe(true);
});
it("should return false for non-matching passwords", async () => {
// Arrange
const password = "password";
const wrongPassword = "wrongpassword";
const hash = await hashPasswordAsync(password);
// Act
const result = await comparePasswordsAsync(wrongPassword, hash);
// Assert
expect(result).toBe(false);
});
});
+4 -4
View File
@@ -25,14 +25,14 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/api": "workspace:^0.1.0",
"react": "19.2.4",
"react-dom": "19.2.4"
"react": "catalog:",
"react-dom": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+5 -5
View File
@@ -25,19 +25,19 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@drizzle-team/brocli": "^0.11.0",
"@drizzle-team/brocli": "catalog:",
"@homarr/auth": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"dotenv": "^17.3.1"
"dotenv": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"esbuild": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+2 -4
View File
@@ -1,6 +1,6 @@
import { command, string } from "@drizzle-team/brocli";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import { hashPasswordAsync } from "@homarr/auth";
import { createId } from "@homarr/common";
import { generateSecureRandomToken } from "@homarr/common/server";
import { and, count, db, eq } from "@homarr/db";
@@ -67,9 +67,8 @@ export const recreateAdmin = command({
permission: "admin",
});
const salt = await createSaltAsync();
const password = generateSecureRandomToken(24);
const hashedPassword = await hashPasswordAsync(password, salt);
const hashedPassword = await hashPasswordAsync(password);
const userId = createId();
await db.insert(users).values({
@@ -77,7 +76,6 @@ export const recreateAdmin = command({
name: result.data,
provider: "credentials",
password: hashedPassword,
salt,
});
await db.insert(groupMembers).values({
+2 -2
View File
@@ -22,7 +22,7 @@ export const resetPassword = command({
where: and(eq(users.name, options.username), eq(users.provider, "credentials")),
});
if (!user?.salt) {
if (!user?.password) {
console.error(`User ${options.username} not found`);
return;
}
@@ -33,7 +33,7 @@ export const resetPassword = command({
await db
.update(users)
.set({
password: await hashPasswordAsync(newPassword, user.salt),
password: await hashPasswordAsync(newPassword),
})
.where(eq(users.id, user.id));
@@ -29,7 +29,7 @@ export const usersUpdatePassword = command({
where: options.id ? eq(users.id, options.id) : eq(users.name, options.username!),
});
if (!user?.salt) {
if (!user?.password) {
console.error("User not found or has no credentials record");
return;
}
@@ -37,7 +37,7 @@ export const usersUpdatePassword = command({
await db
.update(users)
.set({
password: await hashPasswordAsync(options.password, user.salt),
password: await hashPasswordAsync(options.password),
})
.where(eq(users.id, user.id));
+12 -12
View File
@@ -27,22 +27,22 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/core": "workspace:^0.1.0",
"@paralleldrive/cuid2": "^3.3.0",
"cookie": "1.1.1",
"dayjs": "^1.11.20",
"next": "16.2.0",
"octokit": "^5.0.5",
"react": "19.2.4",
"react-dom": "19.2.4",
"undici": "7.18.2",
"zod": "^4.2.1",
"zod-validation-error": "^5.0.0"
"@paralleldrive/cuid2": "catalog:",
"cookie": "catalog:",
"dayjs": "catalog:",
"next": "catalog:",
"octokit": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"undici": "catalog:",
"zod": "catalog:",
"zod-validation-error": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+14 -14
View File
@@ -37,24 +37,24 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@t3-oss/env-nextjs": "^0.13.10",
"better-sqlite3": "^12.6.2",
"dns-caching": "^0.2.9",
"drizzle-orm": "^0.45.1",
"ioredis": "5.10.1",
"mysql2": "3.20.0",
"pg": "^8.20.0",
"superjson": "2.2.6",
"winston": "3.19.0",
"zod": "^4.2.1"
"@t3-oss/env-nextjs": "catalog:",
"better-sqlite3": "catalog:",
"dns-caching": "catalog:",
"drizzle-orm": "catalog:",
"ioredis": "catalog:",
"mysql2": "catalog:",
"pg": "catalog:",
"superjson": "catalog:",
"winston": "catalog:",
"zod": "catalog:"
},
"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",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"@types/better-sqlite3": "catalog:",
"@types/pg": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
@@ -1,7 +1,10 @@
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
export const formatMetadata = (metadata: Record<string, unknown> | Error, ignoreKeys?: string[]) => {
const metadataObject = metadata instanceof ErrorWithMetadata ? metadata.metadata : metadata;
const metadataObject =
metadata instanceof ErrorWithMetadata || ("metadata" in metadata && typeof metadata.metadata === "object")
? (metadata.metadata as Record<string, unknown>)
: metadata;
const filteredMetadata = Object.keys(metadataObject)
.filter((key) => !ignoreKeys?.includes(key))
+11 -11
View File
@@ -28,21 +28,21 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/cron-jobs": "workspace:^0.1.0",
"@tanstack/react-query": "^5.91.2",
"@trpc/client": "^11.13.4",
"@trpc/server": "^11.13.4",
"@trpc/tanstack-react-query": "^11.13.4",
"node-cron": "^4.2.1",
"react": "19.2.4",
"zod": "^4.2.1"
"@tanstack/react-query": "catalog:",
"@trpc/client": "catalog:",
"@trpc/server": "catalog:",
"@trpc/tanstack-react-query": "catalog:",
"node-cron": "catalog:",
"react": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node-cron": "^3.0.11",
"@types/react": "19.2.14",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"@types/node-cron": "catalog:",
"@types/react": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+2 -2
View File
@@ -29,7 +29,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+4 -4
View File
@@ -27,14 +27,14 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^",
"@homarr/db": "workspace:^0.1.0",
"node-cron": "^4.2.1"
"node-cron": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node-cron": "^3.0.11",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"@types/node-cron": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+3 -3
View File
@@ -38,13 +38,13 @@
"@homarr/server-settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"semver-parser": "^4.1.8"
"semver-parser": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+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,
}
: [],
},
}),
);
+1 -1
View File
@@ -5,9 +5,9 @@ import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { EVERY_10_MINUTES } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { items } from "@homarr/db/schema";
// This import is done that way to avoid circular dependencies.
import { rssFeedsRequestHandler } from "@homarr/request-handler/rss-feeds";
// This import is done that way to avoid circular dependencies.
import type { WidgetComponentProps } from "../../../widgets";
import { createCronJob } from "../lib";
@@ -0,0 +1,2 @@
ALTER TABLE `apiKey` DROP COLUMN `salt`;--> statement-breakpoint
ALTER TABLE `user` DROP COLUMN `salt`;
File diff suppressed because it is too large. Load diff
@@ -260,6 +260,13 @@
"when": 1760968518445,
"tag": "0036_add_app_reference_to_integration",
"breakpoints": true
},
{
"idx": 37,
"version": "5",
"when": 1775399941430,
"tag": "0037_remove_salt",
"breakpoints": true
}
]
}
@@ -0,0 +1,2 @@
ALTER TABLE "apiKey" DROP COLUMN "salt";--> statement-breakpoint
ALTER TABLE "user" DROP COLUMN "salt";
File diff suppressed because it is too large. Load diff
@@ -22,6 +22,13 @@
"when": 1760968530084,
"tag": "0002_add_app_reference_to_integration",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1775399963041,
"tag": "0003_remove_salt",
"breakpoints": true
}
]
}
@@ -0,0 +1,2 @@
ALTER TABLE `apiKey` DROP COLUMN `salt`;--> statement-breakpoint
ALTER TABLE `user` DROP COLUMN `salt`;
File diff suppressed because it is too large. Load diff
@@ -246,6 +246,13 @@
"when": 1760968503571,
"tag": "0034_add_app_reference_to_integration",
"breakpoints": true
},
{
"idx": 35,
"version": "6",
"when": 1775399918831,
"tag": "0035_remove_salt",
"breakpoints": true
}
]
}
+21 -21
View File
@@ -42,35 +42,35 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@auth/core": "^0.41.1",
"@auth/core": "catalog:",
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/core": "^8.3.18",
"@paralleldrive/cuid2": "^3.3.0",
"@testcontainers/mysql": "^11.13.0",
"@testcontainers/postgresql": "^11.13.0",
"better-sqlite3": "^12.6.2",
"dotenv": "^17.3.1",
"drizzle-kit": "^0.31.10",
"drizzle-orm": "^0.45.1",
"drizzle-zod": "^0.8.3",
"mysql2": "3.20.0",
"pg": "^8.20.0",
"superjson": "2.2.6"
"@mantine/core": "catalog:",
"@paralleldrive/cuid2": "catalog:",
"@testcontainers/mysql": "catalog:",
"@testcontainers/postgresql": "catalog:",
"better-sqlite3": "catalog:",
"dotenv": "catalog:",
"drizzle-kit": "catalog:",
"drizzle-orm": "catalog:",
"drizzle-zod": "catalog:",
"mysql2": "catalog:",
"pg": "catalog:",
"superjson": "catalog:"
},
"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",
"dotenv-cli": "^11.0.0",
"esbuild": "^0.27.4",
"eslint": "^9.39.4",
"prettier": "^3.8.1",
"tsx": "4.20.4",
"typescript": "^5.9.3"
"@types/better-sqlite3": "catalog:",
"@types/pg": "catalog:",
"dotenv-cli": "catalog:",
"esbuild": "catalog:",
"eslint": "catalog:",
"prettier": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:"
}
}
-2
View File
@@ -51,7 +51,6 @@ export * from "@homarr/core/infrastructure/certificates/hostnames/db/mysql";
export const apiKeys = mysqlTable("apiKey", {
id: varchar({ length: 64 }).notNull().primaryKey(),
apiKey: text().notNull(),
salt: text().notNull(),
userId: varchar({ length: 64 })
.notNull()
.references((): AnyMySqlColumn => users.id, {
@@ -66,7 +65,6 @@ export const users = mysqlTable("user", {
emailVerified: timestamp(),
image: text(),
password: text(),
salt: text(),
provider: varchar({ length: 64 }).$type<SupportedAuthProvider>().default("credentials").notNull(),
homeBoardId: varchar({ length: 64 }).references((): AnyMySqlColumn => boards.id, {
onDelete: "set null",
-2
View File
@@ -50,7 +50,6 @@ export * from "@homarr/core/infrastructure/certificates/hostnames/db/postgresql"
export const apiKeys = pgTable("apiKey", {
id: varchar({ length: 64 }).notNull().primaryKey(),
apiKey: text().notNull(),
salt: text().notNull(),
userId: varchar({ length: 64 })
.notNull()
.references((): AnyPgColumn => users.id, {
@@ -65,7 +64,6 @@ export const users = pgTable("user", {
emailVerified: timestamp(),
image: text(),
password: text(),
salt: text(),
provider: varchar({ length: 64 }).$type<SupportedAuthProvider>().default("credentials").notNull(),
homeBoardId: varchar({ length: 64 }).references((): AnyPgColumn => boards.id, {
onDelete: "set null",
-2
View File
@@ -33,7 +33,6 @@ export * from "@homarr/core/infrastructure/certificates/hostnames/db/sqlite";
export const apiKeys = sqliteTable("apiKey", {
id: text().notNull().primaryKey(),
apiKey: text().notNull(),
salt: text().notNull(),
userId: text()
.notNull()
.references((): AnySQLiteColumn => users.id, {
@@ -48,7 +47,6 @@ export const users = sqliteTable("user", {
emailVerified: int({ mode: "timestamp_ms" }),
image: text(),
password: text(),
salt: text(),
provider: text().$type<SupportedAuthProvider>().default("credentials").notNull(),
homeBoardId: text().references((): AnySQLiteColumn => boards.id, {
onDelete: "set null",
+5 -5
View File
@@ -24,15 +24,15 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"fast-xml-parser": "^5.5.7",
"zod": "^4.2.1"
"fast-xml-parser": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"tsx": "4.20.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"tsx": "catalog:",
"typescript": "catalog:"
}
}
@@ -153,6 +153,7 @@ export type HomarrDocumentationPath =
| "/docs/getting-started/installation/synology"
| "/docs/getting-started/installation/unraid"
| "/docs/integrations/adguard-home"
| "/docs/integrations/anchor"
| "/docs/integrations/aria2"
| "/docs/integrations/codeberg"
| "/docs/integrations/dash-dot"
@@ -188,9 +189,11 @@ export type HomarrDocumentationPath =
| "/docs/integrations/radarr"
| "/docs/integrations/readarr"
| "/docs/integrations/sabnzbd"
| "/docs/integrations/search-ch"
| "/docs/integrations/seerr"
| "/docs/integrations/slskd"
| "/docs/integrations/sonarr"
| "/docs/integrations/speedtest-tracker"
| "/docs/integrations/tdarr"
| "/docs/integrations/tracearr"
| "/docs/integrations/transmission"
@@ -207,6 +210,7 @@ export type HomarrDocumentationPath =
| "/docs/management/settings"
| "/docs/management/tasks"
| "/docs/management/users"
| "/docs/widgets/anchor-note"
| "/docs/widgets/app"
| "/docs/widgets/bookmarks"
| "/docs/widgets/calendar"
@@ -235,8 +239,10 @@ export type HomarrDocumentationPath =
| "/docs/widgets/rss-feed"
| "/docs/widgets/smart-home-entity-state"
| "/docs/widgets/smart-home-execute-automation"
| "/docs/widgets/speedtest-tracker"
| "/docs/widgets/stock-price"
| "/docs/widgets/system-resources"
| "/docs/widgets/timetable"
| "/docs/widgets/tracearr"
| "/docs/widgets/video"
| "/docs/widgets/weather"
+26 -1
View File
@@ -217,7 +217,7 @@ export const integrationDefs = {
name: "Nextcloud",
secretKinds: [["username", "password"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg",
category: ["calendar"],
category: ["calendar", "notifications"],
documentationUrl: createDocumentationLink("/docs/integrations/nextcloud"),
},
unifiController: {
@@ -312,6 +312,13 @@ export const integrationDefs = {
category: ["calendar"],
documentationUrl: createDocumentationLink("/docs/integrations/ical"),
},
anchor: {
name: "Anchor",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/anchor.svg",
category: ["notes"],
documentationUrl: createDocumentationLink("/docs/integrations/anchor"),
},
truenas: {
name: "TrueNAS",
secretKinds: [["username", "password"]],
@@ -334,6 +341,14 @@ export const integrationDefs = {
// @ts-expect-error - docs page will be created when integration is merged
documentationUrl: createDocumentationLink("/docs/integrations/coolify"),
},
searchCh: {
name: "Search.ch",
secretKinds: [[]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/search-ch.png",
defaultUrl: "https://search.ch",
category: ["timetable"],
documentationUrl: createDocumentationLink("/docs/integrations/search-ch"),
},
immich: {
name: "Immich",
secretKinds: [["apiKey"]],
@@ -348,6 +363,13 @@ export const integrationDefs = {
category: ["mediaMonitoring"],
documentationUrl: createDocumentationLink("/docs/integrations/tracearr"),
},
speedtestTracker: {
name: "Speedtest Tracker",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png",
category: ["speedtest"],
documentationUrl: createDocumentationLink("/docs/integrations/speedtest-tracker"),
},
// This integration only returns mock data, it is used during development (but can also be used in production by directly going to the create page)
mock: {
name: "Mock",
@@ -434,8 +456,11 @@ export const integrationCategories = [
"releasesProvider",
"notifications",
"firewall",
"timetable",
"photoService",
"notes",
"mediaMonitoring",
"speedtest",
] as const;
export type IntegrationCategory = (typeof integrationCategories)[number];
+3
View File
@@ -5,6 +5,7 @@ export const widgetKinds = [
"iframe",
"video",
"notebook",
"anchorNote",
"dnsHoleSummary",
"dnsHoleControls",
"smartHome-entityState",
@@ -31,8 +32,10 @@ export const widgetKinds = [
"systemResources",
"coolify",
"systemDisks",
"timetable",
"immich-serverStats",
"immich-albumCarousel",
"tracearr",
"speedtestTracker",
] as const;
export type WidgetKind = (typeof widgetKinds)[number];
+4 -4
View File
@@ -26,14 +26,14 @@
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"dockerode": "^4.0.9"
"dockerode": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/dockerode": "^4.0.1",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"@types/dockerode": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+5 -5
View File
@@ -26,15 +26,15 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/form": "^8.3.18",
"mantine-form-zod-resolver": "^1.3.0",
"zod": "^4.2.1"
"@mantine/form": "catalog:",
"mantine-form-zod-resolver": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+5 -5
View File
@@ -30,15 +30,15 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.18",
"react": "19.2.4",
"zod": "^4.2.1"
"@mantine/core": "catalog:",
"react": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+2 -2
View File
@@ -31,7 +31,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+4 -4
View File
@@ -25,14 +25,14 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"bcrypt": "^6.0.0"
"bcrypt": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/bcrypt": "6.0.0",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"@types/bcrypt": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
+21 -21
View File
@@ -25,39 +25,39 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@ctrl/deluge": "^7.5.2",
"@ctrl/qbittorrent": "^9.11.2",
"@ctrl/transmission": "^7.4.0",
"@gitbeaker/rest": "^43.8.0",
"@ctrl/deluge": "catalog:",
"@ctrl/qbittorrent": "catalog:",
"@ctrl/transmission": "catalog:",
"@gitbeaker/rest": "catalog:",
"@homarr/common": "workspace:^0.1.0",
"@homarr/core": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/image-proxy": "workspace:^0.1.0",
"@homarr/node-unifi": "^2.6.0",
"@homarr/node-unifi": "catalog:",
"@homarr/redis": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@immich/sdk": "^2.6.1",
"@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",
"tsdav": "^2.1.8",
"undici": "7.18.2",
"xml2js": "^0.6.2",
"zod": "^4.2.1"
"@immich/sdk": "catalog:",
"@jellyfin/sdk": "catalog:",
"@octokit/auth-app": "catalog:",
"ical.js": "catalog:",
"maria2": "catalog:",
"octokit": "catalog:",
"proxmox-api": "catalog:",
"timezones-ical-library": "catalog:",
"tsdav": "catalog:",
"undici": "catalog:",
"xml2js": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node-unifi": "^2.5.1",
"@types/xml2js": "^0.4.14",
"eslint": "^9.39.4",
"typescript": "^5.9.3"
"@types/node-unifi": "catalog:",
"@types/xml2js": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:"
}
}
@@ -0,0 +1,118 @@
import type { fetch as undiciFetch } from "undici";
import { ResponseError } from "@homarr/common/server";
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
import { createLogger } from "@homarr/core/infrastructure/logs";
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 { anchorNoteSchema, anchorNoteSummaryListSchema } from "./anchor-types";
import type { AnchorNote, AnchorNotesListInput, AnchorNoteSummary, AnchorNoteUpdateInput } from "./anchor-types";
const logger = createLogger({ module: "anchorIntegration" });
export class AnchorIntegration extends Integration {
protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
const response = await input.fetchAsync(this.url("/api/notes", { limit: 1 }), {
headers: {
Authorization: `Bearer ${this.getSecretValue("apiKey")}`,
},
});
if (!response.ok) return TestConnectionError.StatusResult(response);
anchorNoteSummaryListSchema.parse(await response.json());
return { success: true };
}
public async listNotesAsync(input: AnchorNotesListInput = {}): Promise<AnchorNoteSummary[]> {
const queryParams = buildListQuery(input);
const response = await this.requestAsync(fetchWithTrustedCertificatesAsync, "/api/notes", {
queryParams,
});
return anchorNoteSummaryListSchema.parse(response);
}
public async getNoteAsync(noteId: string): Promise<AnchorNote> {
const response = await this.requestAsync(fetchWithTrustedCertificatesAsync, `/api/notes/${noteId}`);
return anchorNoteSchema.parse(response);
}
public async updateNoteAsync(input: AnchorNoteUpdateInput): Promise<AnchorNote> {
const { noteId, ...updateData } = input;
const response = await this.requestAsync(fetchWithTrustedCertificatesAsync, `/api/notes/${noteId}`, {
method: "PATCH",
body: updateData,
});
if (response === null) {
return this.getNoteAsync(noteId);
}
return anchorNoteSchema.parse(response);
}
private async requestAsync(
fetchAsync: typeof undiciFetch,
path: `/${string}`,
options?: {
queryParams?: Record<string, string>;
method?: string;
body?: unknown;
},
): Promise<unknown> {
const url = this.url(path, options?.queryParams);
const headers: Record<string, string> = {
Authorization: `Bearer ${this.getSecretValue("apiKey")}`,
};
if (options?.body !== undefined) {
headers["Content-Type"] = "application/json";
}
const response = await fetchAsync(url, {
method: options?.method ?? "GET",
headers,
body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,
});
if (!response.ok) {
let errorBody: string | null = null;
try {
errorBody = await response.text();
} catch {
errorBody = null;
}
logger.warn("Anchor request failed", {
statusCode: response.status,
url,
errorBody: errorBody ? errorBody.slice(0, 200) : null,
});
throw new ResponseError(response);
}
if (response.status === 204) {
return null;
}
return await response.json();
}
}
const buildListQuery = (input: AnchorNotesListInput) => {
const queryParams: Record<string, string> = {};
if (input.search?.trim()) {
queryParams.search = input.search.trim();
}
if (input.tagId?.trim()) {
queryParams.tagId = input.tagId.trim();
}
if (typeof input.limit === "number") {
queryParams.limit = input.limit.toString();
}
return Object.keys(queryParams).length > 0 ? queryParams : undefined;
};
@@ -0,0 +1,43 @@
import { z } from "zod/v4";
export const anchorNotesListInputSchema = z.object({
search: z.string().optional(),
tagId: z.string().optional(),
limit: z.number().min(1).max(200).optional(),
});
export type AnchorNotesListInput = z.infer<typeof anchorNotesListInputSchema>;
export const anchorNotePermissionSchema = z.enum(["owner", "viewer", "editor"]);
export type AnchorNotePermission = z.infer<typeof anchorNotePermissionSchema>;
export const anchorNoteSummarySchema = z.object({
id: z.string(),
title: z.string(),
updatedAt: z.string(),
isPinned: z.boolean(),
tagIds: z.array(z.string()),
permission: anchorNotePermissionSchema,
});
export type AnchorNoteSummary = z.infer<typeof anchorNoteSummarySchema>;
export const anchorNoteSchema = anchorNoteSummarySchema.extend({
content: z.string().nullable().optional(),
createdAt: z.string(),
isArchived: z.boolean(),
background: z.string().nullable().optional(),
userId: z.string(),
});
export type AnchorNote = z.infer<typeof anchorNoteSchema>;
export const anchorNoteSummaryListSchema = z.array(anchorNoteSummarySchema);
export const anchorNoteUpdateInputSchema = z
.object({
noteId: z.string(),
title: z.string().optional(),
content: z.string().optional(),
})
.refine((value) => value.title !== undefined || value.content !== undefined, {
message: "At least one field to update must be provided",
});
export type AnchorNoteUpdateInput = z.infer<typeof anchorNoteUpdateInputSchema>;
@@ -1,6 +1,7 @@
import type { IntegrationKind } from "@homarr/definitions";
import { AdGuardHomeIntegration } from "../adguard-home/adguard-home-integration";
import { AnchorIntegration } from "../anchor/anchor-integration";
import { CodebergIntegration } from "../codeberg/codeberg-integration";
import { CoolifyIntegration } from "../coolify/coolify-integration";
import { DashDotIntegration } from "../dashdot/dashdot-integration";
@@ -40,7 +41,9 @@ 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 { SpeedtestTrackerIntegration } from "../speedtest-tracker/speedtest-tracker-integration";
import { TracearrIntegration } from "../tracearr/tracearr-integration";
import { TrueNasIntegration } from "../truenas/truenas-integration";
import { UnifiControllerIntegration } from "../unifi-controller/unifi-controller-integration";
@@ -70,6 +73,7 @@ type IntegrationInstance = new (integration: IntegrationInput) => Integration;
// factories are an array, to differentiate in js between class constructors and functions
export const integrationCreators = {
anchor: AnchorIntegration,
piHole: [createPiHoleIntegrationAsync],
adGuardHome: AdGuardHomeIntegration,
homeAssistant: HomeAssistantIntegration,
@@ -114,7 +118,9 @@ export const integrationCreators = {
coolify: CoolifyIntegration,
tracearr: TracearrIntegration,
glances: GlancesIntegration,
searchCh: SearchChIntegration,
immich: ImmichIntegration,
speedtestTracker: SpeedtestTrackerIntegration,
} satisfies Record<IntegrationKind, IntegrationInstance | [(input: IntegrationInput) => Promise<Integration>]>;
type IntegrationInstanceOfKind<TKind extends keyof typeof integrationCreators> = {
@@ -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([]),
});
Loaded 100 of 212 files, more files were not shown because too many files have changed in this diff. Show more